← Back to CVE List
CVE-2026-101908NVD
Vulnerability Summary
## Summary
Axios fetch adapter requests can be altered by inherited properties on `fetchOptions`. The adapter resolves method, headers, body, signal, and credentials into `resolvedOptions`, creates a `Request`, and then calls `fetch(request, fetchOptions)` instead of `fetch(request, resolvedOptions)`. In runtimes such as Node's undici-backed fetch, inherited `fetchOptions.headers` can override the headers already placed on the `Request`.
Axios does not create the prototype pollution source. This is a read-side gadget that becomes exploitable after same-process prototype pollution.
## Impact
An attacker with a prior prototype-pollution primitive can cause affected fetch-adapter requests to send attacker-controlled headers and drop caller-specified headers. This can affect authorization, cache behavior, metadata-service interactions, or application-specific header-based controls.
The issue is specific to fetch-adapter behavior and does not affect Node HTTP adapter requests.
## Affected Functionality
Affected:
- `adapter: 'fetch'`.
- Runtime environments where the fetch adapter is selected.
- Requests where `fetchOptions` is an object that does not have safe own values for sensitive fetch init fields.
Not affected:
- Node HTTP adapter.
- Requests that do not use the fetch adapter.
- Processes where `Object.prototype` is not polluted.
## Technical Details
`lib/adapters/fetch.js` builds:
```js
const resolvedOptions = {
...fetchOptions,
signal: composedSignal,
method: method.toUpperCase(),
headers: toByteStringHeaderObject(headers.normalize()),
body: data,
duplex: 'half',
credentials: isCredentialsSupported ? withCredentials : undefined,
};
request = isRequestSupported && new Request(url, resolvedOptions);
let response = await (isRequestSupported
? _fetch(request, fetchOptions)
: _fetch(url, resolvedOptions));
```
The fallback path without `Request` uses `resolvedOptions`, but the `Request` path passes the original `fetchOptions` as the second argument to `fetch()`. That second argument can contain inherited properties from `Object.prototype`.
Local verification on axios `1.18.1` set `Object.prototype.headers = { Authorization: 'Bearer POLLUTED' }` and called the fetch adapter with `headers: { 'X-Good': 'yes' }, fetchOptions: {}`. The loopback server received `Authorization: Bearer POLLUTED` and did not receive `X-Good`.
## Proof of Concept of Attack
Constrained local demonstration:
```js
Object.prototype.headers = { Authorization: 'Bearer POLLUTED' };
try {
await axios.get(url, {
adapter: 'fetch',
headers: { 'X-Good': 'yes' },
fetchOptions: {}
});
} finally {
delete Object.prototype.headers;
}
```
Expected safe behavior is that the sanitized axios headers remain in force. Current behavior can use the inherited fetch init headers instead.
## Workarounds
Use the Node HTTP adapter for security-sensitive server-side requests until fixed. If the fetch adapter must be used, avoid passing empty `fetchOptions` objects in processes where prototype pollution is possible, and set explicit safe own values for fetch init fields.
<details>
<summary><h3>Original report</h3></summary>
Hello, I’m not completely sure if this is something you’d consider a security issue, since it depends on prototype pollution happening somewhere else first, but I wanted to report it just in case.
I was testing the fetch adapter with polluted prototype values and found that `Object.prototype.headers` can change the request axios sends.
The issue seems to be in `lib/adapters/fetch.js`. Axios creates a `Request` with the resolved headers/method/body, but then sends it with `fetch(request, fetchOptions)`. With undici, if `fetchOptions` doesn’t have its own headers, an inherited `Object.prototype.headers` value can be used during the final fetch call.
I tested it with this:
```js
import http from 'node:http';
import axios from 'axios';
const server = http.createServer((req, res) => {
res.end(JSON.stringify({
authorization: req.headers.authorization || null,
xGood: req.headers['x-good'] || null
}));
});
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
const { port } = server.address();
Object.prototype.headers = {
Authorization: 'Bearer POLLUTED'
};
try {
const res = await axios.get(`http://127.0.0.1:${port}/`, {
adapter: 'fetch',
headers: { 'X-Good': 'yes' },
fetchOptions: {}
});
console.log(res.data);
} finally {
delete Object.prototype.headers;
server.close();
}
```
The result I get is:
```json
{
"authorization": "Bearer POLLUTED",
"xGood": null
}
```
So the polluted Authorization header is sent, and the normal axios header is not.
Changing the fetch call to pass the already resolved options fixes it for me:
```diff
- _fetch(request, fetchOptions)
+ _fetch(request, resolvedOptions)
```
`resolvedOptions` already includes `...fetchOptions`, so custom fetch options should still work, while headers, method, body, and signal stay as clean own values.
</details>
---
Axios fetch adapter requests can be altered by inherited properties on `fetchOptions`. The adapter resolves method, headers, body, signal, and credentials into `resolvedOptions`, creates a `Request`, and then calls `fetch(request, fetchOptions)` instead of `fetch(request, resolvedOptions)`. In runtimes such as Node's undici-backed fetch, inherited `fetchOptions.headers` can override the headers already placed on the `Request`.
Axios does not create the prototype pollution source. This is a read-side gadget that becomes exploitable after same-process prototype pollution.
## Impact
An attacker with a prior prototype-pollution primitive can cause affected fetch-adapter requests to send attacker-controlled headers and drop caller-specified headers. This can affect authorization, cache behavior, metadata-service interactions, or application-specific header-based controls.
The issue is specific to fetch-adapter behavior and does not affect Node HTTP adapter requests.
## Affected Functionality
Affected:
- `adapter: 'fetch'`.
- Runtime environments where the fetch adapter is selected.
- Requests where `fetchOptions` is an object that does not have safe own values for sensitive fetch init fields.
Not affected:
- Node HTTP adapter.
- Requests that do not use the fetch adapter.
- Processes where `Object.prototype` is not polluted.
## Technical Details
`lib/adapters/fetch.js` builds:
```js
const resolvedOptions = {
...fetchOptions,
signal: composedSignal,
method: method.toUpperCase(),
headers: toByteStringHeaderObject(headers.normalize()),
body: data,
duplex: 'half',
credentials: isCredentialsSupported ? withCredentials : undefined,
};
request = isRequestSupported && new Request(url, resolvedOptions);
let response = await (isRequestSupported
? _fetch(request, fetchOptions)
: _fetch(url, resolvedOptions));
```
The fallback path without `Request` uses `resolvedOptions`, but the `Request` path passes the original `fetchOptions` as the second argument to `fetch()`. That second argument can contain inherited properties from `Object.prototype`.
Local verification on axios `1.18.1` set `Object.prototype.headers = { Authorization: 'Bearer POLLUTED' }` and called the fetch adapter with `headers: { 'X-Good': 'yes' }, fetchOptions: {}`. The loopback server received `Authorization: Bearer POLLUTED` and did not receive `X-Good`.
## Proof of Concept of Attack
Constrained local demonstration:
```js
Object.prototype.headers = { Authorization: 'Bearer POLLUTED' };
try {
await axios.get(url, {
adapter: 'fetch',
headers: { 'X-Good': 'yes' },
fetchOptions: {}
});
} finally {
delete Object.prototype.headers;
}
```
Expected safe behavior is that the sanitized axios headers remain in force. Current behavior can use the inherited fetch init headers instead.
## Workarounds
Use the Node HTTP adapter for security-sensitive server-side requests until fixed. If the fetch adapter must be used, avoid passing empty `fetchOptions` objects in processes where prototype pollution is possible, and set explicit safe own values for fetch init fields.
<details>
<summary><h3>Original report</h3></summary>
Hello, I’m not completely sure if this is something you’d consider a security issue, since it depends on prototype pollution happening somewhere else first, but I wanted to report it just in case.
I was testing the fetch adapter with polluted prototype values and found that `Object.prototype.headers` can change the request axios sends.
The issue seems to be in `lib/adapters/fetch.js`. Axios creates a `Request` with the resolved headers/method/body, but then sends it with `fetch(request, fetchOptions)`. With undici, if `fetchOptions` doesn’t have its own headers, an inherited `Object.prototype.headers` value can be used during the final fetch call.
I tested it with this:
```js
import http from 'node:http';
import axios from 'axios';
const server = http.createServer((req, res) => {
res.end(JSON.stringify({
authorization: req.headers.authorization || null,
xGood: req.headers['x-good'] || null
}));
});
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
const { port } = server.address();
Object.prototype.headers = {
Authorization: 'Bearer POLLUTED'
};
try {
const res = await axios.get(`http://127.0.0.1:${port}/`, {
adapter: 'fetch',
headers: { 'X-Good': 'yes' },
fetchOptions: {}
});
console.log(res.data);
} finally {
delete Object.prototype.headers;
server.close();
}
```
The result I get is:
```json
{
"authorization": "Bearer POLLUTED",
"xGood": null
}
```
So the polluted Authorization header is sent, and the normal axios header is not.
Changing the fetch call to pass the already resolved options fixes it for me:
```diff
- _fetch(request, fetchOptions)
+ _fetch(request, resolvedOptions)
```
`resolvedOptions` already includes `...fetchOptions`, so custom fetch options should still work, while headers, method, body, and signal stay as clean own values.
</details>
---
CVSS v4.0 Base Metrics — Score 6.9
Attack VectorNetwork
Attack ComplexityLow
Attack RequirementsPresent
Privileges RequiredNone
User InteractionNone
Confidentiality (Vulnerable System)None
Integrity (Vulnerable System)None
Availability (Vulnerable System)None
Confidentiality (Subsequent System)Low
Integrity (Subsequent System)High
Availability (Subsequent System)None
Affected & Patched Versions
- axios >= 1.7.0, < 1.20.0
Not provided by Private for this CVE.
External References
- https://github.com/axios/axios/security/advisories/GHSA-vh66-26gq-q6x8
- https://nvd.nist.gov/vuln/detail/CVE-2026-101908
- https://github.com/axios/axios/pull/11141
- https://github.com/axios/axios/commit/d19040bda7a8be2f82c3c6e1a5bc03917daee39a
- https://github.com/axios/axios/releases/tag/v1.20.0
- https://github.com/advisories/GHSA-vh66-26gq-q6x8