How Denial of Service via Unbounded Data Happens in JavaScript and how to fix it
The Vulnerability at a Glance
| Field | Detail |
|---|---|
| CVE | CVE-2025-58754 |
| Severity | High |
| CWE | CWE-400 — Uncontrolled Resource Consumption |
| Affected package | axios 1.8.4 (and legacy branch < 0.30.2) |
| Fixed version | axios 1.18.0 / 0.30.2 |
| Language | JavaScript / TypeScript (Node.js) |
Introduction
The pnpm-lock.yaml file in this project pinned axios at version 1.8.4 — a version that ships without any check on how large a response or request body can be before axios starts processing it. That single missing guard is the entire attack surface for CVE-2025-58754. Any application that uses axios to fetch data from a URL that an attacker can influence — or that relays user-supplied data through axios — is exposed to a resource exhaustion attack that can silently consume all available memory or CPU until the Node.js process crashes or becomes unresponsive.
This post walks through exactly what the vulnerability is, how it can be exploited in practice, and what the upgrade from ^1.7.9 to ^1.18.0 actually changes.
The Vulnerability Explained
What is CWE-400 (Uncontrolled Resource Consumption)?
CWE-400 describes a class of bugs where software allocates or processes a resource — memory, CPU time, file handles, network buffers — in direct proportion to attacker-controlled input, with no upper bound. The result is that a sufficiently large or numerous input will exhaust the resource and deny service to legitimate users.
In axios's case, the resource is the in-memory buffer used to accumulate an HTTP response (or request) body. Before the fix, axios would keep reading and buffering data until the stream ended, regardless of how many bytes had already been consumed.
The Vulnerable Dependency Declaration
Before the fix, package.json specified:
"dependencies": {
"axios": "^1.7.9"
}
And pnpm-lock.yaml resolved this to the exact installed version:
axios:
specifier: ^1.7.9
version: 1.8.4
The ^1.7.9 semver range means "any version ≥ 1.7.9 and < 2.0.0." Because axios 1.8.4 fell inside that range, pnpm locked to it. The problem is that 1.8.4 does not include the data size check introduced in later releases.
How the Exploit Works
Imagine this application uses axios to proxy or fetch a URL supplied by a user:
// Simplified example of a vulnerable usage pattern
app.get('/fetch', async (req, res) => {
const url = req.query.url;
const response = await axios.get(url); // axios 1.8.4 — no size limit
res.json(response.data);
});
An attacker points url at a server they control that streams an infinitely large (or multi-gigabyte) response body. Axios in version 1.8.4 has no mechanism to say "this response is too big — abort." It keeps allocating Node.js heap memory to buffer the incoming bytes. The Node.js process's heap grows until:
- The OS kills the process with an out-of-memory signal, or
- The garbage collector thrashes and CPU utilization spikes to 100%, or
- The heap limit is hit and Node.js throws a fatal
JavaScript heap out of memoryerror.
In all three cases, every other request being handled by the same process is dropped. The application is down.
Even without a proxy scenario, any application that fetches data from third-party APIs could be targeted if an attacker can influence the API endpoint, inject a malicious redirect, or perform a DNS rebinding attack to point a trusted hostname at a malicious server.
Why This Is Rated High Severity
- No authentication required: The attacker only needs network access to the application or to a server the application fetches from.
- Single request can be sufficient: One well-crafted HTTP response can exhaust memory.
- No crash recovery by default: Node.js single-threaded event loop means one blocked/crashed process takes down all concurrent users.
- Widely used library: axios is one of the most downloaded npm packages, meaning the blast radius across the ecosystem is enormous.
The Fix
What Changed in package.json
- "axios": "^1.7.9"
+ "axios": "^1.18.0"
The semver range was bumped to ^1.18.0, which means pnpm will now resolve to axios 1.18.0 or any compatible patch/minor above it — all of which include the size-check fix.
What Changed in pnpm-lock.yaml
axios:
- specifier: ^1.7.9
- version: 1.8.4
+ specifier: ^1.18.0
+ version: 1.18.0
The lock file now pins to 1.18.0 exactly. This is the critical file from a security perspective: even if package.json had been updated without regenerating the lock file, the old 1.8.4 would still have been installed by pnpm install --frozen-lockfile. Both files must be updated together, which is exactly what this PR does.
What axios 1.18.0 Actually Fixes
Axios 1.18.0 introduces an internal check on the size of data being accumulated before it is parsed or returned to the caller. When a response body exceeds a configurable threshold, axios aborts the stream and rejects the promise with an appropriate error — rather than silently buffering indefinitely. This gives application code a chance to handle the error gracefully instead of running out of memory.
The security improvement is concrete: the unbounded allocation path is replaced with a bounded one, and the attacker's ability to dictate memory consumption is removed.
Why Both Files Matter
| File | Role | Why it needed updating |
|---|---|---|
package.json |
Declares the acceptable version range | Old range ^1.7.9 still permitted 1.8.4 to be installed |
pnpm-lock.yaml |
Pins the exact installed version | Without updating this, pnpm install would reinstall 1.8.4 regardless of package.json |
Prevention & Best Practices
1. Keep Dependency Lock Files in Version Control and CI
pnpm-lock.yaml (and equivalents like package-lock.json or yarn.lock) must be committed and used in CI with --frozen-lockfile. This ensures the exact vulnerable version is flagged by SCA scanners, as happened here.
2. Run SCA Scanning on Lock Files
Tools like Trivy, Snyk, and Socket scan lock files for known CVEs. Trivy flagged this exact pattern (CVE-2025-58754 in pnpm-lock.yaml) automatically. Integrate these scanners into your CI pipeline and block merges on high-severity findings.
3. Use maxContentLength and maxBodyLength in axios
Even on patched versions, you can add defense-in-depth by explicitly configuring axios size limits:
const axios = require('axios');
const client = axios.create({
maxContentLength: 10 * 1024 * 1024, // 10 MB response limit
maxBodyLength: 5 * 1024 * 1024, // 5 MB request body limit
});
This makes your intent explicit in code and provides a safety net if a future regression slips through.
4. Apply the Principle of Least Privilege to HTTP Clients
If your application fetches URLs, validate that the URL belongs to an allowlist of trusted domains before passing it to axios. This limits the attacker's ability to redirect axios at a malicious server in the first place.
5. Monitor for Heap Growth
Use Node.js APM tools (Datadog, New Relic, or even the built-in process.memoryUsage()) to alert on abnormal heap growth. Sudden spikes in heapUsed during HTTP fetch operations are a signal worth investigating.
Relevant Standards
- OWASP Top 10 A05:2021 – Security Misconfiguration: Outdated or misconfigured dependencies fall under this category.
- CWE-400: Uncontrolled Resource Consumption — the direct CWE for this vulnerability.
- OWASP Dependency-Check / OWASP Cheat Sheet: Vulnerable and Outdated Components: https://cheatsheetseries.owasp.org/cheatsheets/Vulnerable_Dependency_Management_Cheat_Sheet.html
Key Takeaways
- Pinning to
1.8.4inpnpm-lock.yamlwas the concrete attack surface: The lock file, not justpackage.json, is what determines what actually gets installed. Both must be updated together. - axios's missing data size check in versions < 1.18.0 / < 0.30.2 is the root cause: This is not a generic "keep dependencies updated" lesson — this specific version boundary (1.8.4 → 1.18.0) is where the protection was added.
- A single unbounded HTTP fetch can crash a Node.js process: Because the event loop is single-threaded, one memory-exhausting request affects all concurrent users simultaneously.
maxContentLengthandmaxBodyLengthprovide defense-in-depth: Even after upgrading, explicitly setting these axios options makes your size expectations part of your code contract.- SCA tools scanning lock files (not just
package.json) are essential: Trivy found this because it scannedpnpm-lock.yamlwhere the exact resolved version1.8.4was recorded.
How Orbis AppSec Detected This
- Source: HTTP response data received by axios when fetching from a user-influenced or attacker-controlled URL
- Sink: axios's internal response body accumulation buffer in
axios@1.8.4, which lacked any size boundary before writing to memory - Missing control: No maximum data size check on the response/request body stream prior to buffering and parsing
- CWE: CWE-400 — Uncontrolled Resource Consumption
- Fix: Upgraded the axios specifier in
package.jsonfrom^1.7.9to^1.18.0and regeneratedpnpm-lock.yamlto resolve to1.18.0, which includes the upstream data size validation.
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-2025-58754 is a sharp reminder that a single missing bounds check in a widely-used HTTP client library can turn any fetch call into a denial-of-service vector. The vulnerability in axios 1.8.4 required no special privileges, no complex payload crafting, and no code changes in the application itself — just a large enough HTTP response body. The fix is straightforward: upgrade to axios 1.18.0, update both package.json and pnpm-lock.yaml, and add explicit maxContentLength/maxBodyLength configuration as a belt-and-suspenders measure. Integrate SCA scanning into your CI pipeline so the next CVE in a transitive dependency is caught before it reaches production.