GitHub Advisoryadvisory · npm
GHSA-3w6x-2g7m-8v23 Axios: Invisible JSON Response Tampering via Prototype Pollution Gadget in `parseReviver`
MEDIUM
Official page ↗
- Aliases
- CVE-2026-42044
- Ecosystem
- npm
- Packages
- axios
- Severity
- Moderate
- CVSS vector
- CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:N
- Weaknesses
- CWE-1321, CWE-915
- Reviewed
- 2026-05-05
Affected packages
• npm axios: >= 1.0.0, < 1.15.2 → fixed in 1.15.2
Description
Vulnerability Disclosure: Invisible JSON Response Tampering via Prototype Pollution Gadget in `parseReviver`
Summary
The Axios library is vulnerable to a Prototype Pollution "Gadget" attack that allows any `Object.prototype` pollution in the application's dependency tree to be escalated into surgical, invisible modification of all JSON API responses — including privilege escalation, balance manipulation, and authorization bypass.
The default `transformResponse` function at `lib/defaults/index.js:124` calls `JSON.parse(data, this.parseReviver)`, where `this` is the merged config object. Because `parseReviver` is not present in Axios defaults, not validated by `assertOptions`, and not subject to any constraints, a polluted `Object.prototype.parseReviver` function is called for every key-value pair in every JSON response, allowing the attacker to selectively modify individual values while leaving the rest of the response intact.
This is strictly more powerful than the `transformResponse` gadget because:
1. No constraints — the reviver can return any value (no "must return true" requirement)
2. Selective modification — individual JSON keys can be changed while others remain untouched
3. Invisible — the response structure and most values look completely normal
4. Simultaneous exfiltration — the reviver sees the original values before modification
Severity: Critical (CVSS 9.1)
Affected Versions: All versions (v0.x - v1.x including v1.15.0)
Vulnerable Component: `lib/defaults/index.js:124` (JSON.parse with prototype-inherited reviver)
CWE
• CWE-1321: Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')
• CWE-915: Improperly Controlled Modification of Dynamically-Determined Object Attributes
CVSS 3.1
Score: 9.1 (Critical)
Vector: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N`
| Metric | Value | Justification |
|---|---|---|
| Attack Vector | Network | PP is triggered remotely via any vulnerable dependency |
| Attack Complexity | Low | Once PP exists, single property assignment. Consistent with GHSA-fvcv-3m26-pcqx scoring methodology |
| Privileges Required | None | No authentication needed |
| User Interaction | None | No user interaction required |
| Scope | Unchanged | Within the application process |
| Confidentiality | High | The reviver receives every key-value pair from every JSON response — full data exfiltration. In the PoC, `apiKey: "sk-secret-internal-key"` is captured |
| Integrity | High | Arbitrary, selective modification of any JSON value. No constraints. In the PoC, `isAdmin: false → true`, `role: "viewer" → "admin"`, `balance: 100 → 999999`. The response looks completely normal except for the surgically altered values |
| Availability | None | No crash, no error — the attack is entirely silent |
Comparison with All Known Axios PP Gadgets
| Factor | GHSA-fvcv-3m26-pcqx (Header Injection) | transformResponse | proxy (MITM) | parseReviver (This) |
|---|---|---|---|---|
| PP target | `Object.prototype['header']` | `Object.prototype.transformResponse` | `Object.prototype.proxy` | `Object.prototype.parseReviver` |
| Fixed by 1.15.0? | Yes | No | No | No |
| Constraints | N/A (fixed) | Must return `true` | None | None |
| Data modification | Header injection only | Response replaced with `true` | Full MITM | Selective per-key modification |
| Stealth | Request anomaly visible | Response becomes `true` (obvious) | Proxy visible in network | Completely invisible |
| Data access | Headers only | `this.auth` + raw response | All traffic | Every JSON key-value pair |
| Validated? | N/A | `assertOptions` validates | Not validated | Not validated |
| In defaults? | N/A | Yes → goes through mergeConfig | No → bypasses mergeConfig | No → bypasses mergeConfig |
Usage of "Helper" Vulnerabilities
This vulnerability requires Zero Direct User Input.
If an attacker can pollute `Object.prototype` via any other library in the stack (e.g., `qs`, `minimist`, `lodash`, `body-parser`), the polluted `parseReviver` function is automatically used by every Axios request that receives a JSON response. The developer's code is completely safe — no configuration errors needed.
Root Cause Analysis
The Attack Path
```
Object.prototype.parseReviver = function(key, value) { /* malicious */ }
│
▼
mergeConfig(defaults, userConfig)
│
│ parseReviver NOT in defaults → NOT iterated by mergeConfig
│ parseReviver NOT in userConfig → NOT iterated by mergeConfig
│ Merged config has NO own parseReviver property
│
▼
transformData.call(config, config.transformResponse, response)
│
│ Default transformResponse function runs (NOT overridden)
│
▼
defaults/index.js:124: JSON.parse(data, this.parseReviver)
│
│ this = config (merged config object, plain {})
│ config.parseReviver → NOT own property → traverses prototype chain
│ → finds Object.prototype.parseReviver → attacker's function!
│
▼
JSON.parse calls reviver for EVERY key-value pair
│
│ Attacker can: read original value, modify it, return anything
│ No validation, no constraints, no assertOptions check
│
▼
Application receives surgically modified JSON response
```
Why `parseReviver` Bypasses ALL Existing Protections
1. Not in defaults (`lib/defaults/index.js`): `parseReviver` is not defined in the defaults object, so `mergeConfig`'s `Object.keys({...defaults, ...userConfig})` iteration never encounters it. The merged config has no own `parseReviver` property.
2. Not in assertOptions schema (`lib/core/Axios.js:135-142`): The schema only contains `{baseUrl, withXsrfToken}`. `parseReviver` is not validated.
3. No type check: The `JSON.parse` API accepts any function as a reviver. There is no check that `this.parseReviver` is intentionally set.
4. Works INSIDE the default transform: Unlike `transformResponse` pollution (which replaces the entire transform and is caught by `assertOptions`), `parseReviver` pollution injects into the DEFAULT `transformResponse` function's `JSON.parse` call. The default function itself is not replaced, so `assertOptions` has nothing to catch.
Vulnerable Code
File: `lib/defaults/index.js`, line 124
```javascript
transformResponse: [
function transformResponse(data) {
// ... transitional checks ...
if (data && utils.isString(data) && ((forcedJSONParsing && !this.responseType) || JSONRequested)) {
// ...
try {
return JSON.parse(data, this.parseReviver);
// ^^^^^^^^^^^^^^^^^
// this = config
// config.parseReviver → prototype chain → attacker's function
} catch (e) {
// ...
}
}
return data;
},
],
```
Proof of Concept
```javascript
import http from 'http';
import axios from './index.js';
// Server returns a realistic authorization response
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
user: 'john',
role: 'viewer',
isAdmin: false,
canDelete: false,
balance: 100,
permissions: ['read'],
apiKey: 'sk-secret-internal-key',
}));
});
await new Promise(r => server.listen(0, r));
const port = server.address().port;
// === Before Pollution ===
const before = await axios.get(`http://127.0.0.1:${port}/api/me`);
console.log('Before:', JSON.stringify(before.data));
// {"user":"john","role":"viewer","isAdmin":false,"canDelete":false,"balance":100,...}
// === Simulate Prototype Pollution ===
let stolen = {};
Object.prototype.parseReviver = function(key, value) {
// Silently capture all original values
if (key && typeof value !== 'object') stolen[key] = value;
…
Related
Alias · CVE (1)
- CVE-2026-42044 Axios is a promise based HTTP client for the browser and Node.js. vulnerability