{"id":"CVE-2026-101908","kind":"vulnerability","aliases":["CVE-2026-101908","GHSA-vh66-26gq-q6x8"],"related":[],"title":"Axios: Prototype pollution gadget in fetch adapter can alter outbound requests","description":"## Summary\r\n\r\nAxios 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`.\r\n\r\nAxios does not create the prototype pollution source. This is a read-side gadget that becomes exploitable after same-process prototype pollution.\r\n\r\n## Impact\r\n\r\nAn 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.\r\n\r\nThe issue is specific to fetch-adapter behavior and does not affect Node HTTP adapter requests.\r\n\r\n## Affected Functionality\r\n\r\nAffected:\r\n\r\n- `adapter: 'fetch'`.\r\n- Runtime environments where the fetch adapter is selected.\r\n- Requests where `fetchOptions` is an object that does not have safe own values for sensitive fetch init fields.\r\n\r\nNot affected:\r\n\r\n- Node HTTP adapter.\r\n- Requests that do not use the fetch adapter.\r\n- Processes where `Object.prototype` is not polluted.\r\n\r\n## Technical Details\r\n\r\n`lib/adapters/fetch.js` builds:\r\n\r\n```js\r\nconst resolvedOptions = {\r\n  ...fetchOptions,\r\n  signal: composedSignal,\r\n  method: method.toUpperCase(),\r\n  headers: toByteStringHeaderObject(headers.normalize()),\r\n  body: data,\r\n  duplex: 'half',\r\n  credentials: isCredentialsSupported ? withCredentials : undefined,\r\n};\r\n\r\nrequest = isRequestSupported && new Request(url, resolvedOptions);\r\n\r\nlet response = await (isRequestSupported\r\n  ? _fetch(request, fetchOptions)\r\n  : _fetch(url, resolvedOptions));\r\n```\r\n\r\nThe 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`.\r\n\r\nLocal 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`.\r\n\r\n## Proof of Concept of Attack\r\n\r\nConstrained local demonstration:\r\n\r\n```js\r\nObject.prototype.headers = { Authorization: 'Bearer POLLUTED' };\r\ntry {\r\n  await axios.get(url, {\r\n    adapter: 'fetch',\r\n    headers: { 'X-Good': 'yes' },\r\n    fetchOptions: {}\r\n  });\r\n} finally {\r\n  delete Object.prototype.headers;\r\n}\r\n```\r\n\r\nExpected safe behavior is that the sanitized axios headers remain in force. Current behavior can use the inherited fetch init headers instead.\r\n\r\n## Workarounds\r\n\r\nUse 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.\r\n\r\n<details>\r\n  <summary><h3>Original report</h3></summary>\r\n  \r\nHello, 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.\r\n\r\nI was testing the fetch adapter with polluted prototype values and found that `Object.prototype.headers` can change the request axios sends.\r\n\r\nThe 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.\r\n\r\nI tested it with this:\r\n\r\n```js\r\nimport http from 'node:http';\r\nimport axios from 'axios';\r\n\r\nconst server = http.createServer((req, res) => {\r\n  res.end(JSON.stringify({\r\n    authorization: req.headers.authorization || null,\r\n    xGood: req.headers['x-good'] || null\r\n  }));\r\n});\r\n\r\nawait new Promise(resolve => server.listen(0, '127.0.0.1', resolve));\r\nconst { port } = server.address();\r\n\r\nObject.prototype.headers = {\r\n  Authorization: 'Bearer POLLUTED'\r\n};\r\n\r\ntry {\r\n  const res = await axios.get(`http://127.0.0.1:${port}/`, {\r\n    adapter: 'fetch',\r\n    headers: { 'X-Good': 'yes' },\r\n    fetchOptions: {}\r\n  });\r\n\r\n  console.log(res.data);\r\n} finally {\r\n  delete Object.prototype.headers;\r\n  server.close();\r\n}\r\n```\r\n\r\nThe result I get is:\r\n\r\n```json\r\n{\r\n  \"authorization\": \"Bearer POLLUTED\",\r\n  \"xGood\": null\r\n}\r\n```\r\n\r\nSo the polluted Authorization header is sent, and the normal axios header is not.\r\n\r\nChanging the fetch call to pass the already resolved options fixes it for me:\r\n\r\n```diff\r\n- _fetch(request, fetchOptions)\r\n+ _fetch(request, resolvedOptions)\r\n```\r\n\r\n`resolvedOptions` already includes `...fetchOptions`, so custom fetch options should still work, while headers, method, body, and signal stay as clean own values.\r\n</details>","summary":"When the Axios fetch adapter runs, inherited properties on fetchOptions can override the request headers, so a process already affected by prototype pollution can make Axios send attacker-chosen headers such as Authorization while dropping the caller's headers. Axios does not create the pollution itself; it is a gadget that needs a separate prototype-pollution flaw first.","severity":"medium","cvss":6.9,"epss":0.00471,"exploited":false,"exploited_at":null,"products":[{"name":"axios","note":">=1.7.0","fixed":[">=1.20.0"],"affected":">= 1.7.0","ecosystem":"npm"},{"name":"axios","fixed":["1.20.0"],"affected":">= 1.7.0, < 1.20.0","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["axios","cwe-1321","fetch-adapter","node","npm","package"],"not_affected_if":["axios-below-1.7.0","no-fetch-adapter","no-prototype-pollution","node-http-adapter"],"how_to_check":"- Check the installed axios version with `npm ls axios`; affected versions are `>= 1.7.0, < 1.20.0`.\n- Search your code for `adapter: 'fetch'` or a `fetchOptions` option passed to Axios requests.\n- Confirm whether any code in the same process can write to `Object.prototype` (prototype pollution sources such as unsafe deep merge libraries).","fix":"- axios: upgrade to `1.20.0` or later.","status":"open","published_at":"2026-09-16T09:02:29+00:00","first_seen_at":"2026-11-09T22:53:18+00:00","triaged_at":"2026-11-09T23:00:38+00:00","updated_at":"2026-11-09T23:00:38+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-09T23:00:38Z","model":"claude-haiku-5-5","output":{"fix":"- axios: upgrade to `1.20.0` or later.","reach":"local","title":"Axios: prototype pollution gadget lets fetch adapter send attacker headers","impact":"medium","summary":"When the Axios fetch adapter runs, inherited properties on fetchOptions can override the request headers, so a process already affected by prototype pollution can make Axios send attacker-chosen headers such as Authorization while dropping the caller's headers. Axios does not create the pollution itself; it is a gadget that needs a separate prototype-pollution flaw first.","audience":"developers","category":"package","nicknames":[],"who_cares":["axios","npm","node","fetch-adapter"],"confidence":0.8,"fix_status":"available","needs_auth":"unknown","worst_case":"outbound requests carry attacker-chosen Authorization headers and lose caller headers","analyst_note":"","how_to_check":"- Check the installed axios version with `npm ls axios`; affected versions are `>= 1.7.0, < 1.20.0`.\n- Search your code for `adapter: 'fetch'` or a `fetchOptions` option passed to Axios requests.\n- Confirm whether any code in the same process can write to `Object.prototype` (prototype pollution sources such as unsafe deep merge libraries).","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["node-http-adapter","no-fetch-adapter","no-prototype-pollution","axios-below-1.7.0"],"who_is_affected":"Only applications that use Axios with the fetch adapter (adapter: 'fetch') on a runtime where fetch passes headers from fetchOptions, such as Node's undici-backed fetch, and where the process can also be polluted through another prototype-pollution flaw. The Node HTTP adapter is not affected, and processes where Object.prototype is not polluted are not exposed.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"unknown"},"cost_usd":0.001004,"input_tokens":6565,"output_tokens":694,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-101908':14A,22A '-1321':27B '-2026':13A,21A '1.18.1':364C '26gq':18A 'adapt':7A,30B,38B,97C,108C,192C,231C,239C,244C,251C,276C,284C,374C,413C,451C,465C,533C 'affect':53B,189C,208C,236C,241C,243C,273C 'alreadi':52B,148C 'alter':9A,101C 'applic':218C 'application-specif':217C 'argument':348C,353C 'attack':62B,179C,197C,400C 'attacker-chosen':61B 'attacker-control':196C 'author':67B,209C,367C,386C,406C 'avoid':469C 'await':322C,410C 'axio':1A,25B,36B,59B,74B,95C,153C,363C,431C,546C,555C 'axios':23A,24A 'axios.get':411C 'back':140C 'base':222C 'bearer':368C,387C,407C 'becom':169C 'behavior':211C,232C,426C,437C 'bodi':112C,306C 'build':294C 'cach':210C 'call':123C,371C,592C 'caller':71B,203C 'caller-specifi':202C 'case':527C 'caus':188C 'chang':543C 'chosen':63B 'complet':497C 'composedsign':300C 'concept':398C 'consid':505C 'const':296C 'constrain':401C 'contain':355C 'control':198C,223C 'creat':77B,118C,156C,556C 'credenti':115C,310C 'current':436C 'cve':12A,20A 'cwe':26B 'd':504C 'data':307C 'delet':422C 'demonstr':403C 'depend':511C 'detail':292C 'doesn':575C 'drop':69B,201C 'duplex':308C 'els':517C 'empti':471C 'environ':247C 'expect':424C 'explicit':483C 'exploit':170C 'fallback':331C 'fetch':6A,29B,37B,96C,124C,129C,141C,191C,230C,245C,250C,269C,283C,324C,327C,350C,373C,414C,442C,464C,488C,532C,568C,591C 'fetch-adapt':28B,190C,229C 'fetchopt':43B,106C,126C,256C,298C,326C,344C,381C,420C,472C,570C,574C 'fetchoptions.headers':143C 'field':271C,490C 'final':421C,590C 'first':93B,518C 'fix':461C 'flaw':92B 'forc':435C 'found':539C 'function':242C 'gadget':4A,84B,167C 'ghsa':16A 'ghsa-vh66-26gq-q6x8':15A 'good':379C,395C,418C 'half':309C 'happen':515C 'header':48B,64B,73B,111C,147C,199C,205C,221C,303C,376C,415C,432C,444C,580C 'header-bas':220C 'headers.normalize':305C 'headers/method/body':562C 'hello':493C 'http':238C,275C,450C,600C,603C 'impact':177C 'import':599C,604C 'inherit':40B,103C,142C,356C,441C,582C 'init':270C,443C,489C 'instead':127C,445C 'interact':215C 'iscredentialssupport':311C 'isrequestsupport':315C,323C 'issu':225C,508C,549C 'js':295C,404C,598C 'let':320C 'lib/adapters/fetch.js':293C,554C 'local':360C,402C 'loopback':383C 'm':495C 'make':58B 'metadata':213C 'metadata-servic':212C 'method':110C,301C 'method.touppercase':302C 'must':466C 'need':86B 'new':316C 'node':31B,136C,237C,274C,449C,602C 'npm':32B 'object':259C,473C 'object.prototype':287C,359C 'object.prototype.headers':366C,405C,423C,541C,583C 'origin':343C,491C 'outbound':10A 'overrid':45B,145C 'packag':33B 'pass':341C,470C 'path':332C,340C 'place':149C 'pollut':3A,56B,79B,91B,159C,176C,185C,290C,369C,388C,408C,478C,514C,535C 'possibl':480C 'primit':186C 'prior':182C 'process':51B,174C,285C,475C 'proof':396C 'properti':41B,104C,357C 'prototyp':2A,55B,90B,158C,175C,184C,477C,513C,536C 'prototype-pollut':89B,183C 'q6x8':19A 'read':165C 'read-sid':164C 'receiv':385C,392C 'remain':433C 'report':492C,523C 'request':11A,47B,98C,120C,125C,130C,152C,193C,240C,254C,277C,314C,317C,325C,334C,339C,459C,545C,558C,569C 'resolv':109C,561C 'resolvedopt':117C,131C,297C,319C,329C,336C 'respons':321C 'run':39B 'runtim':133C,246C 'safe':264C,425C,484C 'same-process':172C 'sanit':430C 'second':347C,352C 'secur':454C,507C 'security-sensit':453C 'seem':550C 'select':253C 'send':60B,195C,547C,565C 'sensit':268C,455C 'separ':88B 'server':384C,457C 'server-sid':456C 'servic':214C 'set':365C,482C 'side':166C,458C 'signal':113C,299C 'sinc':509C 'someth':502C 'somewher':516C 'sourc':160C 'specif':219C,227C 'specifi':204C 'summari':94C 'sure':498C 'technic':291C 'test':530C,594C 'tobytestringheaderobject':304C 'tri':409C 'undefin':313C 'undici':139C,572C 'undici-back':138C 'url':318C,328C,412C 'use':281C,335C,439C,447C,468C,587C 'valu':266C,486C,537C,584C 'verif':361C 'vh66':17A 'want':521C 'withcredenti':312C 'without':333C 'workaround':446C 'x':378C,394C,417C 'x-good':377C,393C,416C 'yes':380C,419C","nicknames":[],"mitigations":[],"who_affected":"Only applications that use Axios with the fetch adapter (adapter: 'fetch') on a runtime where fetch passes headers from fetchOptions, such as Node's undici-backed fetch, and where the process can also be polluted through another prototype-pollution flaw. The Node HTTP adapter is not affected, and processes where Object.prototype is not polluted are not exposed.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["axios"],"ptokens":["axios"],"labels":["axios","cwe1321","fetchadapter","medium","node","npm","package"],"campaign_type":null,"search_a":"'-101908':14A,22A '-2026':13A,21A '26gq':18A 'adapt':7A 'alter':9A 'axio':1A 'axios':23A,24A 'cve':12A,20A 'fetch':6A 'gadget':4A 'ghsa':16A 'ghsa-vh66-26gq-q6x8':15A 'outbound':10A 'pollut':3A 'prototyp':2A 'q6x8':19A 'request':11A 'vh66':17A","search_b":"'-1321':27B 'adapt':30B,38B 'affect':53B 'alreadi':52B 'attack':62B 'attacker-chosen':61B 'author':67B 'axio':25B,36B,59B,74B 'caller':71B 'chosen':63B 'creat':77B 'cwe':26B 'drop':69B 'fetch':29B,37B 'fetch-adapt':28B 'fetchopt':43B 'first':93B 'flaw':92B 'gadget':84B 'header':48B,64B,73B 'inherit':40B 'make':58B 'need':86B 'node':31B 'npm':32B 'overrid':45B 'packag':33B 'pollut':56B,79B,91B 'process':51B 'properti':41B 'prototyp':55B,90B 'prototype-pollut':89B 'request':47B 'run':39B 'send':60B 'separ':88B","search_c":"'1.18.1':364C 'adapt':97C,108C,192C,231C,239C,244C,251C,276C,284C,374C,413C,451C,465C,533C 'affect':189C,208C,236C,241C,243C,273C 'alreadi':148C 'alter':101C 'applic':218C 'application-specif':217C 'argument':348C,353C 'attack':179C,197C,400C 'attacker-control':196C 'author':209C,367C,386C,406C 'avoid':469C 'await':322C,410C 'axio':95C,153C,363C,431C,546C,555C 'axios.get':411C 'back':140C 'base':222C 'bearer':368C,387C,407C 'becom':169C 'behavior':211C,232C,426C,437C 'bodi':112C,306C 'build':294C 'cach':210C 'call':123C,371C,592C 'caller':203C 'caller-specifi':202C 'case':527C 'caus':188C 'chang':543C 'complet':497C 'composedsign':300C 'concept':398C 'consid':505C 'const':296C 'constrain':401C 'contain':355C 'control':198C,223C 'creat':118C,156C,556C 'credenti':115C,310C 'current':436C 'd':504C 'data':307C 'delet':422C 'demonstr':403C 'depend':511C 'detail':292C 'doesn':575C 'drop':201C 'duplex':308C 'els':517C 'empti':471C 'environ':247C 'expect':424C 'explicit':483C 'exploit':170C 'fallback':331C 'fetch':96C,124C,129C,141C,191C,230C,245C,250C,269C,283C,324C,327C,350C,373C,414C,442C,464C,488C,532C,568C,591C 'fetch-adapt':190C,229C 'fetchopt':106C,126C,256C,298C,326C,344C,381C,420C,472C,570C,574C 'fetchoptions.headers':143C 'field':271C,490C 'final':421C,590C 'first':518C 'fix':461C 'forc':435C 'found':539C 'function':242C 'gadget':167C 'good':379C,395C,418C 'half':309C 'happen':515C 'header':111C,147C,199C,205C,221C,303C,376C,415C,432C,444C,580C 'header-bas':220C 'headers.normalize':305C 'headers/method/body':562C 'hello':493C 'http':238C,275C,450C,600C,603C 'impact':177C 'import':599C,604C 'inherit':103C,142C,356C,441C,582C 'init':270C,443C,489C 'instead':127C,445C 'interact':215C 'iscredentialssupport':311C 'isrequestsupport':315C,323C 'issu':225C,508C,549C 'js':295C,404C,598C 'let':320C 'lib/adapters/fetch.js':293C,554C 'local':360C,402C 'loopback':383C 'm':495C 'metadata':213C 'metadata-servic':212C 'method':110C,301C 'method.touppercase':302C 'must':466C 'new':316C 'node':136C,237C,274C,449C,602C 'object':259C,473C 'object.prototype':287C,359C 'object.prototype.headers':366C,405C,423C,541C,583C 'origin':343C,491C 'overrid':145C 'pass':341C,470C 'path':332C,340C 'place':149C 'pollut':159C,176C,185C,290C,369C,388C,408C,478C,514C,535C 'possibl':480C 'primit':186C 'prior':182C 'process':174C,285C,475C 'proof':396C 'properti':104C,357C 'prototyp':158C,175C,184C,477C,513C,536C 'prototype-pollut':183C 'read':165C 'read-sid':164C 'receiv':385C,392C 'remain':433C 'report':492C,523C 'request':98C,120C,125C,130C,152C,193C,240C,254C,277C,314C,317C,325C,334C,339C,459C,545C,558C,569C 'resolv':109C,561C 'resolvedopt':117C,131C,297C,319C,329C,336C 'respons':321C 'runtim':133C,246C 'safe':264C,425C,484C 'same-process':172C 'sanit':430C 'second':347C,352C 'secur':454C,507C 'security-sensit':453C 'seem':550C 'select':253C 'send':195C,547C,565C 'sensit':268C,455C 'server':384C,457C 'server-sid':456C 'servic':214C 'set':365C,482C 'side':166C,458C 'signal':113C,299C 'sinc':509C 'someth':502C 'somewher':516C 'sourc':160C 'specif':219C,227C 'specifi':204C 'summari':94C 'sure':498C 'technic':291C 'test':530C,594C 'tobytestringheaderobject':304C 'tri':409C 'undefin':313C 'undici':139C,572C 'undici-back':138C 'url':318C,328C,412C 'use':281C,335C,439C,447C,468C,587C 'valu':266C,486C,537C,584C 'verif':361C 'want':521C 'withcredenti':312C 'without':333C 'workaround':446C 'x':378C,394C,417C 'x-good':377C,393C,416C 'yes':380C,419C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-09-16T09:02:29+00:00","kev":null,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:L/SI:H/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","cvss_by":"security-advisories@github.com","poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-vh66-26gq-q6x8","url":"https://github.com/axios/axios/security/advisories/GHSA-vh66-26gq-q6x8","severity":"medium","published_at":"2026-09-16T09:02:29+00:00","modified_at":"2026-09-28T17:37:32+00:00","first_seen_at":"2026-11-09T22:53:18+00:00","fetched_at":"2026-11-09T22:53:18+00:00","note":null},{"source":"ghsa","external_id":"GHSA-vh66-26gq-q6x8","url":"https://github.com/advisories/GHSA-vh66-26gq-q6x8","severity":"medium","published_at":"2026-09-30T15:13:16+00:00","modified_at":"2026-09-30T15:13:17+00:00","first_seen_at":"2026-11-09T22:57:50+00:00","fetched_at":"2026-11-09T22:57:50+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-101908","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-101908","severity":null,"published_at":"2026-09-28T18:17:19+00:00","modified_at":"2026-09-30T19:38:27+00:00","first_seen_at":"2026-11-09T23:10:12+00:00","fetched_at":"2026-11-09T23:10:12+00:00","note":null}],"documents":[{"id":110484,"url":"https://github.com/axios/axios/security/advisories/GHSA-vh66-26gq-q6x8","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa-repo","publisher":"vendor","text":null,"published_at":"2026-09-16T09:02:29+00:00","relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"},{"id":110809,"url":"https://api.github.com/repos/axios/axios/security-advisories/GHSA-vh66-26gq-q6x8","title":"Maintainer's advisory: api.github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: api.github.com"},{"id":110804,"url":"https://github.com/axios/axios/pull/11141","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":110805,"url":"https://github.com/axios/axios/commit/d19040bda7a8be2f82c3c6e1a5bc03917daee39a","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":110808,"url":"https://github.com/axios/axios/releases/tag/v1.20.0","title":"Release: github.com","kind":"release","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Release: github.com"}],"disclosures":[],"exploit_sources":[{"type":"epss","score":0.00471,"url":"https://api.first.org/data/v1/epss?cve=CVE-2026-101908"}],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}