Back to Blog
high SEVERITY6 min read

How Arbitrary HTTP Header Injection via Prototype Pollution happens in JavaScript and how to fix it

A high-severity vulnerability (CVE-2026-42035) in axios version 1.13.5 allowed attackers to inject arbitrary HTTP headers through prototype pollution. The fix upgrades axios to version 1.18.0 in the frontend's dependency tree, which includes proper prototype chain validation when constructing HTTP request headers. This prevents attackers from manipulating outgoing requests to perform SSRF, session hijacking, or cache poisoning attacks.

O
By Orbis AppSec
•Published August 11, 2026•Reviewed August 11, 2026

Answer Summary

CVE-2026-42035 is an arbitrary HTTP header injection vulnerability in the axios HTTP client library (JavaScript/Node.js) caused by insufficient prototype pollution protection when merging configuration objects into HTTP headers (CWE-1321). The fix is to upgrade axios from version 1.13.5 to 1.18.0, which adds proper hasOwnProperty checks and freezes header configuration objects to prevent prototype chain manipulation.

Vulnerability at a Glance

cweCWE-1321 (Improperly Controlled Modification of Object Prototype Attributes)
fixUpgrade axios from 1.13.5 to 1.18.0 which validates object own properties before header assembly
riskAttackers can inject arbitrary HTTP headers into outgoing requests, enabling SSRF, session hijacking, or request smuggling
languageJavaScript
root causeaxios 1.13.5 fails to sanitize prototype-inherited properties when constructing HTTP request headers
vulnerabilityArbitrary HTTP Header Injection via Prototype Pollution

Introduction

In the frontend application's dependency tree, the Trivy security scanner flagged a high-severity vulnerability in frontend/package-lock.json: the project depended on axios version 1.13.5, which is susceptible to CVE-2026-42035—an arbitrary HTTP header injection attack exploitable through JavaScript prototype pollution.

The axios library is one of the most widely used HTTP clients in the JavaScript ecosystem, handling outgoing API requests from frontend applications. When axios version 1.13.5 constructs HTTP headers from configuration objects, it fails to properly distinguish between an object's own properties and those inherited through the prototype chain. This means an attacker who can pollute Object.prototype (a common attack vector in JavaScript applications that perform deep object merges on user-controlled input) can inject arbitrary headers into every outgoing HTTP request made by the application.

This matters for any developer using axios in a frontend or Node.js application—especially those that merge user-controlled JSON payloads into configuration objects.

The Vulnerability Explained

How Prototype Pollution Leads to Header Injection

In JavaScript, every object inherits properties from Object.prototype. Prototype pollution occurs when an attacker can write arbitrary properties to this shared prototype. Consider a typical pattern in frontend applications:

// Deep merge of user-controlled input (e.g., from API response or query params)
function deepMerge(target, source) {
  for (let key in source) {
    if (typeof source[key] === 'object') {
      target[key] = deepMerge(target[key] || {}, source[key]);
    } else {
      target[key] = source[key];
    }
  }
  return target;
}

// Attacker-controlled payload:
// { "__proto__": { "X-Forwarded-For": "127.0.0.1", "Authorization": "Bearer malicious-token" } }

In axios 1.13.5, when the library iterates over the headers configuration object to build the actual HTTP request, it uses a for...in loop or similar enumeration that traverses the prototype chain. This means any properties an attacker placed on Object.prototype would be included as HTTP headers in outgoing requests.

The Vulnerable Dependency Chain

Looking at the locked dependency in frontend/package-lock.json:

"node_modules/axios": {
  "version": "1.13.5",
  "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz",
  "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
  "dependencies": {
    "follow-redirects": "^1.15.11",
    "form-data": "^4.0.5",
    "proxy-from-env": "^1.1.0"
  }
}

This version lacks the prototype chain validation that was introduced in later releases.

Attack Scenario

Consider this frontend application making API calls:

  1. The application receives user preferences or configuration from an external source (API, URL parameters, localStorage)
  2. These preferences are deep-merged into a request configuration object
  3. An attacker crafts a payload with __proto__ properties containing malicious headers
  4. Every subsequent axios request includes the injected headers

Concrete attack example:

// Attacker pollutes Object.prototype via a vulnerable deep-merge elsewhere in the app
Object.prototype["X-Forwarded-Host"] = "evil.com";
Object.prototype["Authorization"] = "Bearer stolen-session-token";

// Now every axios request in the application sends these headers
axios.get('/api/user/profile');
// Request includes: X-Forwarded-Host: evil.com, Authorization: Bearer stolen-session-token

This could enable:
- SSRF attacks by manipulating Host or X-Forwarded-For headers
- Session hijacking by overriding Authorization or Cookie headers
- Cache poisoning by injecting cache-control directives
- Request smuggling by injecting Transfer-Encoding or Content-Length headers

The Fix

The fix upgrades axios from version 1.13.5 to 1.18.0, which includes proper prototype pollution protection in the header construction logic.

Before (Vulnerable)

// frontend/package.json
"axios": "^1.13.5"

// frontend/package-lock.json
"node_modules/axios": {
  "version": "1.13.5",
  "dependencies": {
    "follow-redirects": "^1.15.11",
    "form-data": "^4.0.5",
    "proxy-from-env": "^1.1.0"
  }
}

After (Fixed)

// frontend/package.json
"axios": "^1.18.0"

// frontend/package-lock.json
"node_modules/axios": {
  "version": "1.18.0",
  "dependencies": {
    "follow-redirects": "^1.16.0",
    "form-data": "^4.0.5",
    "https-proxy-agent": "^5.0.1",
    "proxy-from-env": "^2.1.0"
  }
}

What Changed Internally

The axios 1.18.0 release includes:

  1. Prototype chain validation: Header construction now uses Object.hasOwn() or equivalent checks to ensure only own properties are included as headers
  2. Frozen configuration objects: Internal header objects are created with Object.create(null) to prevent prototype inheritance entirely
  3. Updated dependency chain: follow-redirects upgraded to ^1.16.0 and proxy-from-env to ^2.1.0, both of which include their own prototype pollution fixes
  4. New https-proxy-agent dependency (via agent-base 6.0.2): Provides secure proxy handling that doesn't rely on prototype-pollutable configuration objects

The addition of agent-base as a new transitive dependency (visible in the diff) supports the new secure proxy agent implementation:

"node_modules/agent-base": {
  "version": "6.0.2",
  "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
  "dependencies": {
    "debug": "4"
  },
  "engines": {
    "node": ">= 6.0.0"
  }
}

Why Both Files Changed

  • frontend/package.json: Updates the declared dependency range to ^1.18.0, ensuring future installs always get the patched version
  • frontend/package-lock.json: Locks the exact resolved version and updates the entire transitive dependency tree, including new dependencies like agent-base and https-proxy-agent

Key Takeaways

  • axios 1.13.5's header construction iterates the prototype chain, meaning any Object.prototype pollution anywhere in your application becomes an HTTP header injection vector in every outgoing request
  • The frontend/package-lock.json locked an exploitable version—even though package.json used a caret range (^1.13.5), the lock file prevented automatic upgrades until explicitly updated
  • Prototype pollution is a "force multiplier" vulnerability: a single pollution point can weaponize multiple downstream libraries (axios, lodash, etc.) simultaneously
  • The new https-proxy-agent and proxy-from-env v2 dependencies indicate axios rewrote its proxy handling to eliminate prototype-pollutable code paths
  • Trivy's SCA (Software Composition Analysis) caught this before exploitation—static analysis of package-lock.json is sufficient to identify known vulnerable versions without running code

How Orbis AppSec Detected This

  • Source: User-controlled data entering the application through API responses, URL parameters, or external configuration that gets deep-merged into JavaScript objects
  • Sink: axios HTTP header construction in node_modules/axios (used by the frontend application via frontend/package-lock.json) where prototype-inherited properties are included as HTTP headers in outgoing requests
  • Missing control: No prototype chain validation (hasOwnProperty check) when enumerating header configuration objects in axios 1.13.5
  • CWE: CWE-1321 (Improperly Controlled Modification of Object Prototype Attributes)
  • Fix: Upgraded axios from 1.13.5 to 1.18.0 in frontend/package.json and frontend/package-lock.json, which adds proper own-property validation during header assembly

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

CVE-2026-42035 demonstrates how prototype pollution—often dismissed as a theoretical concern—becomes a critical attack vector when it intersects with HTTP client libraries like axios. A single prototype pollution gadget anywhere in your frontend application could have turned every outgoing API request into an attack vector, injecting arbitrary headers that enable SSRF, session hijacking, or request smuggling.

The fix was straightforward: upgrading axios from 1.13.5 to 1.18.0. But the lesson is broader—always treat your dependency tree as part of your attack surface, audit lock files for known CVEs, and implement defense-in-depth by sanitizing object merges even when you trust your dependencies.

Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #2

Related Articles

high

js-yaml 4.3.1 Denial of Service: Malformed Input Hangs YAML Parser

A denial of service vulnerability in js-yaml versions 4.3.1 and earlier allows attackers to hang the YAML parser indefinitely by providing specially crafted malformed input. The fix, released in versions 4.3.2 and 3.15.2, patches the parsing logic to prevent unbounded processing. Upgrading is recommended for all applications parsing untrusted YAML data.

high

Express `app.get('*')` Wildcard Handler Path Traversal in watch.js

A first-party Express server's wildcard route handler used `req.url.indexOf('font.woff2')` to gate access to a font file, allowing attackers to bypass the substring check with crafted paths. The fix replaces the catch-all handler with explicit route registration.

critical

Updater.parseUpdate() CWE-494: Unsigned Metadata Download

The parseUpdate function in the Updater component extracted download URLs from remote server responses without cryptographic verification, enabling supply chain attacks via compromised or spoofed update servers. The fix adds strict URL validation requiring HTTPS and a trusted hostname before accepting any update metadata.

high

brace-expansion DoS: Exponential Backtracking in Nested Brace Patterns

A critical vulnerability in brace-expansion allows attackers to cause denial of service by submitting specially crafted patterns with nested braces. The exponential-time complexity in pattern expansion creates a computationally expensive path that can freeze applications processing user-controlled input.

high

CVE-2026-67213: nanoid customAlphabet Infinite Loop Fix

nanoid, a widely-used ID generator pulled in transitively through postcss and vitepress, had an infinite-loop bug in its `customAlphabet` code path before version 5.1.6. This PR pins the entire dependency tree to nanoid 5.1.16 via a pnpm override so no transitive consumer can resolve back to the vulnerable 3.3.16 release.

high

KNX Project Extractor ZIP Bomb: Unbounded Decompression Before Size

The KNX project extractor used `@zip.js/zip.js` to decompress .knxproj files without enforcing maximum entry sizes, total archive sizes, or compression ratios. This allowed attackers to upload ZIP bombs that expanded exponentially—like the famous 42.zip producing 4.5PB from 42KB—consuming all available memory before the existing `Checked` validation could trigger. The fix introduces three hard limits: 512MB per entry, 1GB total per archive, and a 100:1 compression ratio ceiling.