AXIOS · CVE-2026-101909 · Package
FIX → 1.20.0 · 0.34.0 · >=0.34.0 +1
Axios reads several form serializer options without own-property checks, so a prototype pollution elsewhere in the same process can change how multipart and URL-encoded request bodies are built. An attacker who already controls Object.prototype can read submitted form values through a polluted visitor function or cause nested form submissions to fail with ERR_FORM_DATA_DEPTH_EXCEEDED.
first public Sep 16, 2026 · 3 sources
Could this affect your app?
Free first check from your website. Connect your repo for the full answer.
Applications that run axios in the affected ranges and serialize plain objects to multipart or URL-encoded form bodies are exposed, but only when Object.prototype is polluted in the same process. The sources state that JSON request bodies and requests that never call toFormData() are not affected, and that processes without prototype pollution are not affected. Teams that only send JSON can skip it.
Not affected if json-only, no-form-serialization, no-prototype-pollution
Who cares axios, multipart, node.js, npm, prototype-pollution
npm ls axios, and look for axios >= 0.28.0, < 0.34.0 or in the lockfile.Axios form serialization reads visitor, maxDepth, dots, indexes, metaTokens, and Blob from an internal options object without own-property guards. When Object.prototype has been polluted elsewhere in the same process, those inherited values can change how axios serializes multipart and URL-encoded request bodies.
Axios does not create the prototype pollution source. This is a read-side gadget: axios turns an existing same-process pollution condition into altered request serialization or request failures.
The impact depends on which property is polluted and which axios serialization path the application uses.
Polluted dots, indexes, or metaTokens can change field names and cause the receiving service to parse different data than the caller intended. Polluted maxDepth can cause nested form submissions to throw , producing request-level or service-level denial of service for affected workflows. Polluted can execute as the serializer visitor if an attacker can place a function on , but that condition generally implies a stronger same-process code-execution or malicious-dependency primitive and should be described carefully.
Check it with your coding agent
It reads this page and tells you if your code is affected.
Are you affected?
Ask from here. InfraStack checks the affected versions against your stack and answers in your inbox.
InfraStack checks the affected versions against your stack and answers in Slack, with the evidence and the fix.
>= 1.15.1, < 1.20.0axios.toFormData() or posts plain objects with Content-Type: multipart/form-data or URL-encoded bodies.Upgrade to 1.20.0 · 0.34.0 · >=0.34.0 +1
axios 0.34.0 for the 0.x lineaxios 1.20.0 for the 1.x lineERR_FORM_DATA_DEPTH_EXCEEDEDvisitorObject.prototypeAffected:
axios.toFormData().transformRequest paths that serialize plain objects to multipart/form-data.formSerializer option defaults when the relevant properties are absent as own properties.Not affected:
toFormData().Object.prototype is not polluted.lib/helpers/toFormData.js merges caller options with defaults using utils.toFlatObject(). When options is undefined, toFlatObject() returns the default object unchanged:
{
metaTokens: true,
dots: false,
indexes: false
}
That default object has Object.prototype in its prototype chain. toFormData() then reads behavior-affecting values directly:
const metaTokens = options.metaTokens;
const visitor = options.visitor || defaultVisitor;
const dots = options.dots;
const indexes = options.indexes;
const _Blob = options.Blob || (typeof Blob !== 'undefined' && Blob);
const maxDepth = options.maxDepth === undefined ? DEFAULT_FORM_DATA_MAX_DEPTH : options.maxDepth;
These reads can resolve inherited polluted properties.
Local code review confirmed the direct reads in v1.18.1. Tag checks show the option-based form serializer exists in v0.28.0 and later; maxDepth appears in the 1.x line from the form recursion fix.
Constrained local demonstration:
Object.prototype.maxDepth = 1;
await axios.post(url, { a: { b: { c: 'value' } } }, {
headers: { 'Content-Type': 'multipart/form-data' }
});
Expected safe behavior is that the default max depth is used unless the caller sets an own formSerializer.maxDepth. Current behavior reads the inherited value and can throw ERR_FORM_DATA_DEPTH_EXCEEDED.
For serializer alteration, polluting Object.prototype.dots = true changes nested field naming from bracket notation to dot notation when the caller did not opt into that behavior.
Avoid serializing attacker-controlled objects as form data in a process with known prototype pollution. As a partial mitigation, callers can pass an own formSerializer object that sets explicit safe values for all relevant keys, including visitor, maxDepth, dots, indexes, metaTokens, and Blob.
axios v1.18.1 contains a read-side prototype pollution gadget in its form data serialization logic. Six option properties (visitor, maxDepth, dots, indexes, metaTokens, Blob) are read from a plain JavaScript object that inherits from Object.prototype without hasOwnProperty guards. When Object.prototype has been polluted elsewhere in the process a common consequence of compromised transitive npm dependencies, these polluted values silently control axios' form serialization behavior.
The highest-impact gadget is visitor: a polluted function on Object.prototype.visitor is invoked for every key-value pair during multipart and URL-encoded form serialization, receiving the value, key, path, and internal helper functions as arguments.
The attack chain has three steps:
Step 1: formSerializer is read safely, but undefined flows through
In lib/defaults/index.js, the default transformRequest function reads formSerializer from config using the own() helper, which enforces hasOwnProp:
const formSerializer = own(this, 'formSerializer');
When the user does not explicitly configure formSerializer, this correctly returns undefined. That undefined is then passed as the options parameter to toFormData():
return toFormData(data, _FormData && new _FormData(), formSerializer);
// ^^^^^^^^^^^^ undefined
Step 2: toFlatObject returns a plain-object default
Inside lib/helpers/toFormData.js, options (which is undefined) is merged with defaults via utils.toFlatObject():
options = utils.toFlatObject(
options, // undefined
{ metaTokens: true, dots: false, indexes: false }, // plain object literal
false,
function defined(option, source) {
return !utils.isUndefined(source[option]);
}
);
toFlatObject has an early-return for null/undefined sources:
// lib/utils.js:607
if (sourceObj == null) return destObj;
Since options is undefined, the function returns destObj unchanged — the plain object { metaTokens: true, dots: false, indexes: false }. This object's prototype is Object.prototype.
Step 3: Options are read without hasOwnProp guards
The six option properties are read directly from the plain object:
const metaTokens = options.metaTokens; // line 117
const visitor = options.visitor || defaultVisitor; // line 119
const dots = options.dots; // line 120
const indexes = options.indexes; // line 121
const _Blob = options.Blob || (typeof Blob !== 'undefined' && Blob); // line 122
const maxDepth = options.maxDepth === undefined // line 123
? DEFAULT_FORM_DATA_MAX_DEPTH
: options.maxDepth;
None of these reads use utils.hasOwnProp(). Since the options object inherits from Object.prototype, any property set on Object.prototype by a compromised dependency is resolved through the prototype chain.
axios has extensive prototype pollution defenses. However, those defenses are all focused on the config object (created by mergeConfig, which returns Object.create(null)). The toFormData function creates its own internal options object that sits outside that boundary, and the 6 reads on that internal object were never audited.
Any environment with Node.js and npm. Tested on: - Node.js v24.15.0, npm 11.13.0 - axios v1.18.1 (latest release at time of writing)
mkdir axios-pp-poc
cd axios-pp-poc
npm init -y
npm install axios@1.18.1
Create poc.mjs with the following content:
import axios from 'axios';
import http from 'http';
// Simulate pollution from a compromised tran
This page as .md · .json · CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:H/SC:N/SI:N/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
GitHub Advisory Database, CC-BY 4.0 · NVD data is provided by NIST and is public domain. Posts quoted belong to their authors.