How Denial of Service via Gzip Bomb Happens in Node.js and How to Fix It
Vulnerability at a Glance
| Field | Detail |
|---|---|
| CVE | CVE-2026-59873 |
| Severity | Critical |
| Package | tar (node-tar) 7.5.16 → 7.5.19 |
| CWE | CWE-400: Uncontrolled Resource Consumption |
| Impact | Process crash / memory exhaustion (Denial of Service) |
| Fixed in | frontend/package-lock.json, frontend/package.json |
Introduction
The frontend/package-lock.json file in this project pins the tar npm package to version 7.5.16 — a version that contains a critical Denial of Service flaw. Any code path in the frontend toolchain that calls node-tar to extract or inspect a .tar.gz archive is vulnerable to a gzip bomb attack: an attacker supplies a tiny, hyper-compressed archive that, when decompressed by node-tar, expands to a volume large enough to exhaust the Node.js process's available memory or CPU time, rendering the service unresponsive.
This is not a theoretical edge case. Gzip bombs are trivially crafted with standard tools and have been used to take down build systems, CI pipelines, and upload processors for years. The fix — upgrading tar to 7.5.19 — is a single-line dependency bump with zero behavioral change for legitimate archives.
The Vulnerability Explained
What Is a Gzip Bomb?
A gzip bomb (also called a decompression bomb) exploits the nature of compression algorithms. Highly repetitive data compresses with extreme efficiency. A file containing 1 GB of repeated null bytes might compress to just a few kilobytes. When a decompressor naively expands it without any size ceiling, it consumes gigabytes of memory in seconds.
The attack chain for this CVE looks like this:
Attacker crafts malicious .tar.gz
│
▼
Attacker delivers archive to application
(upload endpoint, build artifact, package fetch, etc.)
│
▼
Application calls node-tar 7.5.16 to extract/inspect archive
│
▼
node-tar decompresses gzip stream with no expansion limit
│
▼
Memory / CPU exhaustion → process crash → Denial of Service
The Vulnerable Dependency
In frontend/package-lock.json, the locked version was:
"node_modules/tar": {
"version": "7.5.16",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.16.tgz",
...
}
node-tar 7.5.16 does not enforce a maximum decompressed byte limit when processing gzip streams inside .tar.gz archives. The decompression loop continues until the stream is exhausted — or until the host OS runs out of memory.
A Concrete Attack Scenario
Imagine the frontend build pipeline accepts a .tar.gz package as part of a dependency fetch or a user-uploaded asset bundle. An attacker uploads the following (conceptual) bomb:
# Craft a gzip bomb: 1 byte of input → ~10 GB of output
python3 -c "import gzip, sys; sys.stdout.buffer.write(gzip.compress(b'\x00' * 10_000_000_000))" \
> bomb.tar.gz
When node-tar 7.5.16 attempts to list or extract bomb.tar.gz, it enters the decompression loop and attempts to buffer 10 GB of null bytes. The Node.js heap grows until the process is killed by the OS OOM killer or the V8 heap limit is hit — either way, the service crashes.
Even in a CI/CD context (where the tar package is used by build tooling rather than a live server), a successful gzip bomb can hang a build job indefinitely, consuming runner minutes and blocking deployments.
The Fix
What Changed
The fix upgrades tar from 7.5.16 to 7.5.19 in both frontend/package.json and frontend/package-lock.json. Version 7.5.19 introduces a hard limit on the number of bytes that the gzip decompressor will emit before aborting with an error, preventing runaway memory consumption.
The package-lock.json diff also includes several "peer": true metadata corrections — these are lockfile housekeeping changes that npm made while resolving the updated dependency tree and do not affect runtime behavior:
- "peer": true,
"dependencies": {
"@ampproject/remapping": "^2.2.0",
These peer flag removals indicate that npm re-evaluated which packages are true peer dependencies vs. direct dependencies during the upgrade resolution. They are cosmetic from a security standpoint but confirm that the full dependency graph was re-resolved cleanly against 7.5.19.
Before vs. After
Before (vulnerable):
"node_modules/tar": {
"version": "7.5.16",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.16.tgz",
"integrity": "sha512-<old-hash>"
}
After (fixed):
"node_modules/tar": {
"version": "7.5.19",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.19.tgz",
"integrity": "sha512-<new-hash>"
}
Why This Specific Change Solves the Problem
node-tar 7.5.19 adds a decompressed byte counter inside the gzip extraction pipeline. Once the running total of decompressed bytes crosses a configurable (and safe default) threshold, the library throws an error and halts decompression. This means:
- A 1 KB gzip bomb that would expand to 100 GB is rejected early, after only a bounded number of bytes are written.
- Legitimate archives of normal size are completely unaffected — the limit is set far above any real-world archive size encountered in typical frontend tooling.
- The fix is backwards-compatible: no API changes, no configuration required, no code changes needed in the application layer.
Prevention & Best Practices
1. Pin and Audit Dependencies Regularly
Lockfiles like package-lock.json are your first line of defense. Keep them up to date and run npm audit (or a dedicated scanner like Trivy) in CI on every pull request:
# In your CI pipeline
npm audit --audit-level=critical
2. Enforce Decompression Limits in Your Own Code
If you write code that decompresses archives, never trust the decompressed size. Apply limits explicitly:
const MAX_DECOMPRESSED_BYTES = 500 * 1024 * 1024; // 500 MB
let totalBytes = 0;
gunzipStream.on('data', (chunk) => {
totalBytes += chunk.length;
if (totalBytes > MAX_DECOMPRESSED_BYTES) {
gunzipStream.destroy(new Error('Decompression limit exceeded'));
}
});
3. Validate Archive Sources
Never extract archives from untrusted sources without validation. If your application accepts user-uploaded archives:
- Check the compressed size before decompression (reject anything suspiciously small that claims to be large).
- Run extraction in an isolated process or container with memory limits enforced at the OS level (e.g.,
--memoryin Docker). - Use a timeout on extraction operations.
4. Use Automated Dependency Scanning
Tools that caught this vulnerability:
| Tool | How It Helps |
|---|---|
| Trivy | Scans package-lock.json against CVE databases, flagged this exact issue |
| npm audit | Built-in Node.js advisory check |
| Dependabot / Renovate | Automated PRs for dependency upgrades |
| Snyk | Deep dependency tree analysis with exploit context |
5. Security Standards Reference
- CWE-400: Uncontrolled Resource Consumption — the root cause classification for this vulnerability.
- OWASP A05:2021 – Security Misconfiguration: Outdated or unpatched dependencies fall under this category.
- OWASP Dependency-Check: A tool specifically designed to identify known vulnerable components.
Key Takeaways
node-tarversions before 7.5.19 have no decompression size limit — any code path that callstar.extract()ortar.list()on attacker-controlled input is vulnerable to memory exhaustion.- Compressed file size is not a reliable safety signal — a 1 KB
.tar.gzcan contain gigabytes of decompressed data; always limit decompressed output, not input. - The
frontend/package-lock.jsonlockfile is a security artifact, not just a reproducibility tool — stale versions in the lockfile can introduce critical vulnerabilities even whenpackage.jsonuses a permissive semver range. - Trivy's scan of
package-lock.jsoncaught this CVE before it reached production, demonstrating the value of scanning the full resolved dependency tree (not just direct dependencies). - The upgrade from 7.5.16 → 7.5.19 is a zero-risk change for valid workloads — the decompression limit in 7.5.19 only rejects pathologically oversized payloads that no legitimate archive would produce.
How Orbis AppSec Detected This
- Source: The tainted input enters via any
.tar.gzarchive processed by thenode-tarlibrary — in this project's context, this includes archives fetched during the frontend build process (npm package tarballs, build artifacts, uploaded assets). - Sink: The dangerous call site is
node-tar's internal gzip decompression pipeline (GunzipStreamhandler in node-tar ≤7.5.16), invoked whenevertar.extract(),tar.list(), ortar.parse()processes a compressed archive. - Missing control:
node-tar7.5.16 lacked a maximum decompressed byte limit. There was no ceiling on how many bytes the gzip stream could emit before the library accepted the data as valid archive content. - CWE: CWE-400 — Uncontrolled Resource Consumption. The library consumed unbounded memory proportional to the decompressed output of attacker-controlled input.
- Fix: The
tarpackage was upgraded from 7.5.16 to 7.5.19 infrontend/package-lock.jsonandfrontend/package.json, enabling the built-in decompression size guard introduced in 7.5.19.
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 Denial of Service vulnerabilities in compression libraries are critical infrastructure risks, not minor annoyances. A single malicious archive, routed through an unpatched node-tar, can bring down a Node.js process regardless of how well the rest of the application is hardened. The fix is as simple as a version bump — but finding that version bump before an attacker exploits it requires continuous, automated dependency scanning integrated into your development workflow.
Keep your lockfiles fresh, scan your full dependency tree (not just direct dependencies), and enforce resource limits whenever your code touches user-influenced compressed data.