Back to Blog
critical SEVERITY7 min read

How Denial of Service via Gzip Bomb happens in Node.js tar and how to fix it

A critical Denial of Service vulnerability (CVE-2026-59873) was discovered in the `tar` npm package at version 2.2.2, used in the frontend dependency tree. An attacker could craft a malicious gzip bomb that, when processed by node-tar, would expand to consume all available memory and crash the application. The fix upgrades `tar` from the legacy 2.2.2 to 7.5.19, which includes decompression limits and removes the vulnerable `block-stream` dependency.

O
By Orbis AppSec
Published September 6, 2026Reviewed September 6, 2026

Answer Summary

CVE-2026-59873 is a critical Denial of Service vulnerability in the Node.js `tar` (node-tar) package, classified under CWE-400 (Uncontrolled Resource Consumption). In version 2.2.2, node-tar lacked decompression size limits, allowing a crafted gzip bomb to exhaust system memory. The fix is to upgrade `tar` to version 7.5.19 in `package.json` and `package-lock.json`, which introduces safe decompression bounds and replaces the legacy `block-stream` with modern streaming primitives.

Vulnerability at a Glance

cweCWE-400 (Uncontrolled Resource Consumption)
fixUpgrade `tar` from 2.2.2 to 7.5.19, replacing `block-stream` with `@isaacs/fs-minipass` and modern `minipass` streaming with built-in decompression limits
riskAttacker can crash the application or server by supplying a crafted tar/gzip archive that expands to consume all available memory
languageJavaScript (Node.js)
root causenode-tar 2.2.2 uses unbounded `block-stream` decompression with no size limits on extracted content
vulnerabilityDenial of Service via Gzip Bomb (Decompression Bomb)

Introduction

In the frontend build pipeline of this project, a critical vulnerability lurked not in application source code, but deep in the dependency tree. The frontend/package-lock.json pinned tar at version 2.2.2 — a release from the era when Node.js 0.4 was still a supported target. This ancient version of node-tar relied on block-stream@0.0.9, a low-level streaming primitive with no safeguards against decompression bombs. CVE-2026-59873 exposed exactly this weakness: a crafted gzip archive could be fed to node-tar, expanding to consume all available system memory and bringing down the process — or the entire server — in a classic Denial of Service attack.

This matters for every developer who manages npm dependencies. Even if your application code never directly calls tar.extract(), build tools, package managers, and transitive dependencies may invoke it during npm install, CI/CD pipelines, or asset processing. A vulnerable tar in your lockfile is a ticking time bomb.

The Vulnerability Explained

What Is a Gzip Bomb?

A gzip bomb (also called a decompression bomb or zip bomb) is a maliciously crafted compressed file designed to look tiny on disk but expand to an absurd size when decompressed. A classic example: a 42-kilobyte file that decompresses to 4.5 petabytes of data. When a program naively decompresses such a file without checking how much output it's producing, the result is memory exhaustion and a crash.

How node-tar 2.2.2 Was Vulnerable

The old dependency tree looked like this in frontend/package-lock.json:

"node_modules/block-stream": {
    "version": "0.0.9",
    "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
    "integrity": "sha512-OorbnJVPII4DuUKbjARAe8u8EfqOmkEEaSFIyoQ7OjTHn6kafxWl0wLgoZ2rXaYd7MyLcDaU4TmhfxtwgcccMQ==",
    "license": "ISC",
    "dependencies": {
        "inherits": "~2.0.0"
    },
    "engines": {
        "node": "0.4 || >=0.5.8"
    }
}

block-stream@0.0.9 was the core streaming engine used by tar@2.2.2 to process archive data. It had no concept of output size limits. When decompressing a gzip stream, it would faithfully buffer and emit every byte the compressed data produced, regardless of how much memory that consumed.

The critical issue: there was no decompression ratio check, no maximum output size, and no backpressure mechanism that could abort extraction when the output grew suspiciously large relative to the input.

Attack Scenario

Consider this realistic attack path specific to this codebase:

  1. Entry point: The frontend build process runs npm install, which processes .tgz packages from the npm registry (or a private registry). If an attacker can poison a dependency or perform a registry substitution attack, they can inject a crafted .tgz file.

  2. Exploitation: The crafted package contains a gzip bomb. When tar@2.2.2 extracts it via block-stream, the decompression expands unchecked. A 1 KB compressed payload could decompress to 10 GB+ of data.

  3. Impact: The Node.js process running npm install (or any build step that triggers tar extraction) consumes all available memory. On a CI/CD runner, this crashes the build. On a shared server, it can starve other processes of resources. In a containerized environment, it hits the memory limit and triggers an OOM kill — potentially causing cascading failures.

  4. Severity: This is rated CRITICAL because it requires no authentication, can be triggered remotely through dependency supply chain manipulation, and results in complete service disruption.

The Fix

What Changed

The fix upgrades tar from 2.2.2 to 7.5.19 by modifying two files:

  • frontend/package.json — updates the declared dependency version
  • frontend/package-lock.json — updates the resolved dependency tree

No application source code was changed. This is purely a dependency upgrade, which is the correct remediation for a vulnerability in a third-party library.

Before: Vulnerable Dependency Tree

// frontend/package-lock.json (BEFORE)
"node_modules/block-stream": {
    "version": "0.0.9",
    "dependencies": {
        "inherits": "~2.0.0"
    },
    "engines": {
        "node": "0.4 || >=0.5.8"
    }
}
// tar@2.2.2 depends on block-stream@0.0.9
// No decompression limits, no modern streaming safeguards

After: Patched Dependency Tree

// frontend/package-lock.json (AFTER)
"node_modules/@isaacs/fs-minipass": {
    "version": "4.0.1",
    "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
    "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
    "license": "ISC",
    "dependencies": {
        "minipass": "^7.0.4"
    },
    "engines": {
        "node": ">=18.0.0"
    }
}

"node_modules/chownr": {
    "version": "3.0.0",
    "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
    // ...
}
// tar@7.5.19 uses @isaacs/fs-minipass and minipass@^7.0.4
// block-stream is REMOVED entirely

Why This Fix Works

The upgrade addresses CVE-2026-59873 through several architectural changes in node-tar 7.x:

  1. block-stream removed entirely: The diff explicitly shows the removal of node_modules/block-stream@0.0.9. This legacy streaming module had no size guards and is no longer used.

  2. @isaacs/fs-minipass@4.0.1 added: This modern replacement uses minipass@^7.0.4, which implements proper backpressure and can enforce limits on data flowing through the stream pipeline.

  3. chownr upgraded to 3.0.0: The updated chownr aligns with the modern dependency chain and requires node >= 18.0.0, ensuring the runtime supports modern stream APIs with proper resource management.

  4. Decompression safeguards: tar 7.x includes built-in protections against decompression bombs, including configurable maximum entry sizes and ratio-based detection that aborts extraction when the decompression ratio exceeds safe thresholds.

  5. Modern Node.js requirement: The new engines field ("node": ">=18.0.0") ensures the library runs on a Node.js version with mature stream handling, garbage collection improvements, and security patches — a massive leap from the "node": "0.4 || >=0.5.8" requirement of block-stream.

Prevention & Best Practices

1. Pin and Audit Dependencies Regularly

# Run npm audit as part of your CI pipeline
npm audit --audit-level=critical

# Use lockfile-lint to ensure lockfile integrity
npx lockfile-lint --path frontend/package-lock.json --type npm --allowed-hosts npm

2. Automate Dependency Updates

Don't let dependencies age to the point where you're running a package targeting Node.js 0.4 in a modern application. Use automated tools to keep dependencies current.

3. Set Resource Limits in CI/CD

Even with patched dependencies, defense in depth matters:

# Example: GitHub Actions with memory limits
jobs:
  build:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - run: npm install
        env:
          NODE_OPTIONS: "--max-old-space-size=2048"

4. Monitor for Supply Chain Attacks

Use tools like Trivy, Snyk, or Socket.dev to monitor your dependency tree for known vulnerabilities and suspicious package behavior.

5. Validate Archive Processing

If your application directly processes user-supplied archives, always enforce:
- Maximum decompressed size limits
- Maximum number of entries
- Maximum path length for extracted files
- Decompression ratio thresholds

Key Takeaways

  • tar@2.2.2 with block-stream@0.0.9 had zero decompression limits, making it trivially exploitable with a crafted gzip bomb — even though the dependency appeared innocuous in a lockfile.
  • The block-stream package targeted Node.js 0.4, a version released in 2011. Running decade-old streaming primitives in modern applications is an unacceptable security risk.
  • Upgrading from tar 2.x to 7.x is a major version jump (5 major versions), but because this project only consumed tar as a transitive dependency for build tooling, no source code changes were required — only manifest updates.
  • Trivy's lockfile scanning caught this vulnerability in frontend/package-lock.json without needing to prove runtime reachability, demonstrating the value of SCA (Software Composition Analysis) in CI pipelines.
  • The fix replaces block-stream with @isaacs/fs-minipass and minipass@^7.0.4, which implement proper backpressure, size limits, and modern Node.js stream semantics — architectural improvements, not just patches.

How Orbis AppSec Detected This

  • Source: Dependency manifest frontend/package-lock.json declaring tar@2.2.2 with transitive dependency block-stream@0.0.9, which processes compressed archive data during npm install and build operations.
  • Sink: The block-stream decompression pipeline within tar@2.2.2, which expands gzip-compressed data without enforcing output size limits, leading to unbounded memory allocation.
  • Missing control: No decompression size limit, no compression ratio check, and no maximum output threshold in the archive extraction pipeline.
  • CWE: CWE-400 (Uncontrolled Resource Consumption)
  • Fix: Upgraded tar from 2.2.2 to 7.5.19 in frontend/package.json and frontend/package-lock.json, replacing block-stream with @isaacs/fs-minipass and minipass which enforce decompression safeguards.

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-59873 is a textbook example of how transitive dependency debt creates critical security exposure. A tar package pinned at version 2.2.2 — relying on block-stream@0.0.9 with its Node.js 0.4 era architecture — had no defenses against gzip bomb attacks. The fix was straightforward: upgrade to tar@7.5.19, which replaces the vulnerable streaming engine with modern, bounded alternatives. No application code needed to change.

The lesson is clear: your security posture is only as strong as your oldest, most neglected dependency. Regular auditing with tools like Trivy, automated dependency updates, and proactive lockfile scanning are essential practices for any team shipping Node.js applications.

References

Frequently Asked Questions

What is a gzip bomb Denial of Service?

A gzip bomb is a maliciously crafted compressed archive that appears small but decompresses to an enormous size (sometimes gigabytes or terabytes), exhausting system memory and CPU, causing the application to crash or become unresponsive.

How do you prevent gzip bomb DoS in Node.js?

Use up-to-date archive processing libraries like node-tar 7.x+ that enforce decompression size limits, validate archive contents before full extraction, and set resource quotas (memory limits, timeouts) on extraction operations.

What CWE is gzip bomb Denial of Service?

CWE-400: Uncontrolled Resource Consumption. This covers scenarios where an application does not properly limit the amount of resources (memory, CPU, disk) consumed when processing input.

Is simply validating file extensions enough to prevent gzip bomb attacks?

No. File extension validation does not inspect archive contents. A file with a valid `.tar.gz` extension can still be a gzip bomb. You need decompression size limits and content validation within the extraction library itself.

Can static analysis detect gzip bomb vulnerabilities?

Yes. Tools like Trivy, Snyk, and npm audit scan dependency manifests (package-lock.json) against known CVE databases and can flag vulnerable versions of libraries like node-tar before they reach production.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #16

Related Articles

high

How Denial of Service via Memory Exhaustion happens in Socket.IO Parser and how to fix it

A high-severity denial of service vulnerability (CVE-2026-69185) was discovered in socket.io-parser versions prior to 4.2.7, 3.4.5, and 3.3.6. The flaw allowed attackers to exhaust server memory through specially crafted packets, potentially crashing real-time communication services. The fix involved upgrading the socket.io-parser dependency in the react-dashboard component to the patched version 4.2.7.

critical

How Command Injection Vulnerabilities Happen in Python Subprocess Calls and How to Fix Them

A critical command injection vulnerability was discovered in `src/unused/server/fft.py` where external binaries like `oggenc` and `cocoa_text` were executed with file path parameters that could be manipulated by user input. Although `shell=False` was used, the lack of input validation allowed attackers to potentially trigger processing of arbitrary files or cause denial of service. This fix implements proper path validation to prevent exploitation.

critical

How Rate Limiting Vulnerabilities Happen in Node.js OAuth Endpoints and How to Fix Them

A critical resource exhaustion vulnerability was discovered in the OAuth token endpoint at `server/routes/oauth.js`. Without rate limiting, attackers could flood the `/api/oauth/token` endpoint with requests, each triggering expensive bcrypt verification operations that would exhaust server CPU and memory. The fix implements per-IP rate limiting using `express-rate-limit` to cap requests at 20 per 15-minute window.

critical

How Unvalidated External Content Fetching happens in Python Build Scripts and how to fix it

A Python build script in the NUR (Nix User Repository) project was fetching external content from GitHub without implementing response integrity validation or proper error handling. While TLS verification was enabled by default, the absence of timeout controls, status code validation, and integrity checks left the build pipeline vulnerable to man-in-the-middle attacks and denial-of-service conditions that could compromise the generated static site content.

high

How Denial of Service Attacks Happen in PHP Markdown Parsers and How to Fix Them

The league/commonmark library contained a denial of service vulnerability in its Attributes extension that could be triggered by specially crafted markdown with distinctly-named attributes. This vulnerability was fixed in version 2.10.0 by addressing how attribute names are processed during markdown parsing, preventing attackers from exhausting server resources.

critical

How Command Injection happens in Python subprocess calls and how to fix it

A critical command injection vulnerability was discovered in `spider/php/crawler.py` where the `PHPBridge.call()` method passed unvalidated external arguments directly to `subprocess.run()`. An attacker controlling the `spider_path` or `method` parameters could execute arbitrary PHP scripts or inject malicious method names. The fix adds strict input validation — requiring `method` to be a valid Python identifier and `spider_path` to resolve to an existing `.php` file — before any subprocess exec