How HTTP Transport Hijacking via Prototype Pollution Happens in JavaScript and How to Fix It
The Problem in Plain Sight: A Dependency That Became a Liability
The package-lock.json file in the deltamod project locked axios at version 1.14.0. On the surface, that looks unremarkable — a pinned dependency doing its job. But Trivy's static analysis flagged it as CVE-2026-42033, a high-severity vulnerability that could allow an attacker to silently redirect every outbound HTTP request the application makes. The fix was a version bump, but understanding why the old version was dangerous — and exactly what changed — is worth a close look.
The Vulnerability Explained
What Is Prototype Pollution?
JavaScript's prototype chain means that almost every object in a Node.js runtime ultimately inherits from Object.prototype. If an attacker can write an arbitrary key to Object.prototype, every plain object in the process will appear to have that key as an "own-like" property — unless the code explicitly guards against inherited values.
// Attacker-controlled input triggers this somewhere in the dependency tree:
Object.prototype.adapter = maliciousTransportAgent;
// Now, inside axios's transport resolution:
const config = {};
console.log(config.adapter); // → maliciousTransportAgent ← NOT the real axios adapter
How Axios 1.14.0 Was Affected
In axios versions before the fix, the library resolved its HTTP transport adapter by reading the adapter (and related transport configuration) properties from the merged request config object. Because this lookup did not use Object.prototype.hasOwnProperty checks or Object.create(null) maps, a polluted prototype could inject a foreign value into the transport resolution path.
The vulnerable package-lock.json entry looked like this:
"node_modules/axios": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.14.0.tgz",
"integrity": "sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ==",
"dependencies": {
"follow-redirects": "^1.15.11",
"form-data": "^4.0.5",
"proxy-from-env": "^2.1.0"
}
}
Two things stand out:
- No
https-proxy-agent— there is no explicit, version-pinned HTTPS proxy agent. The transport layer for HTTPS connections was therefore resolved more loosely. follow-redirectsfloor at^1.15.11— this floor is below the version that introduced hardened prototype-pollution defenses in redirect handling.
The Attack Scenario
Imagine deltamod is a desktop application that fetches update manifests or patch metadata over HTTPS. An attacker who can influence any JavaScript object deserialization in the same process (e.g., via a malicious plugin, a crafted TOML config loaded through @std/toml, or a compromised transitive dependency) could execute:
// Somewhere in a malicious or compromised dependency:
const payload = JSON.parse('{"__proto__": {"adapter": "http"}}');
// Or more directly:
Object.prototype.httpsAgent = new http.Agent({ /* attacker proxy */ });
Once Object.prototype is polluted, the next time deltamod calls:
axios.get('https://update-server.example.com/manifest.json')
…axios's internal config merge produces an object that appears to have httpsAgent set to the attacker's proxy. All HTTPS traffic is now routed through that proxy, enabling:
- Credential theft — any
Authorizationheaders or tokens in requests are exposed. - Response tampering — the attacker can serve a malicious update manifest, potentially triggering a malicious binary download.
- Data exfiltration — request bodies containing user data are intercepted silently.
For deltamod specifically — a tool that downloads and applies binary patches — a hijacked transport could mean the application installs attacker-controlled files.
The Fix
What Changed in package-lock.json
The pull request upgrades axios from 1.14.0 to 1.18.0. Here is the exact diff for the axios entry:
Before:
"node_modules/axios": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.14.0.tgz",
"integrity": "sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ==",
"dependencies": {
"follow-redirects": "^1.15.11",
"form-data": "^4.0.5",
"proxy-from-env": "^2.1.0"
}
}
After:
"node_modules/axios": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.18.0.tgz",
"integrity": "sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==",
"dependencies": {
"follow-redirects": "^1.16.0",
"form-data": "^4.0.5",
"https-proxy-agent": "^5.0.1",
"proxy-from-env": "^2.1.0"
}
},
"node_modules/axios/node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"dependencies": {
"debug": "4"
},
"engines": {
"node": ">= 6.0.0"
}
}
Why Each Change Matters
| Change | Security Significance |
|---|---|
follow-redirects floor raised from ^1.15.11 → ^1.16.0 |
follow-redirects 1.16.0 introduced its own prototype-pollution guards in redirect URL resolution, closing a secondary vector |
https-proxy-agent: ^5.0.1 added as an explicit dependency |
axios now resolves the HTTPS transport agent from a declared, integrity-checked package rather than from a loosely inherited property |
New agent-base@6.0.2 sub-dependency pinned |
Provides the underlying socket abstraction that https-proxy-agent uses; having it explicitly pinned prevents prototype-inherited agent-base substitution |
package.json axios range updated from ^1.12.0 → ^1.18.0 |
Ensures future npm install runs never resolve back to a vulnerable version |
The Core Security Improvement
By making https-proxy-agent an explicit, integrity-verified dependency, axios 1.18.0 no longer needs to fall back to prototype-chain property lookup when constructing its HTTPS transport. The agent is imported directly:
// Conceptual representation of the fix inside axios internals:
// Before (vulnerable): agent resolved from config object — pollutable
const agent = config.httpsAgent; // could come from Object.prototype
// After (fixed): agent resolved from explicit, imported module
const { HttpsProxyAgent } = require('https-proxy-agent'); // explicit import
const agent = proxyUrl ? new HttpsProxyAgent(proxyUrl) : undefined;
This means even if Object.prototype.httpsAgent is polluted, axios ignores it in favor of the explicitly constructed agent.
Prevention & Best Practices
1. Guard Against Prototype Pollution in Your Own Code
When merging configuration objects, never use plain {} as a base if the source could be attacker-influenced:
// Vulnerable
const config = Object.assign({}, userInput, defaults);
// Safer
const config = Object.assign(Object.create(null), userInput, defaults);
// Or use structured clone:
const config = structuredClone(defaults);
Object.assign(config, sanitize(userInput));
2. Validate Own-Property Access
When reading security-sensitive properties from config objects:
// Vulnerable
const adapter = config.adapter;
// Safe
const adapter = Object.prototype.hasOwnProperty.call(config, 'adapter')
? config.adapter
: defaultAdapter;
3. Pin and Audit Your Dependency Tree Regularly
The deltamod package.json had "axios": "^1.12.0" — a range that allowed any 1.x minor. While semver ranges are convenient, they mean a npm install on a fresh machine could resolve to any version in that range. After this fix, the range is ^1.18.0, which ensures the minimum resolved version is always patched.
# Run regularly in CI:
npm audit
npx better-npm-audit audit
# Or use Trivy directly:
trivy fs --scanners vuln package-lock.json
4. Use --ignore-scripts and Subresource Integrity
npm ci --ignore-scripts
This prevents malicious postinstall scripts from polluting prototypes during installation.
5. Consider --frozen-lockfile in Production
npm ci # always uses package-lock.json exactly; never resolves ranges
Security Standards Reference
- CWE-1321: Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')
- OWASP A06:2021 – Vulnerable and Outdated Components
- OWASP A08:2021 – Software and Data Integrity Failures
Key Takeaways
package-lock.jsonlockingaxios@1.14.0was the direct root cause — a version range of^1.12.0inpackage.jsonsilently allowed a vulnerable version to be installed for months.- The absence of an explicit
https-proxy-agentdependency in axios 1.14.0 meant HTTPS transport resolution was vulnerable to prototype-inherited property injection — a subtle but critical gap. - Raising the
follow-redirectsfloor from^1.15.11to^1.16.0closes a secondary prototype-pollution vector in redirect URL handling that would otherwise remain open even after the primary fix. - For deltamod specifically, the risk was concrete: a hijacked transport could have caused the application to download and apply attacker-controlled binary patches, leading to full system compromise.
- Trivy's static analysis of
package-lock.jsoncaught this before exploitation — demonstrating the value of scanning lock files, not just source code.
How Orbis AppSec Detected This
- Source: The tainted data entry point is any JavaScript object deserialization or merge operation within the deltamod process (or its dependency tree) that processes attacker-influenced input without prototype-pollution guards.
- Sink: axios's internal transport adapter resolution, which reads
httpsAgentandadapterfrom the merged request config object — properties that can be inherited from a pollutedObject.prototype. - Missing control: axios 1.14.0 lacked an explicit
https-proxy-agentimport and did not performhasOwnPropertychecks before using transport-related config properties, allowing prototype-inherited values to substitute the legitimate transport agent. - CWE: CWE-1321 — Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')
- Fix: Upgraded axios from
1.14.0to1.18.0inpackage-lock.json, adding an explicithttps-proxy-agent@^5.0.1dependency and raising thefollow-redirectsfloor to^1.16.0.
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-42033 is a reminder that prototype pollution is not just a theoretical concern — in a library as widely used as axios, it translates directly into HTTP transport hijacking that can silently redirect every outbound request your application makes. For deltamod, a tool that downloads and applies binary patches, that could have meant the difference between a legitimate update and a full system compromise.
The fix is straightforward: upgrade axios to 1.18.0. But the broader lesson is architectural — explicit dependency declarations, integrity hashes, and own-property guards are the layers that prevent prototype pollution from becoming transport hijacking. Keep your lock files audited, your dependency ranges tight, and your CI pipelines running npm audit and Trivy on every commit.