Back to Blog
critical SEVERITY7 min read

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

A critical Denial of Service vulnerability (CVE-2026-59873) was discovered in node-tar versions prior to 7.5.19, allowing attackers to craft malicious gzip archives that expand to consume excessive memory or CPU, crashing the host process. The fix upgrades the `tar` dependency from 7.5.16 to 7.5.19 in both `package.json` and `package-lock.json`, closing the attack surface for any application that processes user-supplied or remotely fetched archives.

O
By Orbis AppSec
Published August 26, 2026Reviewed August 26, 2026

Answer Summary

CVE-2026-59873 is a critical Denial of Service vulnerability in the Node.js `node-tar` library (CWE-400: Uncontrolled Resource Consumption). Versions 7.5.16 and earlier fail to impose adequate decompression limits on gzip streams, allowing a specially crafted "gzip bomb" archive to exhaust server memory or CPU and crash the process. The fix is to upgrade `tar` to version 7.5.19, which adds decompression size guards. In this project the change was made by pinning `"tar": "^7.5.19"` in `package.json` and regenerating `package-lock.json`.

Vulnerability at a Glance

cweCWE-400
fixUpgrade `tar` to 7.5.19, which introduces decompression guards that abort extraction when output size exceeds safe thresholds
riskAttacker can crash the Node.js process by supplying a tiny compressed archive that expands to gigabytes, exhausting memory or CPU
languageJavaScript / Node.js
root causenode-tar 7.5.16 does not enforce decompression size limits on gzip streams, allowing unbounded expansion
vulnerabilityDenial of Service via Gzip Bomb (Uncontrolled Resource Consumption)

The Scenario: A Tiny File That Can Take Down Your Server

Imagine your Node.js application receives a .tar.gz upload — perhaps a plugin bundle, a build artifact, or a data import file. The file is only 50 KB. Your code hands it to the tar library and begins extracting. Seconds later, your server is out of memory and the process crashes. No payload executed, no data was stolen — but your service is offline. This is the essence of a gzip bomb attack, and it is exactly what CVE-2026-59873 enables in node-tar versions prior to 7.5.19.

This post walks through the specific vulnerability, the exact code change that fixes it, and what you can do to prevent similar issues in your own projects.


The Vulnerability Explained

What Is a Gzip Bomb?

A gzip bomb (also called a decompression bomb) exploits the fact that gzip compression ratios can be extreme. A legitimate file might compress 10:1. A crafted gzip bomb can achieve ratios of 1,000,000:1 or higher — a 50 KB file that decompresses to 50 GB. When a library streams decompressed bytes into memory without any size ceiling, the host process will consume all available RAM and either crash or be killed by the OS.

The Vulnerable Code Path in node-tar 7.5.16

The vulnerability lives in how node-tar processes the gzip layer of a .tar.gz archive. In versions up to and including 7.5.16, the library pipes the gzip decompression stream directly into the tar parser without enforcing a maximum decompressed byte count. The relevant behavior is:

[compressed .tar.gz input]
        
  zlib.createGunzip()   ← no size limit enforced here
        
  tar entry parser      ← receives unbounded byte stream
        
  file system / memory  ← exhausted by bomb payload

Because there is no guard between the Gunzip stream and the tar parser, a crafted archive can instruct the decompressor to emit an arbitrarily large output. The application's process — in this case a Netlify-hosted Node.js app — has no opportunity to intervene before memory is consumed.

The vulnerable version was locked in package-lock.json:

// BEFORE — vulnerable
"node_modules/tar": {
  "version": "7.5.16",
  "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.16.tgz",
  "integrity": "sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w=="
}

And the package.json did not pin tar as a direct dependency at all — it arrived transitively through netlify-cli ^26.1.0, meaning the vulnerable version could silently persist across installs.

Real-World Attack Scenario

Consider this application's use of netlify-cli as a production dependency. The CLI uses tar internally to pack and unpack deployment bundles. An attacker who can influence the content of a deployment archive — for example, through a compromised CI pipeline, a malicious npm package in the dependency tree, or a crafted response from a spoofed registry — could supply a .tar.gz gzip bomb. When netlify-cli (and by extension node-tar 7.5.16) attempts to extract it, the decompression loop runs without bounds, consuming all available memory on the build or runtime server.

The impact is a complete process crash — a Denial of Service that could take down a production deployment pipeline or a running service, rated CRITICAL severity.


The Fix

Two-File Change: Direct Dependency Pin + Lock File Update

The fix involves two coordinated changes:

1. package.json — Pin tar as a direct dependency

// BEFORE
"dependencies": {
  "@netlify/database": "^1.1.0",
  "@netlify/identity": "^1.2.0",
  "netlify-cli": "^26.1.0",
  "postgres": "^3.4.9"
}

// AFTER
"dependencies": {
  "@netlify/database": "^1.1.0",
  "@netlify/identity": "^1.2.0",
  "netlify-cli": "^26.1.0",
  "postgres": "^3.4.9",
  "tar": "^7.5.19"      // ← explicit floor on the safe version
}

Adding tar as an explicit direct dependency ensures that npm's resolution algorithm will always select at least version 7.5.19, even when netlify-cli or another transitive dependency would otherwise resolve to an older version.

2. package-lock.json — Updated resolution and integrity hash

// BEFORE
"node_modules/tar": {
  "version": "7.5.16",
  "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.16.tgz",
  "integrity": "sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w=="
}

// AFTER
"node_modules/tar": {
  "version": "7.5.19",
  "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.19.tgz",
  "integrity": "sha512-4LeEWl96twnS2Q7Bz4MGqgazLqO+hJN63GZxXoIqh1T3VweYD997gbU1ItNsQafqqXTXd5WFyFdReLtwvRBNiw=="
}

The updated integrity hash (sha512-4LeEWl96...) cryptographically binds the installed package to the exact bytes of version 7.5.19 — any tampering or substitution will cause npm ci to fail, providing supply-chain protection.

What Changed Inside node-tar 7.5.19

Version 7.5.19 introduces decompression size guards in the gzip pipeline. The patched library tracks the total number of bytes emitted by the Gunzip stream and aborts extraction with an error when the output exceeds a configurable (and sane default) threshold. This means a gzip bomb triggers a controlled error rather than unbounded memory growth:

[compressed .tar.gz input]
        
  zlib.createGunzip()
        
  [size counter: bytes emitted so far]  ← NEW in 7.5.19
        ↓  if > maxDecompressedSize → throw ERR_TAR_DECOMPRESSION_BOMB
  tar entry parser
        
  file system / memory  ← protected

The application's error handling can now catch this error gracefully rather than being terminated by OOM.


Prevention & Best Practices

1. Pin Transitive Security-Critical Dependencies

When a transitive dependency has a known CVE, do not rely solely on the upstream package to update. Add an explicit entry in your package.json dependencies (or overrides) to force a safe minimum version, exactly as this fix does with "tar": "^7.5.19".

// Use overrides for packages you don't directly import
"overrides": {
  "tar": "^7.5.19"
}

2. Enforce Archive Size Limits at the Application Layer

Even with a patched library, consider wrapping archive processing with application-level guards:

const MAX_UNCOMPRESSED_BYTES = 500 * 1024 * 1024; // 500 MB

async function safeExtract(archivePath, destination) {
  let totalBytes = 0;
  await tar.extract({
    file: archivePath,
    cwd: destination,
    onentry: (entry) => {
      totalBytes += entry.size;
      if (totalBytes > MAX_UNCOMPRESSED_BYTES) {
        throw new Error('Archive exceeds maximum allowed size');
      }
    }
  });
}

3. Never Process Untrusted Archives Without Timeouts

Wrap archive extraction in a timeout to limit the blast radius of any resource exhaustion attack:

const extractWithTimeout = (archivePath, dest, timeoutMs = 30_000) =>
  Promise.race([
    tar.extract({ file: archivePath, cwd: dest }),
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error('Extraction timed out')), timeoutMs)
    )
  ]);

4. Integrate Dependency Scanning in CI

The scanner that caught this vulnerability was Trivy, which compares package-lock.json entries against the NVD and GitHub Advisory Database. Add it to your CI pipeline:

# .github/workflows/security.yml
- name: Run Trivy vulnerability scanner
  uses: aquasecurity/trivy-action@master
  with:
    scan-type: 'fs'
    scan-ref: '.'
    severity: 'HIGH,CRITICAL'
    exit-code: '1'

5. Security Standards

  • OWASP A06:2021 — Vulnerable and Outdated Components: Regularly audit and update third-party dependencies.
  • CWE-400 — Uncontrolled Resource Consumption: Ensure all resource-consuming operations (decompression, parsing, network I/O) have explicit upper bounds.

Key Takeaways

  • Transitive dependencies carry real risk: tar was not in package.json at all before this fix — it arrived through netlify-cli. That didn't make CVE-2026-59873 any less exploitable.
  • Gzip bombs bypass file-size checks: Checking the size of the .tar.gz file before extraction is useless against this attack. Only decompression-time limits (added in node-tar 7.5.19) are effective.
  • Pinning a direct dependency overrides transitive versions: Adding "tar": "^7.5.19" to dependencies in package.json is the correct npm idiom to force a minimum safe version regardless of what netlify-cli requests.
  • Integrity hashes in package-lock.json matter: The new sha512 hash for 7.5.19 ensures that npm ci will reject any attempt to substitute a different (potentially malicious) build of the package.
  • Static analysis scanners catch CVEs before attackers do: Trivy flagged this pattern in the lock file automatically — demonstrating the value of integrating dependency scanning into every build pipeline.

How Orbis AppSec Detected This

  • Source: The bun.lock / package-lock.json dependency manifest, which recorded node-tar version 7.5.16 as the resolved version of the tar package — a version known to accept unbounded gzip decompression input.
  • Sink: The tar library's internal gzip decompression pipeline (zlib.createGunzip() stream in node_modules/tar) receives data from any caller that invokes tar.extract() or tar.list() on a user-influenced or remotely fetched .tar.gz file.
  • Missing control: No maximum decompressed byte count was enforced in the Gunzip → tar-parser pipeline in version 7.5.16, allowing unbounded memory growth.
  • CWE: CWE-400 — Uncontrolled Resource Consumption
  • Fix: The tar dependency was upgraded from 7.5.16 to 7.5.19 in both package.json (as an explicit direct dependency pin) and package-lock.json (with an updated integrity hash), ensuring the patched version with decompression size guards is installed.

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 sharp reminder that compressed data is not safe data. The tar library's failure to cap decompressed output in version 7.5.16 means a single malicious archive — small enough to pass file-size checks — can silently kill a Node.js process. The fix is surgical: upgrade to 7.5.19, pin the version explicitly in package.json so it cannot regress through transitive resolution, and validate the lock file's integrity hash. Pair that with CI-integrated scanning tools like Trivy, and this class of vulnerability becomes detectable and patchable before it ever reaches production.


References

Frequently Asked Questions

What is a gzip bomb vulnerability?

A gzip bomb is a maliciously crafted compressed file that is tiny on disk but expands to an enormous size when decompressed. If a library processes such a file without size limits, it can exhaust system memory or CPU, causing a Denial of Service.

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

Use a version of node-tar (≥7.5.19) that enforces decompression size limits, validate archive sizes before extraction, and avoid processing untrusted archives without resource caps or timeouts.

What CWE is a gzip bomb Denial of Service?

CWE-400 — Uncontrolled Resource Consumption. The program does not limit the resources used during decompression, allowing an attacker to trigger exhaustion.

Is input validation alone enough to prevent gzip bomb attacks?

No. Checking the compressed file size is insufficient because gzip bombs are intentionally tiny when compressed. You need decompression-time size limits enforced inside the extraction library itself.

Can static analysis detect gzip bomb vulnerabilities?

Yes. Tools like Trivy and Grype scan dependency manifests (package.json, package-lock.json) against known CVE databases and will flag vulnerable versions of node-tar, as happened here with CVE-2026-59873.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #106

Related Articles

high

How Denial of Service via infinite loop happens in Node.js dependencies and how to fix it

A high-severity Denial of Service vulnerability in the nanoid package (CVE-2026-67213) was discovered in the project's dependency tree, where crafted input could trigger an infinite loop during random ID generation. The fix upgrades nanoid from 3.3.17 to 3.3.18 and adds an npm override to ensure all transitive dependencies use the patched version.

high

How Dependabot Missing Cooldown happens in GitHub Actions and how to fix it

A Dependabot configuration in `.github/dependabot.yml` was missing cooldown periods for both its npm and GitHub Actions package ecosystems, meaning newly published — potentially malicious or unstable — package versions could be proposed for adoption immediately after release. Adding a `cooldown` block with `default-days: 7` to each ecosystem entry creates a 7-day buffer, allowing the security community time to identify and flag compromised packages before they reach your codebase.

high

How pnpm Missing Minimum Release Age happens in Node.js workspaces and how to fix it

A missing `minimumReleaseAge` setting in `pnpm-workspace.yaml` left this Node.js workspace vulnerable to immediately installing newly published — potentially malicious — package versions. The fix adds `minimumReleaseAge: 10080` (7 days in minutes) to enforce a quarantine window before any freshly published package can be installed. This single configuration change significantly reduces the risk of supply chain attacks targeting the package publishing pipeline.

high

How Dependabot Missing Cooldown happens in GitHub Actions and how to fix it

A high-severity misconfiguration in `.github/dependabot.yml` left three `package-ecosystem` entries without a cooldown period, meaning Dependabot could immediately propose updates from newly published—potentially malicious—packages. The fix adds a `cooldown` block with `default-days: 7` to each entry, introducing a mandatory waiting period before any newly released package version is surfaced as an update candidate. For a Node.js library whose vulnerabilities ripple downstream to all consumers,

critical

How Unauthenticated Proxy Endpoints Enable DoS Amplification in FastAPI and how to fix it

Public proxy endpoints in `backend/api/proxy.py` had no rate limiting, allowing any attacker to flood the httpx connection pool with unauthenticated requests and amplify denial-of-service attacks against downstream tile and coordinate-conversion services. The fix introduces a per-IP sliding-window rate limiter using environment-configurable thresholds, closing the amplification vector without breaking legitimate usage.

high

How Dependabot Missing Cooldown happens in GitHub Actions and how to fix it

A missing `cooldown` block in `.github/dependabot.yml` meant that Dependabot could immediately propose updates to newly published npm packages — including those that may be malicious, compromised, or unstable. By adding a `cooldown` with `default-days: 7`, the project now waits one week before surfacing new package versions, giving the security community time to detect and flag bad releases before they reach production.