Back to Blog
medium SEVERITY6 min read

Axios DoS via Unbounded Stream Consumption Fixed in pnpm-lock.yaml

A medium-severity Denial of Service vulnerability (CVE-2026-42036) was discovered in axios 1.12.2, where using `responseType: 'stream'` could allow an attacker to exhaust server memory through unbounded stream consumption. The fix upgrades axios from version 1.12.2 to 1.15.1 in the project's `pnpm-lock.yaml`, closing the attack surface before it could be exploited in production.

O
By Orbis AppSec
Published May 31, 2026Reviewed June 3, 2026

Answer Summary

CVE-2026-42036 is a medium-severity Denial of Service vulnerability in axios 1.12.2 (Node.js HTTP client) where using `responseType: 'stream'` allows an attacker to exhaust server memory through unbounded stream consumption, classified under CWE-400 (Uncontrolled Resource Consumption). The fix is a dependency upgrade from axios 1.12.2 to 1.15.1, applied in `pnpm-lock.yaml`, which patches the stream-handling logic to enforce proper resource limits and prevent memory exhaustion.

Vulnerability at a Glance

cweCWE-400
fixUpgrade axios from 1.12.2 to 1.15.1 in pnpm-lock.yaml
riskAttacker can exhaust server memory, causing service unavailability
languageJavaScript / Node.js
root causeaxios 1.12.2 does not bound memory usage when consuming HTTP response streams with `responseType: 'stream'`
vulnerabilityDenial of Service via Unbounded Stream Consumption

Axios DoS via Unbounded Stream Consumption: How responseType: 'stream' Became an Attack Vector

Introduction

The pnpm-lock.yaml file in this project pins axios at version 1.12.2 — a version that contains a quietly dangerous flaw. When HTTP responses are consumed using responseType: 'stream', axios fails to enforce any upper bound on how much data it will accept from the remote server. The result? A malicious or misconfigured server can push an endless stream of bytes into your Node.js process, consuming memory until the application crashes or becomes unresponsive.

This is CVE-2026-42036, a medium-severity Denial of Service vulnerability that affects any application using axios with streaming responses. If your backend proxies external content, fetches large files, or pipes API responses — this vulnerability deserves your full attention.


The Vulnerability Explained

What Goes Wrong with responseType: 'stream'?

When you configure an axios request like this:

const response = await axios.get('https://external-api.example.com/data', {
  responseType: 'stream'
});

response.data.pipe(someWritableStream);

You're telling axios: "Don't buffer this response — give me the raw Node.js readable stream." This is a perfectly legitimate pattern for handling large files, real-time data feeds, or proxying responses.

The problem in axios 1.12.2 is that the stream handed back to the caller has no built-in consumption limit. Axios does not apply a maxContentLength guard when the response type is stream, nor does it enforce a timeout on how long an idle or slow stream can hold a connection open.

The Attack Scenario

Consider a Node.js service that proxies responses from a third-party API:

// A typical proxy handler — looks safe, but isn't with axios 1.12.2
app.get('/proxy', async (req, res) => {
  const upstream = await axios.get(req.query.url, {
    responseType: 'stream'
  });
  upstream.data.pipe(res);
});

An attacker — or a compromised upstream server — can respond to this request with an HTTP response that never ends, or that sends data at a trickle for hours. Because axios 1.12.2 does not cap stream consumption:

  1. The Node.js event loop remains tied to this connection.
  2. Memory allocated for buffering grows without bound.
  3. With enough concurrent requests of this type, the process exhausts available heap memory.
  4. The application crashes or becomes too slow to serve legitimate users.

This is a classic slow-read / infinite-stream DoS attack. The attacker doesn't need to send data fast — they just need to keep the connection alive and growing.

Why maxContentLength Wasn't Enough

Axios does expose a maxContentLength option, but in 1.12.2, this guard was not reliably enforced for streaming responses. The check was designed for buffered responses where axios accumulates the full body before resolving the promise. When responseType: 'stream' bypasses that accumulation step, the guard is effectively skipped — leaving the stream unbounded.


The Fix

What Changed: axios 1.12.21.15.1

The remediation is a direct dependency upgrade captured in pnpm-lock.yaml:

# Before (vulnerable)
axios:
  version: 1.12.2

# After (fixed)
axios:
  version: 1.15.1

Axios 1.15.1 addresses the unbounded stream consumption issue by ensuring that stream responses are subject to the same content-length and timeout enforcement as buffered responses. Specifically, the fix in the axios codebase:

  • Enforces maxContentLength for stream responses — the readable stream now tracks bytes transferred and destroys the stream if the configured limit is exceeded.
  • Applies response timeout to streaming connections — a connection that stalls mid-stream will now be terminated after the configured timeout window, rather than being held open indefinitely.
  • Emits a proper error event on the stream when limits are exceeded, allowing callers to handle the rejection gracefully rather than experiencing a silent memory leak.

Before vs. After Behavior

Before (axios 1.12.2):

// maxContentLength is set, but ignored for streams
const response = await axios.get(url, {
  responseType: 'stream',
  maxContentLength: 10 * 1024 * 1024 // 10MB — NOT enforced in 1.12.2
});
// Stream can grow past 10MB with no error thrown
response.data.pipe(destination);

After (axios 1.15.1):

// maxContentLength is now enforced even for streams
const response = await axios.get(url, {
  responseType: 'stream',
  maxContentLength: 10 * 1024 * 1024 // 10MB — ENFORCED in 1.15.1
});
// Stream is destroyed and an error is emitted if 10MB is exceeded
response.data.on('error', (err) => {
  console.error('Stream limit exceeded:', err.message);
});
response.data.pipe(destination);

This is a non-breaking change for well-behaved applications — if you weren't hitting content limits before, you won't notice a difference. But it closes the door on the unbounded consumption attack.


Prevention & Best Practices

1. Always Configure maxContentLength and timeout for Stream Requests

Even with the fix in place, defense-in-depth means you should explicitly configure limits:

const response = await axios.get(url, {
  responseType: 'stream',
  maxContentLength: 50 * 1024 * 1024, // 50MB hard limit
  timeout: 30000,                       // 30 second connection timeout
  maxBodyLength: 50 * 1024 * 1024
});

2. Never Proxy Arbitrary User-Supplied URLs Without Validation

The attack scenario above becomes dramatically more dangerous when users control the upstream URL. Validate and allowlist upstream hosts before making proxied requests:

const ALLOWED_HOSTS = new Set(['api.trusted.com', 'cdn.trusted.com']);

function isSafeUrl(rawUrl) {
  try {
    const parsed = new URL(rawUrl);
    return ALLOWED_HOSTS.has(parsed.hostname);
  } catch {
    return false;
  }
}

3. Audit pnpm-lock.yaml (and package-lock.json) Regularly

Lock files pin transitive dependencies that your direct package.json entries may not explicitly reference. A vulnerability in a nested dependency can be invisible until you scan the lock file directly.

Tools to integrate into your CI pipeline:
- pnpm audit — scans your pnpm lock file against the npm advisory database
- Orbis AppSec — automated PR-based fixes like the one that generated this post
- Dependabot / Renovate — automated dependency update PRs
- Snyk — deep dependency graph scanning with fix suggestions

4. Apply Backpressure When Piping Streams

Even with axios fixed, always handle backpressure and error events when piping:

const source = response.data;
const dest = fs.createWriteStream('/tmp/output');

source.on('error', (err) => {
  dest.destroy();
  // handle error
});

dest.on('error', (err) => {
  source.destroy();
  // handle error
});

source.pipe(dest);

5. Reference Standards

  • CWE-400: Uncontrolled Resource Consumption — the root CWE for this vulnerability class
  • OWASP A05:2021 – Security Misconfiguration (includes missing resource limits)
  • OWASP A06:2021 – Vulnerable and Outdated Components (the direct category for this fix)

Key Takeaways

  • responseType: 'stream' bypassed axios's maxContentLength guard in versions before 1.15.1 — a guard that developers reasonably expected to protect them.
  • The pnpm-lock.yaml file is a security artifact, not just a reproducibility tool. Pinning axios at 1.12.2 locked in this vulnerability until an explicit upgrade was applied.
  • Upgrading from 1.12.2 to 1.15.1 is the minimum safe version for any project that uses axios with streaming responses — partial version bumps within 1.12.x do not contain this fix.
  • Slow-stream DoS attacks are low-bandwidth and hard to detect with traditional rate limiting — they require application-level resource caps to mitigate effectively.
  • Explicit timeout and content-length configuration in axios is now a best practice, not optional hardening, especially for any service that fetches data from external or user-controlled URLs.

Conclusion

CVE-2026-42036 is a reminder that even well-trusted libraries like axios can harbor subtle resource management flaws. The combination of a widely-used responseType: 'stream' pattern and a missing enforcement boundary in versions up to 1.12.2 created a genuine Denial of Service risk for any Node.js application that fetches external data. The upgrade to 1.15.1 — a single line change in pnpm-lock.yaml — closes that gap by bringing stream responses under the same resource controls that buffered responses have always had.

Keep your lock files audited, configure explicit resource limits on all HTTP clients, and treat dependency upgrades as security patches — not just feature updates.


This vulnerability was automatically detected and remediated by Orbis AppSec. Automated security fixes help teams stay ahead of dependency vulnerabilities without waiting for manual review cycles.

Frequently Asked Questions

What is unbounded stream consumption in axios?

It is a condition where axios reads an HTTP response stream without enforcing any size or memory limit, allowing a malicious or misbehaving server to send an arbitrarily large response that fills available memory.

How do you prevent unbounded stream consumption DoS in Node.js?

Use up-to-date HTTP client libraries that enforce stream limits, add explicit `maxContentLength` and `maxBodyLength` options, and apply backpressure or destroy streams when thresholds are exceeded.

What CWE is unbounded stream consumption?

CWE-400 — Uncontrolled Resource Consumption ("Resource Exhaustion"), which covers scenarios where a program does not properly limit the amount of resources it allocates in response to external input.

Is setting `maxContentLength` in axios enough to prevent this DoS?

It helps, but it was not sufficient in axios 1.12.2 because the stream path bypassed that check. The 1.15.1 patch closes the gap; combining the upgrade with explicit limits is the most robust approach.

Can static analysis detect unbounded stream consumption?

Yes — tools like Semgrep can flag uses of `responseType: 'stream'` in combination with outdated axios versions, and dependency scanners (Dependabot, Snyk, Orbis AppSec) can detect the vulnerable version directly in lock files.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #11

Related Articles

high

How IPv4-mapped IPv6 addresses bypass rate limiting in Express.js and how to fix it

A critical vulnerability in express-rate-limit versions prior to 8.2.2 allowed attackers to bypass per-client rate limiting on dual-stack servers by exploiting incorrect IPv6 subnet masking. When IPv4 clients connected through IPv4-mapped IPv6 addresses (like ::ffff:192.0.2.1), the library failed to properly identify unique clients, enabling unlimited requests that could lead to denial of service. The fix upgrades express-rate-limit to 8.2.2 and its dependency ip-address to 10.1.0, implementing

high

How Denial of Service via Unbounded Brace Expansion happens in Node.js and how to fix it

The brace-expansion library in Node.js contained a critical denial-of-service vulnerability where specially crafted input could trigger unbounded array expansion, consuming all available memory and crashing the process. This vulnerability affected multiple versions across the library's version branches. The fix upgrades brace-expansion to patched versions that implement strict limits on intermediate array sizes.

high

How Remote Memory Exhaustion Happens in Rust QUIC Implementations and How to Fix It

A high-severity vulnerability in `quinn-proto` allowed remote attackers to exhaust server memory by sending carefully crafted out-of-order QUIC stream data, triggering unbounded buffer growth during reassembly. The fix upgrades `rustls-webpki` from `0.103.10` to `0.103.13` in `Cargo.lock`, closing a related denial-of-service primitive that could be chained with the stream reassembly weakness. Together, these changes harden the QUIC stack against memory exhaustion attacks that require no authenti

high

How Denial of Service via Resource Leaks Happens in Go SSH Libraries and How to Fix It

A Denial of Service vulnerability in `golang.org/x/crypto/ssh` (CVE-2026-39830) allowed attackers to exhaust server resources by sending unsolicited SSH responses that were never properly cleaned up. The fix upgrades `golang.org/x/crypto` from `v0.50.0` to `v0.52.0` in `go.mod`, patching the resource leak in the SSH package's response handling logic. Any Go application that uses the `golang.org/x/crypto/ssh` package for SSH client or server functionality was potentially exposed.

high

How Unbound Thread Allocation Denial of Service happens in Python Engine.IO and how to fix it

A high-severity vulnerability (CVE-2026-48802) in python-engineio 4.12.2 allowed attackers to exhaust system resources through unbound thread allocation, leading to denial of service. The fix upgrades the dependency to version 4.13.2, which implements thread pool limits to prevent resource exhaustion attacks against real-time WebSocket applications.

high

How Denial of Service via unbounded brace expansion happens in Node.js and how to fix it

A high-severity Denial of Service vulnerability (CVE-2026-14257) in the `brace-expansion` package version 1.1.12 allowed attackers to craft malicious brace patterns that caused exponential-time complexity, leading to out-of-memory process crashes. The fix upgrades the dependency to version 1.1.16 using npm overrides to ensure the patched version is used throughout the entire dependency tree.