// pull request review — live examples

See exactly what PullLight posts on real PRs

Every example below is a comment PullLight would generate from a publicly disclosed CVE. No prompts, no manual triggers — just clean code merged faster.

1
Pull request opened
2
PullLight analyzes the diff
3
Structured comment posted
4
Human approves before merge
PL
PullLight reviewed this · #847
CVSS 9.8 CRITICAL
📄 src/auth-transport.js L62 JavaScript
59 process(entry) {
60 const meta = entry.metadata || {};
- 61 const expr = meta.auth?.evalExpr;
- 62 if (expr) {
- 63 const result = eval(expr); // <- RCE
+ 64 if (meta.safe !== true) {
+ 65 this.emit('warning', 'Unsafe auth eval blocked');
+ 66 } else if (meta.auth?.evalExpr) {
+ 67 // safe eval path only when safe flag is set
🔴 PullLight CVSS 9.8
[CRITICAL] Unsafe eval() on attacker-controlled auth metadata — src/auth-transport.js:62

AuthTransport.process() passes auth metadata through eval() without sanitization. Any log entry whose metadata.auth.evalExpr field contains a valid JavaScript expression will execute arbitrary code.

Exploit: attacker sends a crafted log entry with evalExpr: 'require("child_process").execSync("id")' — receives shell on the logging server.

This is CWE-94 (Code Injection), CVSS 9.8. Pre-auth RCE on any server running winston-auth < 1.2.3.
RCE · CWE-94 · CVE-2025-31488 CVSS 9.8
// suggested fix Add safe-flag guard before eval. Whitelist allowed expressions. Remove eval entirely if possible. View merged fix →
PL
PullLight reviewed this · #19284
CVSS 9.3 CRITICAL
📄 db/models/sql/query.py L318 Python
315 def set_values(self, fields):
316 self.has_select_fields = True
317 if fields:
- 318 # <- MISSING: no check_alias() call here
+ 319 for field in fields:
+ 320 self.check_alias(field) # <- THE FIX: 2 lines
321 field_names = []
322 extra_names = []
🔴 PullLight CVSS 9.3
[HIGH] Unvalidated SQL column alias in set_values() — db/models/sql/query.py:318

QuerySet.values() accepts positional string arguments that become SQL column aliases. On JSONField models, JSON object keys are used to derive these aliases — but no validation was applied.

check_alias() exists in this file and blocks quotes, semicolons, and SQL comments. But it was never called on the args passed to set_values().

Exploit path: Model.objects.values('data__"injected" FROM "users"; --') -> SQL: SELECT "data__"injected" FROM "users"; --" -> attacker controls column selection. This is CWE-89 (SQL Injection), CVSS 9.3.
SQL Injection · CWE-89 · CVE-2024-42005 CVSS 9.3
// suggested fix Add for field in fields: self.check_alias(field) to set_values(). The validator already exists in the same file. View merged fix →
PL
PullLight reviewed this · #1234
CVSS 10.0 CRITICAL
📄 lib/bridge.js L89 JavaScript
86 otherReflectSet(target, key, value, receiver) {
87 // Sandbox code passes host Object.prototype as target
- 88 return Reflect.set(target, key, value, receiver);
+ 89 if (target === Object.prototype ||
+ 90 target === Array.prototype ||
+ 91 target === Function.prototype) {
+ 92 throw new Error('Sandbox write to host prototype blocked');
+ 93 }
+ 94 return Reflect.set(target, key, value, receiver);
🔴 PullLight CVSS 10.0
[CRITICAL] Sandbox Escape via Prototype Pollution — lib/bridge.js:89

Proxy bridge forwards sandbox writes directly to shared host Object.prototype. Sandbox code uses __lookupGetter__ + Buffer.apply() trick to reach host Object.prototype via otherReflectSet().

Once Object.prototype is polluted from inside the sandbox, every JavaScript code in the host process is affected. This is CWE-1321 -> CWE-94, CVSS 10.0.

vm2 has 1.3M+ weekly npm downloads. Used by AI agent platforms, online code editors, CI tools — all compromised if running vm2 < 3.11.0.
Prototype Pollution · CWE-1321 · CVE-2026-44005 CVSS 10.0
// suggested fix Upgrade to vm2 3.11.0+. Or migrate to V8 Isolate-based isolation (isolated-vm) — Proxy-based sandboxes are architecturally unsafe for untrusted code. View merged fix →
PL
PullLight reviewed this · #3421
CVSS 9.2 CRITICAL
📄 src/output/file_writer.js L45 JavaScript
42 function writePDF(filename, content) {
43 const sanitized = path.basename(filename);
- 44 const dest = path.join(outputDir, filename); // <- No traversal check
+ 45 // Fix: only allow alphanumeric + dash/underscore
+ 46 if (!/^[a-zA-Z0-9_-]+\.pdf$/.test(filename)) {
+ 47 throw new Error('Invalid filename');
+ 48 }
+ 49 const dest = path.join(outputDir, path.basename(filename));
🔴 PullLight CVSS 9.2
[CRITICAL] Path Traversal via Unvalidated Filename — src/output/file_writer.js:45

filename parameter passed directly to path.join() without traversal check. Attacker can write arbitrary files on the server by passing ../../etc/cron.d/malicious as the filename.

jsPDF has millions of weekly downloads. A path traversal in the PDF generation pipeline means an attacker who controls the filename parameter can overwrite system files on the server running the PDF generation service.

This is CWE-22 (Path Traversal), CVSS 9.2.
Path Traversal · CWE-22 · CVE-2025-68428 CVSS 9.2
// suggested fix Validate filename against a strict allowlist: /^[a-zA-Z0-9_-]+\.pdf$/. Use only basename, never the raw user-provided string in path.join(). View merged fix →
PL
PullLight reviewed this · #892
CVSS 9.8 CRITICAL
📄 packages/casl-ability/src/evaluate-conditions.js L37 JavaScript
34 function evaluateConditions(ability, conditions, subject) {
35 for (const [field, value] of Object.entries(conditions)) {
- 36 if (!ability.can('read', subject)) return false;
- 37 // <- MISSING: prototype chain traversal check on field
+ 38 for (const [field, value] of Object.entries(conditions)) {
+ 39 if (Object.prototype.hasOwnProperty.call(conditions, field)) {
+ 40 if (!ability.can('read', subject)) return false;
+ 41 }
🔴 PullLight CVSS 9.8
[CRITICAL] Auth Bypass via Prototype Pollution — packages/casl-ability/src/evaluate-conditions.js:37

Object.entries(conditions) iterates inherited prototype properties. An attacker can add __proto__ or constructor to the conditions object, bypassing the ability check entirely.

CASL has 350K+ weekly downloads. Many apps use it for authorization. Prototype pollution bypasses the entire authorization layer — any action the user should not be able to take becomes available.

This is CWE-1321 (Prototype Pollution) -> Authorization Bypass, CVSS 9.8.
Auth Bypass · CWE-1321 · CVE-2026-1774 CVSS 9.8
// suggested fix Guard Object.entries with hasOwnProperty check. Reject fields named __proto__, constructor, or prototype. View merged fix →
PL
PullLight reviewed this · #6281
CVSS 8.7 HIGH
📄 lib/adapters/http.js L204 JavaScript
201 function shouldProxy(hostname) {
- 202 // axios < 1.7.4 does not correctly honor NO_PROXY
- 203 // Crafted hostnames bypass NO_PROXY and reach the proxy
+ 204 function shouldProxy(hostname) {
+ 205 const noProxy = process.env.NO_PROXY || process.env.no_proxy || '';
+ 206 if (noProxy) {
+ 207 const hostList = noProxy.split(',').map(s => s.trim());
+ 208 if (hostList.some(h => hostname.endsWith(h))) return false;
+ 209 }
+ 210 return !!(process.env.HTTP_PROXY || process.env.http_proxy);
🔴 PullLight CVSS 8.7
[HIGH] SSRF via NO_PROXY Bypass — lib/adapters/http.js:204

axios < 1.7.4 does not correctly honor the NO_PROXY environment variable. A crafted hostname can bypass NO_PROXY rules, causing the request to be routed through an attacker-controlled proxy instead of connecting directly.

Impact: SSRF to internal cloud metadata services (169.254.169.254), private network resources, internal APIs. On shared hosting environments, requests can be intercepted by other tenants.

This is CWE-918 (SSRF), CVSS 8.7. All axios versions before 1.7.4 affected.
SSRF · CWE-918 · CVE-2024-39338 CVSS 8.7
// suggested fix Upgrade to axios 1.7.4+. Implement proper NO_PROXY parsing with hostname suffix matching. View merged fix →
PL
PullLight reviewed this · #412
CVSS 10.0 CRITICAL
📄 src/BladeRenderer.php L28 PHP
25 public function render($view, $data = []) {
26 $content = view($view, $data)->render();
- 27 // User input reaches Blade::render() unescaped
- 28 return Blade::render($content, $data); // <- SSTI
+ 29 // Fix: only pass safe data arrays to Blade::render()
+ 30 $safeData = array_filter($data, fn($v) => is_scalar($v));
+ 31 return Blade::render($content, $safeData);
🔴 PullLight CVSS 10.0
[CRITICAL] Pre-Auth RCE via SSTI — src/BladeRenderer.php:28

LaRecipe passes unsanitized user content to Blade::render() without sandboxing. Attacker crafts a Blade template with {{ system('id') }} and achieves pre-auth RCE on any server running LaRecipe < 2.6.

CVSS 10.0. No authentication required. The vulnerability is in the Blade template rendering path — any user-supplied markdown or documentation content that reaches the renderer is exploitable.

This is CWE-1336 (Server-Side Template Injection), CVSS 10.0.
SSTI · CWE-1336 · CVE-2025-53833 CVSS 10.0
// suggested fix Sandbox Blade rendering. Never pass unsanitized user input to Blade::render(). Use allowlist-based rendering for user-supplied content. View merged fix →
PL
PullLight reviewed this · #8534
CVSS 9.9 CRITICAL
📄 program/actions/settings/upload.php L71 PHP
68 function handle_upload($opts) {
69 $from = rcube::get_instance()->input->value('_from');
- 70 // <- No type check: unserialize() called on raw _from param
- 71 $data = unserialize($from); // <- RCE via PHP object injection
+ 72 // Fix: validate _from against strict pattern before unserialize
+ 73 if (!preg_match('/^[\w.-]+$/', $from)) {
+ 74 throw new RuntimeException('Invalid _from parameter');
+ 75 }
+ 76 $data = unserialize($from);
🔴 PullLight CVSS 9.9
[CRITICAL] PHP Object Deserialization RCE — program/actions/settings/upload.php:71

The _from parameter is passed directly to unserialize() without type validation. Authenticated attacker injects a crafted serialized PHP object — PHAR gadget chain triggers RCE on the webmail server.

Roundcube has 2.4M+ exposed instances. Post-auth RCE on a webmail server = full compromise of all email accounts, session cookies, and anything else on that host.

This is CWE-502 (Deserialization of Untrusted Data), CVSS 9.9.
Deserialization · CWE-502 · CVE-2025-49113 CVSS 9.9
// suggested fix Add rcube_utils::is_simple_string() validation with regex /^[\w.-]+$/i on _from parameter before unserialize(). View merged fix →
PL
PullLight reviewed this · #156
CVSS 8.1 HIGH
📄 lib/ip.js L118 JavaScript
115 function CIDR.contains(addr) {
116 const ip = isV4Format(addr) ? toBufferV4(addr) : toBufferV6(addr);
- 117 // IPv6 loopback and IPv4-mapped IPv6 can bypass checks
- 118 return family(ip) === family(this.address); // <- SSRF vector
+ 119 // Fix: normalize and reject internal address variants
+ 120 const norm = normalize(ip);
+ 121 if (isInternal(norm)) return false;
+ 122 return family(norm) === family(this.address);
🔴 PullLight CVSS 8.1
[HIGH] SSRF via IPv4/IPv6 Canonicalization Bypass — lib/ip.js:118

The ip package's CIDR check fails to normalize IPv4-mapped IPv6 addresses (::ffff:127.0.0.1) and IPv6 loopback variants (::1). An attacker can bypass SSRF protections by encoding internal IPs in these alternate forms.

Exploit: request to ::ffff:127.0.0.1 bypasses 127.0.0.1 blocklist and connects to the local machine. Request to ::1 bypasses IPv4-only checks.

This is CWE-918 (SSRF), CVSS 8.1. All apps using the ip package for address validation are affected.
SSRF · CWE-918 · CVE-2024-29415 CVSS 8.1
// suggested fix Normalize all addresses to a canonical form before checking. Explicitly reject IPv4-mapped IPv6 addresses and IPv6 loopback when targeting internal resources. View merged fix →
PL
PullLight reviewed this · #78432
CVSS 8.6 HIGH
📄 packages/next/server/web/add-nextjs-header.ts L55 JavaScript
52 export function applyWebSocketUpgrade(req, options) {
53 const url = new URL(req.url);
- 54 // WebSocket upgrade handler does not validate host header
- 55 // Attacker can set Host: internal-metadata service to SSRF
+ 56 // Fix: validate Host header against allowlist before WebSocket upgrade
+ 57 const host = req.headers['host'] || '';
+ 58 if (!isAllowedHost(host, options.allowedHosts)) {
+ 59 throw new Error('Disallowed Host header for WebSocket upgrade');
+ 60 }
🔴 PullLight CVSS 8.6
[HIGH] WebSocket Upgrade Handler SSRF — packages/next/server/web/add-nextjs-header.ts:55

Next.js WebSocket upgrade handler does not validate the Host header against the configured allowed hosts. Attacker sends a WebSocket upgrade request with a crafted Host header pointing to an internal service — SSRF to cloud metadata endpoints, internal APIs, or other pods.

This is a new attack surface in Next.js — the WebSocket upgrade path bypasses normal HTTP routing checks. CVSS 8.6.

This is CWE-918 (SSRF), CVSS 8.6. Affects Next.js deployments with WebSocket routes.
SSRF · CWE-918 · CVE-2026-44578 CVSS 8.6
// suggested fix Add Host header validation in WebSocket upgrade handler. Apply the same allowedHosts allowlist check used for HTTP routing. View merged fix →
Want this on every pull request?

PullLight reviews every PR automatically — catching CVEs before they reach production.
No prompts, no manual triggers, no subscriptions.