Back to Blog
high SEVERITY7 min read

How Denial of Service via ZIP Parsing happens in Node.js and how to fix it

CVE-2026-39244 is a high-severity Denial of Service vulnerability in the adm-zip npm package (versions prior to 0.6.0) that allows an attacker to cause excessive memory allocation by supplying a specially crafted ZIP file. The fix upgrades adm-zip from 0.5.16 to 0.6.0 and pins the version via a package.json override to ensure no transitive dependency can silently pull in the vulnerable release. Left unpatched, any Node.js application that processes user-supplied ZIP archives with adm-zip is expo

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

Answer Summary

CVE-2026-39244 is a high-severity Denial of Service (DoS) vulnerability in the adm-zip npm library (CWE-400: Uncontrolled Resource Consumption). Versions up to and including 0.5.x fail to validate size fields embedded inside crafted ZIP entries before allocating memory, so an attacker can submit a malicious archive that forces the Node.js process to allocate gigabytes of RAM until it crashes or becomes unresponsive. The fix is to upgrade adm-zip to 0.6.0, which adds proper bounds-checking on those size fields, and to add a `"overrides"` entry in `package.json` so that transitive dependents cannot silently re-introduce the vulnerable version.

Vulnerability at a Glance

cweCWE-400 (Uncontrolled Resource Consumption)
fixUpgrade adm-zip to 0.6.0 and pin the version with a package.json override
riskAn attacker who can supply a ZIP file can crash or hang the Node.js process
languageJavaScript / Node.js
root causeadm-zip 0.5.x trusts ZIP entry size fields without bounds-checking before allocating buffers
vulnerabilityDenial of Service via crafted ZIP file (excessive memory allocation)

The Hidden Bomb in Your ZIP Parser

Every time a Node.js application calls adm-zip to open an archive uploaded by a user, it implicitly trusts the numbers embedded in that archive's headers. How many bytes should this entry be? The ZIP file says so. How much memory should be pre-allocated for decompression? The ZIP file says so. In adm-zip versions up to and including 0.5.x, that blind trust was never challenged — and CVE-2026-39244 is the result.

This post walks through exactly what went wrong, how the fix in version 0.6.0 closes the door, and what every Node.js developer handling user-supplied archives should do right now.


The Vulnerability Explained

What adm-zip Does (and Where It Goes Wrong)

adm-zip is one of the most widely used pure-JavaScript ZIP libraries on npm. It reads ZIP archives, parses their central directory and local file headers, and exposes entries for reading or extraction. The central directory of a ZIP file contains metadata for every entry: compressed size, uncompressed size, file name length, extra field length, and so on.

In adm-zip 0.5.x (the version locked in package-lock.json before this fix), those size fields were used directly to allocate buffers without adequate bounds validation. An attacker who controls the ZIP file can set an uncompressed-size field to, say, 0xFFFFFFFF (≈ 4 GB) while the actual compressed data is only a few hundred bytes. When adm-zip reads that field and calls something equivalent to:

// Simplified illustration of the vulnerable pattern in adm-zip 0.5.x
const buf = Buffer.alloc(entry.header.size); // size comes straight from the ZIP header

…the Node.js process immediately attempts to allocate that many bytes on the heap. On a server with 2 GB of RAM, a single such request can exhaust memory and either crash the process with an out-of-memory error or trigger aggressive garbage collection that renders the service unresponsive.

The package-lock.json before the fix recorded:

"node_modules/adm-zip": {
  "version": "0.5.16",
  "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz",
  "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==",
  "engines": {
    "node": ">=12.0"
  }
}

Attack Scenario

Consider a document-processing service that accepts ZIP uploads and uses adm-zip to enumerate the contents before handing files off to a downstream processor:

const AdmZip = require('adm-zip');

app.post('/upload', upload.single('archive'), (req, res) => {
  const zip = new AdmZip(req.file.buffer); // <-- attacker controls this buffer
  const entries = zip.getEntries();
  entries.forEach(entry => {
    const data = entry.getData(); // triggers the dangerous allocation
    // ... process data
  });
  res.json({ count: entries.length });
});

An attacker crafts a ZIP file with a single entry whose uncompressed-size header field is set to several gigabytes. The file itself is tiny — easily under any upload-size limit — but entry.getData() internally calls into adm-zip's buffer allocation logic with the attacker-supplied size. The result: the Node.js event loop freezes or the process dies, taking every concurrent user's session with it.

Because this requires only an HTTP POST with a small file, it is trivially automatable and requires no authentication if the upload endpoint is public.


The Fix

Two-File Change, One Clear Goal

The fix touches exactly two files: package-lock.json (to update the resolved version and integrity hash) and package.json (to add an overrides block that prevents transitive dependencies from pulling in any older version).

package-lock.json — Version and Integrity Update

 "node_modules/adm-zip": {
-  "version": "0.5.16",
-  "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz",
-  "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==",
+  "version": "0.6.0",
+  "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.6.0.tgz",
+  "integrity": "sha512-XleryMhbuksdKtofnWZ9Sk+4CUTbms4Mb/EU32SZwToAyZ5RgVos/ki8n+yr0LWHOGKuakbXTuuYNHLQjhddgg==",
   "license": "MIT",
   "engines": {
-    "node": ">=12.0"
+    "node": ">=14.0"
   }
 }

adm-zip 0.6.0 introduces proper validation of size fields before any buffer allocation takes place. The library now checks that the declared uncompressed size is consistent with the actual compressed payload and rejects entries whose headers claim unreasonable sizes. This directly eliminates the attack vector described above.

The node engine bump from >=12.0 to >=14.0 is also meaningful: Node.js 14 introduced improved Buffer APIs and memory-safety improvements that the new adm-zip release takes advantage of.

package.json — The overrides Block

+  "overrides": {
+    "adm-zip": "0.6.0"
+  }

This is the second, equally important part of the fix. Without it, a transitive dependency — some other package in the tree that lists adm-zip as its own dependency — could still resolve to 0.5.x. The npm overrides field (introduced in npm 8.3) forces every consumer in the dependency tree to use exactly 0.6.0, regardless of what version range they specify. This closes the gap between "we upgraded our direct dependency" and "we actually run the patched code everywhere."

Why Both Changes Are Necessary

Change What it protects
package-lock.json version bump Ensures npm ci installs the patched library for direct usage
package.json overrides block Ensures no transitive dependency can silently re-introduce 0.5.x

Prevention & Best Practices

1. Never Trust Archive Metadata

When parsing any container format (ZIP, TAR, JAR, DOCX), treat all size and count fields as untrusted input. Validate them against configurable maximums before allocating memory or opening file handles:

const MAX_UNCOMPRESSED_SIZE = 512 * 1024 * 1024; // 512 MB

zip.getEntries().forEach(entry => {
  if (entry.header.size > MAX_UNCOMPRESSED_SIZE) {
    throw new Error(`Entry ${entry.entryName} exceeds size limit`);
  }
  const data = entry.getData();
});

2. Pin Transitive Dependencies with overrides

The npm overrides field (or Yarn's resolutions) is an underused but powerful tool. Any time a security scanner flags a transitive dependency, add an override so future npm install runs cannot regress:

"overrides": {
  "adm-zip": "0.6.0"
}

3. Integrate Dependency Scanning in CI

Tools like Trivy, npm audit, and Snyk can flag vulnerable dependency versions before they reach production. Trivy rule CVE-2026-39244 is exactly what caught this issue. Add a step like the following to your CI pipeline:

- name: Scan dependencies
  run: trivy fs --exit-code 1 --severity HIGH,CRITICAL .

4. Set Upload Size and Entry Count Limits

Even with a patched library, apply defense-in-depth at the application layer:

  • Reject archives larger than a reasonable threshold (e.g., 50 MB).
  • Limit the number of entries processed (e.g., 1,000 max).
  • Consider processing archives in a sandboxed worker or container with a memory cap.

5. Monitor for Relevant CWEs

This vulnerability maps to CWE-400: Uncontrolled Resource Consumption and CWE-789: Memory Allocation with Excessive Size Value. Review your codebase for any pattern where a size or length value derived from external input is passed directly to Buffer.alloc(), new Array(), or similar allocation calls.

OWASP Reference: OWASP A06:2021 – Vulnerable and Outdated Components


Key Takeaways

  • adm-zip 0.5.x trusts ZIP header size fields without bounds-checking — a single malicious archive can exhaust all available Node.js heap memory.
  • Upgrading to 0.6.0 is not optional if you process user-supplied ZIPs — the vulnerability is trivially exploitable with a tiny, specially crafted file.
  • The overrides block in package.json is as important as the version bump — without it, transitive dependencies can silently re-introduce the vulnerable 0.5.x release.
  • The Node.js engine requirement changed from >=12.0 to >=14.0 — verify your runtime version before deploying the patched library.
  • Trivy's static dependency scan caught this before runtime — integrating scanner rules like CVE-2026-39244 into CI gives you a safety net that code review alone cannot provide.

How Orbis AppSec Detected This

  • Source: A user-supplied ZIP archive passed to new AdmZip(buffer) or new AdmZip(filePath) in any route or service that accepts file uploads.
  • Sink: adm-zip's internal buffer allocation logic, which calls Buffer.alloc(entry.header.size) (or equivalent) using the unvalidated size field from the ZIP central directory — resolved via the node_modules/adm-zip entry in package-lock.json.
  • Missing control: No upper-bound validation on the declared uncompressed size before memory allocation; no rejection of entries with implausible size ratios.
  • CWE: CWE-400 — Uncontrolled Resource Consumption (also related: CWE-789 — Memory Allocation with Excessive Size Value).
  • Fix: Upgraded adm-zip from 0.5.16 to 0.6.0 in package-lock.json and added an overrides entry in package.json to enforce the patched version across the entire dependency tree.

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-39244 is a sharp reminder that ZIP files are not passive data containers — they are structured documents whose metadata fields are fully under attacker control. adm-zip 0.5.x placed unconditional trust in those fields, and the consequence is a straightforward path to memory exhaustion and service outage. The fix is surgical: two files changed, one version bumped, one overrides block added. But the lesson extends well beyond adm-zip — any code that allocates memory based on a size field from an external source must validate that field before acting on it.

Keep your dependencies current, scan them automatically, and never let an archive tell your application how much memory to use.


References

Frequently Asked Questions

What is a Denial of Service via crafted ZIP file?

It is an attack where a malicious ZIP archive contains manipulated size or count fields that trick the parser into allocating far more memory than the actual data warrants, exhausting available RAM and crashing the process.

How do you prevent ZIP-based DoS in Node.js?

Use a well-maintained ZIP library that validates entry size fields before allocation, enforce maximum file-size and entry-count limits, and keep dependencies pinned to patched versions via package.json overrides.

What CWE is this Denial of Service vulnerability?

CWE-400 — Uncontrolled Resource Consumption, which covers cases where software does not limit the resources it allocates in response to attacker-controlled input.

Is rate-limiting enough to prevent this DoS vulnerability?

Rate-limiting reduces the frequency of attacks but does not prevent a single malicious ZIP from exhausting memory; the root fix must be in the parsing library itself, as done in adm-zip 0.6.0.

Can static analysis detect this vulnerability?

Yes — dependency scanners like Trivy and npm audit can flag known-vulnerable versions of adm-zip; Trivy rule CVE-2026-39244 is exactly what detected this issue in the project's package-lock.json.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #626

Related Articles

high

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

A high-severity misconfiguration in `.github/dependabot.yml` left this Node.js library without a cooldown period, meaning Dependabot would immediately propose updates to newly published packages — including potentially malicious or unstable ones. The fix adds a `cooldown` block with `default-days: 7` to both the `npm` and `github-actions` package ecosystem entries, introducing a mandatory 7-day waiting period before any new package version is surfaced as an update candidate.

critical

How CSRF Protection Failures Happen in FastAPI and How to Fix Them

A critical CORS misconfiguration in `backend/main.py` allowed cookies to be sent alongside wildcard-origin requests, violating the CORS specification and opening the door to cross-site request forgery attacks. The fix conditionally disables `allow_credentials` when the allowed origins list contains a wildcard, bringing the configuration into compliance with browser security rules. This change closes a subtle but dangerous gap that could have let attackers on sibling subdomains forge authenticate

critical

How Missing Rate Limiting Happens in Node.js SSE Handlers and How to Fix It

A critical missing rate-limiting control in `src/sse/handlers/chat.js` allowed any caller to flood the SSE chat endpoint with unlimited requests, risking server resource exhaustion, denial of service, and runaway AI provider API costs. The fix introduces a per-IP sliding-window rate limiter that caps requests at 60 per minute and returns HTTP 429 on violations. Because the endpoint was publicly reachable and only validated API keys — not request frequency — exploitation required nothing more tha

medium

How Denial of Service via Catastrophic Backtracking happens in Node.js and how to fix it

CVE-2026-4867 is a Denial of Service vulnerability in path-to-regexp 0.1.12 where malformed URL parameters can trigger catastrophic backtracking in the library's regular expression engine, allowing an attacker to hang or crash a Node.js application with a single crafted request. The fix upgrades path-to-regexp to version 0.1.13, which patches the vulnerable regex patterns. This change was applied via a package-level override to ensure the patched version is used throughout the entire dependency

high

How Denial of Service via Exponential-Time Complexity happens in Node.js and how to fix it

CVE-2026-13149 is a high-severity Denial of Service vulnerability in the `brace-expansion` npm package, where crafted input strings trigger exponential-time processing that can freeze or crash a Node.js application. The fix upgrades `brace-expansion` from `2.0.2` to `2.1.4` and `minimatch` from `5.1.6` to `5.1.9`, along with npm `overrides` to ensure the patched versions are used throughout the entire dependency tree.

critical

How Unrestricted File Upload happens in Node.js/Express and how to fix it

A critical unrestricted file upload vulnerability was discovered in `mainsystem/routes/admin/profile.js`, where the avatar upload endpoint accepted any file type without validation. An authenticated attacker could upload a malicious server-side script to a web-accessible directory and execute arbitrary code on the server. The fix adds MIME type filtering, an allowlist of safe image formats, and a 2 MB file size limit to the multer middleware.