How Denial of Service via gzip bomb happens in Node.js tar and how to fix it
The Vulnerability at a Glance
| Field | Detail |
|---|---|
| Vulnerability | Denial of Service via crafted gzip bomb |
| CVE | CVE-2026-59873 |
| CWE | CWE-400 — Uncontrolled Resource Consumption |
| Package | node-tar (npm) |
| Affected version | 7.5.16 (and earlier in the 7.x line) |
| Fixed version | 7.5.19 |
| Severity | Critical |
Introduction
The package-lock.json file in this repository pinned node-tar at version 7.5.16 — a version that contains a critical Denial of Service flaw catalogued as CVE-2026-59873. The Trivy scanner flagged the exact integrity hash for that version:
"integrity": "sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w=="
Because tar is listed under dependencies (not devDependencies) in package.json, this library ships in the production bundle. Any Node.js process that calls into node-tar to extract an archive — whether that's unpacking a plugin, processing a user upload, or handling a build artifact — is exposed to this attack while the vulnerable version is installed.
The Vulnerability Explained
What is a gzip bomb?
A gzip bomb (sometimes called a "zip of death") is a compressed archive that is tiny on disk but expands to an enormous amount of data when decompressed. The classic example is a file that is a few kilobytes compressed but decompresses to gigabytes of repeated bytes. The attack exploits the mathematical properties of the DEFLATE compression algorithm, which can represent long runs of identical bytes with extreme efficiency.
How CVE-2026-59873 works in node-tar
node-tar uses Node.js's built-in zlib module to decompress gzip streams before parsing the TAR format. In versions up to and including 7.5.16, the decompression pipeline does not enforce an upper bound on how many bytes can be emitted from the decompressor. The relevant code path looks roughly like this (simplified pseudocode representing the vulnerable pattern):
// Vulnerable pattern in node-tar ≤ 7.5.16 (conceptual illustration)
const gunzip = zlib.createGunzip();
inputStream.pipe(gunzip).pipe(tarParser);
// No byte-count check on gunzip output — an attacker controls
// how many bytes flow into tarParser
Because there is no decompression-size guard between the zlib.createGunzip() output and the TAR parser, an attacker who can supply a crafted .tar.gz file can cause the Node.js process to:
- Allocate large amounts of heap memory as the decompressed data streams in.
- Peg the CPU at 100% as the event loop tries to process the flood of decompressed chunks.
- Ultimately crash with an out-of-memory error or become completely unresponsive.
Concrete attack scenario
Suppose this application allows users to upload a .tar.gz plugin bundle, and the server calls node-tar to extract it:
const tar = require('tar');
app.post('/upload-plugin', async (req, res) => {
// req.file.path is attacker-controlled
await tar.extract({ file: req.file.path, cwd: './plugins' });
res.send('Plugin installed');
});
With node-tar 7.5.16, an attacker uploads a 50 KB gzip bomb that decompresses to 10 GB. The tar.extract() call happily starts streaming decompressed data with no size limit. The Node.js process runs out of memory and crashes — taking down the entire application for every other user. Because the crash is triggered by a single HTTP request, this is trivially repeatable for sustained downtime.
Real-world impact
- Availability: The Node.js process crashes or becomes unresponsive, causing a full service outage.
- No authentication required: If any unauthenticated endpoint processes tar archives, the attack requires zero credentials.
- Amplification ratio: A well-crafted gzip bomb can achieve compression ratios exceeding 1,000:1, meaning a 1 MB upload can exhaust gigabytes of server RAM.
The Fix
What changed
The fix is a targeted version bump in two files:
package.json — tar is explicitly pinned to a safe minimum:
- "oxc-parser": "^0.140.0",
+ "oxc-parser": "^0.140.0",
+ "tar": "^7.5.19",
"ws": "^8.19.0"
package-lock.json — the resolved version and integrity hash are updated to the patched release:
"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==",
+ "version": "7.5.19",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.19.tgz",
+ "integrity": "sha512-4LeEWl96twnS2Q7Bz4MGqgazLqO+hJN63GZxXoIqh1T3VweYD997gbU1ItNsQafqqXTXd5WFyFdReLtwvRBNiw==",
Why both files must change
package.json: Adding"tar": "^7.5.19"as an explicit top-level dependency ensures that even if another package in the dependency tree requests an older version oftar, npm's deduplication logic will resolve to 7.5.19 or newer. Without this entry, a transitive dependency could silently pull in the vulnerable 7.5.16.package-lock.json: The lock file records the exact resolved URL and SHA-512 integrity hash. Updating the integrity hash to the one for 7.5.19 ensures thatnpm ci(used in CI/CD pipelines) installs the verified, patched binary and rejects any tampered or downgraded package.
What node-tar 7.5.19 actually fixes
Version 7.5.19 introduces decompression size accounting in the gzip pipeline. The patched version tracks the number of bytes emitted by the decompressor and aborts extraction if that count exceeds a configurable (and safe default) threshold. This means even a perfectly crafted gzip bomb is stopped before it can exhaust system resources.
Prevention & Best Practices
1. Keep dependencies locked and audited
Always commit your package-lock.json and run npm audit as part of your CI pipeline. A single npm audit --audit-level=critical step would have caught CVE-2026-59873 before it reached production.
# Add to your CI pipeline
npm audit --audit-level=critical
2. Use a software composition analysis (SCA) scanner
Tools like Trivy, Snyk, and GitHub Dependabot continuously monitor your lock files against known CVE databases. This vulnerability was caught by Trivy's CVE-2026-59873 rule scanning package-lock.json.
# Example: run Trivy against your repo
trivy fs --scanners vuln .
3. Enforce decompression limits in your own code
Even with a patched library, apply defense-in-depth by limiting the size of archives you accept before decompression:
const MAX_ARCHIVE_BYTES = 100 * 1024 * 1024; // 100 MB
app.post('/upload-plugin', upload.single('archive'), async (req, res) => {
if (req.file.size > MAX_ARCHIVE_BYTES) {
return res.status(413).send('Archive too large');
}
await tar.extract({ file: req.file.path, cwd: './plugins' });
res.send('Plugin installed');
});
Note: checking the compressed size does not prevent a gzip bomb — you need library-level decompression limits (provided by node-tar ≥7.5.19) in addition to upload size checks.
4. Pin transitive dependencies explicitly
If a library you depend on uses tar internally, that transitive dependency may resolve to a vulnerable version unless you override it explicitly in package.json (as this fix does). Use npm ls tar to audit which version is actually installed:
npm ls tar
# Should show: tar@7.5.19
5. Security standards reference
- CWE-400: Uncontrolled Resource Consumption
- OWASP: Denial of Service Cheat Sheet
- OWASP A06:2021: Vulnerable and Outdated Components
Key Takeaways
node-tar7.5.16 has no decompression size limit — a single crafted.tar.gzfile can crash a Node.js process regardless of how the rest of the application is hardened.- Checking the compressed file size is not enough — gzip bombs are small when compressed; protection must happen at the decompression layer, which is what 7.5.19 provides.
- Both
package.jsonandpackage-lock.jsonmust be updated — updating only the lock file leaves the door open for transitive resolution to pull in the vulnerable version on the nextnpm install. - Production
dependenciesvs.devDependenciesmatters — becausetarwas independencies, this vulnerability was live in the deployed application, not just in the developer's build environment. - SCA scanners catch what code review misses — no human reviewer inspecting application logic would notice a version number buried in
package-lock.json; automated scanning is essential for dependency security.
How Orbis AppSec Detected This
- Source: The
package-lock.jsonfile, which records the exact resolved version (7.5.16) and integrity hash of thenode-tarpackage installed in the production dependency tree. - Sink: Any call to
tar.extract(),tar.list(), or relatednode-tarAPIs that processes a gzip-compressed archive — the decompression pipeline in node-tar 7.5.16 has no byte-count ceiling. - Missing control: No decompression size limit in the
zlibpipeline inside node-tar 7.5.16; the library streams decompressed bytes to the TAR parser without checking how many bytes have been emitted. - CWE: CWE-400 — Uncontrolled Resource Consumption.
- Fix: The
tarpackage was upgraded from 7.5.16 to 7.5.19 in bothpackage.jsonandpackage-lock.json, replacing the vulnerable integrity hash with the verified hash of the patched release.
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 dependency libraries are just as dangerous as logic flaws in your own code. A single outdated entry in package-lock.json — "version": "7.5.16" — was enough to expose every archive-processing code path in this application to a resource-exhaustion attack requiring no authentication and no special knowledge of the application's internals.
The fix is surgical: two files changed, two version strings and one integrity hash updated. But the protection it provides is significant — node-tar 7.5.19 closes the decompression-size loophole that made the gzip bomb attack possible. Pair that with automated SCA scanning in your CI pipeline, explicit top-level dependency pinning, and upload-size guards in your own code, and you have a robust, layered defense against this entire class of attack.
Keep your lock files up to date. Scan them automatically. And never trust compressed input.