Back to Blog
high SEVERITY6 min read

How Denial of Service via Prototype Pollution happens in Axios and how to fix it

Axios versions prior to 1.15.1 merged untrusted configuration objects without guarding against the `__proto__` key, letting attacker-controlled input pollute `Object.prototype` and crash or destabilize applications. Upgrading axios (and its transitive dependencies `form-data`, `follow-redirects`, `proxy-from-env`) closes this Denial of Service and prototype-pollution attack surface without changing any application code.

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published September 7, 2026•Reviewed September 7, 2026

Answer Summary

CVE-2026-25639 is a Denial of Service vulnerability in the axios HTTP client caused by its `mergeConfig` function recursively merging objects without rejecting the `__proto__` key, mapped to CWE-1321 (Prototype Pollution). It's fixed by upgrading axios from 1.12.2 to 1.15.1 (and 0.31.1 for the 0.x line), which adds explicit guards against prototype-polluting keys during config merging.

Vulnerability at a Glance

cweCWE-1321 (Improperly Controlled Modification of Object Prototype Attributes)
fixUpgrade axios to 1.15.1 (and the 0.x line to 0.31.1), which sanitizes merge keys and updates dependent packages (`form-data`, `follow-redirects`, `proxy-from-env`)
riskAttacker-influenced config or request objects can pollute `Object.prototype`, crashing the process or corrupting shared object behavior across the app
languageJavaScript / Node.js
root causeaxios `mergeConfig` recursively copied properties from source config objects into targets without excluding `__proto__`, `constructor`, or `prototype` keys
vulnerabilityDenial of Service via Prototype Pollution (`__proto__` key in mergeConfig)

Introduction

This Denial of Service vulnerability could have allowed attackers to crash Node.js processes or silently corrupt shared object behavior across an entire application — simply by getting a specially crafted __proto__ key into a configuration object that axios later merges. The flaw lived deep inside axios's internal mergeConfig utility, the function every axios request uses to combine default configuration with per-request options (headers, params, baseURL, transformers, and more).

In this repository, the dependency was pinned to axios 1.12.2 in package-lock.json, a version that predates the fix for this exact issue. Any code path where request configuration is built from user-influenced data — think a proxy service that forwards client-supplied headers or params into an axios call — could be exposed to this class of bug even though no application code directly referenced __proto__.

The Vulnerability Explained

Axios builds its final request configuration by recursively merging several config sources together (defaults, instance config, and per-call overrides). Internally, mergeConfig walks the keys of each source object and copies them onto a target object:

// simplified representation of the vulnerable merge pattern
function mergeDeepProperties(target, source) {
  for (const key in source) {
    target[key] = source[key];   // no check for "__proto__"
  }
  return target;
}

The problem is that for...in (and naive Object.keys copies) will happily iterate over a key literally named __proto__ if it exists as an own enumerable property on the source object — which is exactly what happens when that object was created via JSON.parse() on attacker-controlled input, such as:

{ "__proto__": { "isAdmin": true, "toString": "polluted" } }

If a caller passes an object like this as part of axios request config (for example, merging user-supplied query params or headers into the config before calling axios(config)), the assignment target[key] = source[key] effectively becomes target.__proto__ = { ... }, mutating Object.prototype for the entire Node.js process — not just the current request.

Example attack scenario: Imagine a backend service that exposes an endpoint accepting a JSON body used to build an outbound axios request, e.g. axios.request(mergeConfig(defaults, userSuppliedOptions)). An attacker submits userSuppliedOptions containing a __proto__ key with a nested object designed to:
- Overwrite Object.prototype.toString or hasOwnProperty, breaking unrelated code that relies on default prototype behavior, or
- Inject unexpected enumerable properties onto every plain object in the app, causing infinite loops or unexpected branches in code that iterates for...in over objects — leading to CPU exhaustion and a Denial of Service.

Because the pollution lands on Object.prototype, the blast radius isn't scoped to the axios call — it can affect authentication checks, template rendering, or any other logic elsewhere in the same Node.js process that trusts default object behavior.

The Fix

The remediation here isn't a hand-written patch to application code — it's a dependency upgrade that pulls in the upstream axios fix. The diff in package-lock.json shows the actual change:

- "version": "1.12.2",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz",
- "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
+ "version": "1.15.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.1.tgz",
+ "integrity": "sha512-WOG+Jj8ZOvR0a3rAn+Tuf1UQJRxw5venr6DgdbJzngJE3qG7X0kL83CZGpdHMxEm+ZK3seAbvFsw4FfOfP9vxg==",
  "dependencies": {
-   "follow-redirects": "^1.15.6",
-   "form-data": "^4.0.4",
-   "proxy-from-env": "^1.1.0"
+   "follow-redirects": "^1.15.11",
+   "form-data": "^4.0.5",
+   "proxy-from-env": "^2.1.0"
  }

Inside axios 1.15.1, the internal merge logic (mergeConfig / mergeDeepProperties) now explicitly filters out dangerous keys — __proto__, constructor, and prototype — before copying properties, and iterates safely so a config object crafted by an attacker can no longer reach Object.prototype. This closes the exact code path described in the CVE ("Denial of Service via __proto__ Key in mergeConfig") without axios changing its public API — valid configuration objects merge exactly as before.

The lockfile update also pulls in form-data 4.0.6 (bumping its hasown and mime-types ranges) and follow-redirects 1.15.11, both routine hardening releases bundled into the same upgrade. None of these transitive bumps alter axios's request/response behavior for legitimate use — the PR description correctly notes the change "tightens handling of untrusted input and leaves valid inputs unaffected."

Before: axios@1.12.2 → vulnerable mergeConfig, no key filtering.
After: axios@1.15.1 → mergeConfig rejects __proto__/constructor/prototype keys during merge.

Key Takeaways

  • The vulnerable pattern lived in axios's mergeConfig/mergeDeepProperties internals, not in this repo's own source — a reminder that transitive dependency code runs with the same trust as your own.
  • A single __proto__ key in a JSON-derived config object was enough to pollute Object.prototype process-wide, not just the current request.
  • Bumping axios from 1.12.2 to 1.15.1 in package-lock.json (plus form-data, follow-redirects, proxy-from-env transitive updates) fully remediates the flagged path with no code changes required.
  • Any service that builds axios request config from user-supplied JSON (headers, params, proxy settings) should treat this class of CVE as high-priority even if "not confirmed reachable" by static scanning.

How Orbis AppSec Detected This

  • Source: Configuration objects built from user- or client-influenced JSON (e.g., request headers, query params, or proxy settings) passed into axios's config merging pipeline.
  • Sink: axios's internal mergeConfig / mergeDeepProperties function in axios@1.12.2, which recursively copied source object keys onto the target config without filtering __proto__.
  • Missing control: No key allow-listing or explicit rejection of __proto__, constructor, or prototype during the recursive merge.
  • CWE: CWE-1321 — Improperly Controlled Modification of Object Prototype Attributes (Prototype Pollution).
  • Fix: Upgraded the axios dependency in package.json/package-lock.json from 1.12.2 to 1.15.1 (and the 0.x line to 0.31.1), which patches mergeConfig to reject prototype-polluting keys.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

This case is a textbook example of why dependency hygiene is a first-class security control, not an afterthought. The vulnerable code — a recursive object merge missing a __proto__ guard — never appeared in this project's own source files, yet it shipped with every axios request made through version 1.12.2. Upgrading to axios 1.15.1 (and 0.31.1 on the legacy branch) eliminates the Denial of Service and prototype-pollution risk in mergeConfig with zero behavioral change for legitimate requests. Treat CVE alerts on widely-used HTTP clients like axios as urgent, keep automated dependency scanning in your pipeline, and remember that "not confirmed reachable" doesn't mean "not exploitable" — it means the attack surface exists and should be closed proactively.

Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #1181

Related Articles

high

CVE-2026-4800: lodash Template Imports Allow Code Execution

CVE-2026-4800 affects lodash's `_.template()` templating API, where untrusted input reaching the `imports` option can lead to arbitrary code execution. The fix was shipped as a dependency upgrade from lodash 4.17.21 to 4.18.1 in the project's lockfile, though the PR itself notes it was never verified against the actual code paths in this repository.

critical

proxy-addr 2.0.7 IP Spoofing: CVE-2026-90711 Trust Bypass

A critical vulnerability in proxy-addr 2.0.7 allowed attackers to spoof client IP addresses by manipulating X-Forwarded-For headers when the trust chain evaluation contained specific misconfigurations. The fix in version 2.0.8 hardens the trust evaluation logic to prevent IP address falsification in Express.js applications relying on this common middleware dependency.

high

TweenMax `_applyCycle` Prototype Pollution via vars.cycle Keys

A bundled copy of the TweenMax animation library copied attacker-influenceable `vars.cycle` property names straight onto a tween configuration object using an unguarded `for...in` loop, so a key named `__proto__`, `constructor`, or `prototype` was written through to the object's prototype chain. The fix adds an explicit key denylist to both copies of the `_applyCycle` helper so those three names are skipped during the merge. No CVE or GHSA is assigned; the issue is tracked as CWE-1321 (Improperl

high

picomatch 2.3.1 ReDoS: Extglob Pattern Catastrophic Backtracking

picomatch versions below 2.3.2, 3.0.2, or 4.0.4 contain a Regular Expression Denial of Service vulnerability in extglob pattern parsing. An attacker can cause catastrophic backtracking with patterns containing nested alternations and quantifiers, freezing any Node.js process that evaluates untrusted glob expressions.

critical

pet-window.js Dynamic Code Evaluation: CWE-94 Hardening via Number

The pet-window module constructed dynamic JavaScript by embedding raw configuration values into code strings. An attacker with local access could inject arbitrary JavaScript by modifying stored configuration. The fix replaces string interpolation with explicit Number() coercion and NaN validation for all numeric configuration parameters.

high

sanitizeUnicodeInput(): Fullwidth U+ Bypasses Codepoint Validation

The `sanitizeUnicodeInput()` helper used by the project character-range settings screen rewrote `U+` prefixes to `0x` and called `parseInt()`, but never normalized its argument first. Compatibility-equivalent forms such as fullwidth `U+`, superscript digits, or mathematical alphanumerics never matched the `/U\+/gi` regex, fell through to the `else return inputString` branch, and were handed back to callers verbatim as "sanitized" values. The fix inserts a `String.prototype.normalize('NFKC')` pas