Back to Blog
high SEVERITY9 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 before 0.6.0) that allows an attacker to cause excessive memory allocation by supplying a specially crafted ZIP file. The vulnerability was present in the `dsh-mneme` component of the project and was remediated by upgrading `adm-zip` from `0.5.18` to `0.6.0`. Left unpatched, this flaw could allow any user capable of uploading or supplying ZIP input to crash or severely degrade the Node.js ser

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

Answer Summary

CVE-2026-39244 is a Denial of Service vulnerability (CWE-400: Uncontrolled Resource Consumption) in the `adm-zip` npm package versions prior to 0.6.0. An attacker can supply a crafted ZIP archive that causes the library to allocate excessive memory during parsing, potentially crashing or hanging the Node.js process. The fix is to upgrade `adm-zip` to version 0.6.0 or later, which adds proper validation of ZIP metadata fields before allocating memory buffers. In this project, the dependency was updated in `dsh-mneme/package-lock.json` and `dsh-mneme/package.json`.

Vulnerability at a Glance

cweCWE-400 (Uncontrolled Resource Consumption)
fixUpgrade adm-zip from 0.5.18 to 0.6.0 in dsh-mneme/package-lock.json and package.json
riskAttacker-supplied ZIP file crashes or hangs the Node.js process
languageJavaScript / Node.js
root causeadm-zip <0.6.0 trusts ZIP metadata size fields without bounds validation before allocating memory
vulnerabilityDenial of Service via crafted ZIP file (excessive memory allocation)

How Denial of Service via ZIP Parsing Happens in Node.js and How to Fix It


Vulnerability at a Glance

Field Detail
CVE CVE-2026-39244
Severity High
Package adm-zip < 0.6.0
CWE CWE-400 – Uncontrolled Resource Consumption
Language JavaScript / Node.js
Fixed in adm-zip 0.6.0

Introduction

The dsh-mneme component of this project handles ZIP file processing through the popular adm-zip npm package. A high-severity vulnerability — CVE-2026-39244 — was discovered in adm-zip versions prior to 0.6.0 that allows any party capable of supplying a ZIP file to the application to trigger uncontrolled memory allocation, potentially crashing the Node.js process entirely.

This is not a subtle logic flaw buried deep in application code. It lives in a widely-used third-party library that is a transitive or direct dependency recorded in dsh-mneme/package-lock.json. The fix is a one-version upgrade, but understanding why this class of vulnerability is dangerous — and how ZIP files can be weaponized — is essential knowledge for any Node.js developer working with file uploads or archive processing.


The Vulnerability Explained

How ZIP Parsing Works (and Where It Can Go Wrong)

A ZIP archive is not just a compressed blob. It contains a structured Central Directory at the end of the file, which is a table of metadata entries describing each file inside the archive: its name, compressed size, uncompressed size, offset in the file, and more. A ZIP parser typically reads this directory first to understand what's inside before decompressing anything.

The critical trust boundary is here: the parser must not blindly trust the size fields in the Central Directory. Those fields are written by whoever created the ZIP file. An attacker can craft a ZIP archive where a metadata field claims an entry's uncompressed size is, say, 4 gigabytes — even though the actual compressed data is only a few bytes. If the parser allocates a buffer of that claimed size before reading the compressed data, memory is exhausted instantly.

The Vulnerable Pattern in adm-zip < 0.6.0

In versions of adm-zip prior to 0.6.0, the library's entry-reading logic did not adequately validate the size values declared in ZIP Central Directory headers against the actual file size or configurable limits before allocating output buffers. The vulnerable behavior looks conceptually like this:

// Simplified illustration of the vulnerable pattern in adm-zip < 0.6.0
function readEntry(entry) {
  const uncompressedSize = entry.header.size; // Attacker-controlled value
  const buffer = Buffer.alloc(uncompressedSize); // Allocated WITHOUT bounds check
  // ... decompress into buffer
}

The entry.header.size field comes directly from the ZIP file's metadata. In adm-zip 0.5.18 (the version pinned in dsh-mneme/package-lock.json before this fix), that value was used to allocate a Buffer without first verifying it was reasonable relative to the actual compressed data or any system limit.

Attack Scenario

Consider a dsh-mneme service endpoint that accepts ZIP file uploads — for example, a batch data import feature. An attacker constructs a ZIP file that is only a few kilobytes on disk but declares an uncompressed entry size of 2 GB in its Central Directory. When the Node.js process calls adm-zip to read the archive:

  1. adm-zip reads the Central Directory and encounters the 2 GB size claim.
  2. It calls Buffer.alloc(2_000_000_000) (or equivalent) to prepare the output buffer.
  3. The Node.js process attempts to allocate 2 GB of RAM.
  4. The process either crashes with an out-of-memory error or the host system begins swapping, degrading all other services.
  5. The attacker repeats this with a handful of concurrent requests to guarantee a full outage.

This attack requires no authentication if the upload endpoint is public, and only minimal authentication bypass if it is protected — making it a realistic threat for any web service that processes user-supplied ZIP files.

Real-World Impact for dsh-mneme

The dsh-mneme component's package-lock.json locked adm-zip at version 0.5.18. Any code path in dsh-mneme that calls adm-zip APIs such as readFile(), getEntries(), or extractAllTo() on untrusted input was potentially reachable by this attack. While the PR assessment notes the path was "not confirmed reachable," the presence of a vulnerable version in the dependency tree is sufficient risk to warrant immediate remediation — especially for a High-severity CVE.


The Fix

What Changed

The fix upgrades adm-zip from 0.5.18 to 0.6.0 in two files:

  • dsh-mneme/package.json — updates the declared version range
  • dsh-mneme/package-lock.json — pins the resolved version and updates the integrity hash

The package-lock.json diff also removes a series of "libc" constraint fields from optional platform-specific binary dependencies (entries for architectures like arm, arm64, ppc64, riscv64, s390x, x64 under both glibc and musl variants). This is a metadata cleanup that accompanied the version bump in the lock file regeneration — these "libc" fields were removed because the updated lock file format no longer requires them for platform resolution.

Before and After

Before (dsh-mneme/package-lock.json — adm-zip 0.5.18):

"adm-zip": {
  "version": "0.5.18",
  "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.18.tgz",
  ...
}

After (dsh-mneme/package-lock.json — adm-zip 0.6.0):

"adm-zip": {
  "version": "0.6.0",
  "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.6.0.tgz",
  ...
}

How Version 0.6.0 Fixes the Problem

adm-zip 0.6.0 introduces validation of ZIP entry metadata fields before memory allocation. Specifically, it checks that declared entry sizes are consistent with the actual archive file size and enforces limits that prevent absurdly large buffer allocations from crafted headers. This means a ZIP file claiming a 2 GB uncompressed entry will be rejected early in parsing — before any large allocation occurs — rather than causing the process to run out of memory.

The fix is entirely backward-compatible for legitimate ZIP files: valid archives with accurate metadata are processed identically. Only malformed or malicious archives with inflated size fields are now rejected.

The libc Field Removals

The diff shows removal of "libc": ["glibc"] and "libc": ["musl"] entries from multiple optional platform binary entries, for example:

-      "libc": [
-        "glibc"
-      ],
       "license": "LGPL-3.0-or-later",
       "optional": true,

This is a lock file normalization change introduced when regenerating package-lock.json with npm's updated resolution algorithm. These fields were used in some npm versions to further constrain which optional binary to install based on the system's C library. Their removal does not affect security — it reflects a cleaner lock file format that relies on the os and cpu fields alone for platform selection.


Key Takeaways

  • adm-zip versions before 0.6.0 trust ZIP Central Directory size fields without bounds checking — a single crafted upload can exhaust Node.js heap memory.
  • The dsh-mneme/package-lock.json lock file pinned the vulnerable 0.5.18 version, which is exactly why lock file scanning with tools like Trivy is essential — it catches the precise version in use.
  • Upgrading to adm-zip 0.6.0 is the complete fix — no application code changes are needed; the library itself now validates metadata before allocation.
  • ZIP-based DoS attacks require no decompression — the damage happens at the header-reading stage, making even "read-only" archive inspection code vulnerable.
  • Defense in depth matters: combine library patching with application-layer size limits and container memory caps to minimize the impact of any future similar vulnerabilities.

How Orbis AppSec Detected This

  • Source: A ZIP file supplied via user-controlled input to any dsh-mneme code path that invokes adm-zip APIs (e.g., new AdmZip(userSuppliedBuffer) or readFile(userPath)).
  • Sink: adm-zip's internal buffer allocation during ZIP Central Directory parsing — specifically, the Buffer.alloc() call sized from an unvalidated entry.header.size field in adm-zip 0.5.18.
  • Missing control: No bounds validation on ZIP entry size metadata fields before memory allocation; no maximum allocation limit enforced by the library.
  • CWE: CWE-400 – Uncontrolled Resource Consumption.
  • Fix: Upgraded adm-zip from 0.5.18 to 0.6.0 in dsh-mneme/package-lock.json and dsh-mneme/package.json, which introduces metadata validation before buffer allocation.

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 the attack surface of a Node.js application extends well beyond the code your team writes. A single unpatched npm dependency — in this case adm-zip 0.5.18 in dsh-mneme — can expose your entire service to a high-severity Denial of Service attack that requires nothing more than a malformed ZIP file. The fix is straightforward: upgrade to adm-zip 0.6.0. But the broader lesson is to treat your package-lock.json as a security artifact, scan it continuously, and apply patches promptly when vulnerabilities in dependencies are disclosed.


Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #5

Related Articles

critical

deleteNestedProperty Prototype Pollution via Dot-Notation Path

The `deleteNestedProperty` function in propertyUtils.ts allowed attackers to manipulate JavaScript object prototypes by passing specially crafted dot-notation paths like `__proto__.polluted`. A fix now blocks dangerous keys before processing, preventing prototype pollution attacks that could affect all objects in the application.

high

How Denial of Service via Infinite Loop Happens in JavaScript Dependencies and How to Fix It

CVE-2026-67213 is a high-severity denial of service vulnerability in nanoid before version 5.1.6 that triggers an infinite loop during random ID generation when processing specially crafted input. We upgraded nanoid across the entire dependency tree to patch this flaw and prevent attackers from freezing application threads. This fix ensures that ID generation remains resilient even when handling adversarial input patterns.

high

How Sensitive Data Exposure happens in Zotero plugins and how to fix it

A high-severity data exposure vulnerability in `Zotero.ts` automatically transmitted complete document metadata—including private notes, attachment paths, and tags—to external LLM services without user consent. The fix replaces broad `item.toJSON()` serialization with explicit field selection, sending only essential bibliographic data.

high

How missing dependency update cooldowns happen in GitHub Dependabot configurations and how to fix it

A semgrep scan flagged `.github/dependabot.yml` for lacking a cooldown period, meaning Dependabot would immediately propose updates to brand-new package versions across npm, Bundler, and Docker ecosystems. The fix adds a `cooldown: default-days: 7` block to every `package-ecosystem` entry, forcing a one-week waiting period before newly published releases are considered — reducing exposure to malicious or unstable package drops.

high

How Path Traversal Happens in TensorFlow's Data Service and How to Fix It

TensorFlow's data service dispatcher validated dataset IDs against forward-slash traversal attacks but overlooked backslash characters on non-Windows platforms, allowing attackers to escape the root directory. A targeted fix adds explicit backslash validation across all platforms, closing a high-severity path traversal vulnerability in the snapshot management system.

critical

How Unbounded WebSocket Message Handling Causes Resource Exhaustion in Node.js and How to Fix It

The WebSocketCrossServerAdapter class in a popular Node.js WebSocket library lacked any rate limiting on inbound messages, allowing attackers to flood Redis nodes and WebSocket servers with high-volume traffic. The fix introduces a configurable `rateLimit` option that caps messages per connection per second, preventing resource exhaustion while preserving legitimate functionality.