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 adm-zip versions prior to 0.6.0, where a specially crafted ZIP file can trigger excessive memory allocation and crash a Node.js application. The vulnerability was present in the deepseek-ivideo plugin's dependency tree and was fixed by pinning adm-zip to version 0.6.0 in both `package.json` and `pnpm-lock.yaml`. Because adm-zip processes ZIP archives that can originate from user-supplied or external sources, this flaw represent

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

Answer Summary

CVE-2026-39244 is a Denial of Service (DoS) vulnerability (CWE-400: Uncontrolled Resource Consumption) in the adm-zip npm package (versions < 0.6.0). A remote attacker can supply a maliciously crafted ZIP file that causes adm-zip to allocate an unbounded amount of memory, exhausting the Node.js process heap and crashing the application. The fix is to upgrade adm-zip to exactly 0.6.0, which adds guards against oversized or malformed ZIP metadata before memory is allocated. In the deepseek-ivideo plugin, this was accomplished by changing the dependency specifier from `^0.5.16` to `0.6.0` and adding a pnpm `overrides` entry to ensure no transitive dependency can pull in the vulnerable version.

Vulnerability at a Glance

cweCWE-400 (Uncontrolled Resource Consumption)
fixPin adm-zip to 0.6.0 and add a pnpm lockfile override to block the vulnerable version across the dependency tree
riskAn attacker who can supply a crafted ZIP file can exhaust process memory and crash the application
languageJavaScript / Node.js
root causeadm-zip < 0.6.0 trusts ZIP header metadata fields (e.g., uncompressed size) without bounding allocation
vulnerabilityDenial of Service via uncontrolled memory allocation during ZIP parsing

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

The Vulnerability at a Glance

Field Detail
CVE CVE-2026-39244
Severity High
CWE CWE-400 — Uncontrolled Resource Consumption
Package adm-zip < 0.6.0
Affected file source/plugins/deepseek-ivideo/pnpm-lock.yaml
Fix Upgrade adm-zip to 0.6.0

Introduction

The source/plugins/deepseek-ivideo plugin is responsible for handling video-related functionality in a DeepSeek-powered application stack. Buried inside its dependency tree was a ticking clock: adm-zip ^0.5.16, a version range that satisfies itself with 0.5.18 — a release carrying CVE-2026-39244, a high-severity Denial of Service flaw.

The problem is not in the plugin's own code. It is in how adm-zip processes ZIP archives. When adm-zip reads a ZIP file, it trusts the metadata fields embedded in the archive's central directory — fields like the uncompressed size of each entry. In versions before 0.6.0, the library allocates a buffer sized to match that field before verifying whether the actual compressed data supports it. An attacker who can feed a crafted ZIP to the application can declare an uncompressed size of, say, 4 GB for a 1 KB payload, causing Node.js to attempt a 4 GB heap allocation — and crash.

For developers building plugins that download, extract, or process ZIP archives (browser bundles, font packages, video assets), this is a very real attack surface.


The Vulnerability Explained

What adm-zip Does and Where It Goes Wrong

adm-zip is a pure-JavaScript library for reading and writing ZIP archives without native bindings. It is popular precisely because it requires no compilation step — ideal for cross-platform plugins like deepseek-ivideo.

When adm-zip opens a ZIP file, it reads the End of Central Directory (EOCD) record and then iterates over each file entry in the Central Directory. Each entry contains two size fields:

  • compressedSize — how many bytes are stored on disk
  • uncompressedSize — how many bytes the entry expands to after decompression

In adm-zip < 0.6.0, the library uses uncompressedSize to pre-allocate the output buffer:

// Simplified representation of the vulnerable pattern in adm-zip 0.5.x
var data = Buffer.alloc(entry.header.size); // 'size' comes directly from ZIP metadata
// ... then decompress into 'data'

There is no upper-bound check on entry.header.size before Buffer.alloc() is called. A crafted ZIP can set this field to 0xFFFFFFFF (4,294,967,295 bytes — ~4 GB). Node.js will attempt to honor that allocation. On most production servers, this either:

  1. Triggers an out-of-memory crash (FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed)
  2. Causes severe memory pressure that degrades all other requests served by the same process

The Vulnerable Dependency Declaration

In package.json, the dependency was declared with a caret range:

// Before — package.json (vulnerable)
"adm-zip": "^0.5.16"

The caret (^) allows npm/pnpm to resolve any 0.5.x version, which means 0.5.18 — the version that carries CVE-2026-39244 — satisfies this range. The lockfile then freezes 0.5.18 as the resolved version:

# Before — pnpm-lock.yaml (vulnerable)
adm-zip:
  specifier: ^0.5.16
  version: 0.5.18

Attack Scenario Specific to deepseek-ivideo

The deepseek-ivideo plugin uses @puppeteer/browsers and other tooling that may download browser binaries or assets packaged as ZIP archives. If any code path passes a remotely fetched or user-supplied ZIP to adm-zip for extraction, an attacker controlling that ZIP source can:

  1. Craft a ZIP where one or more entries declare an uncompressedSize of several gigabytes.
  2. Deliver this file to the plugin's extraction routine.
  3. Trigger a single Buffer.alloc() call that exhausts the Node.js heap.
  4. Crash the entire plugin process — and potentially the host application alongside it.

Because this is a pre-decompression allocation (the crash happens before any bytes are actually decompressed), even stream-based or chunked reading does not help. The damage is done the moment the size field is read.


The Fix

What Changed and Why Each Change Was Necessary

The fix touches three files. Here is what each change does and why it matters.

1. package.json — Pin the version exactly

// Before
"adm-zip": "^0.5.16"

// After
"adm-zip": "0.6.0"

The caret range ^0.5.16 was replaced with an exact pin 0.6.0. This is intentional and important: it prevents any future pnpm install or CI run from accidentally resolving a different (potentially still-vulnerable) patch release. adm-zip 0.6.0 introduced validation that checks uncompressedSize against the actual compressed data size and enforces reasonable limits before any allocation occurs.

2. pnpm-lock.yaml — Add a global override

# After — pnpm-lock.yaml
overrides:
  adm-zip: 0.6.0

This is the most security-critical change. The overrides block in pnpm forces all resolutions of adm-zip — whether direct or transitive — to 0.6.0. Without this, a transitive dependency (e.g., some tool in the dev dependency tree) could still pull in 0.5.18 and re-introduce the vulnerability into the installed node_modules. The override acts as a blanket guarantee across the entire dependency graph.

The lockfile entry for adm-zip also changes from:

# Before
adm-zip:
  specifier: ^0.5.16
  version: 0.5.18

to the resolved 0.6.0 entry, confirming that pnpm will install only the patched version.

3. pnpm-workspace.yaml — Workspace-level consistency

The workspace configuration is updated to ensure the override propagates correctly in a monorepo context, where multiple packages might otherwise resolve their own independent copies of adm-zip.

Why adm-zip 0.6.0 Is Safe

adm-zip 0.6.0 added defensive checks before buffer allocation. The fix validates that the declared uncompressedSize is consistent with the compressedSize and rejects entries where the ratio or absolute size exceeds safe thresholds. This means a crafted ZIP with a 4 GB declared size but 1 KB of actual data will be rejected at parse time rather than triggering a catastrophic allocation.


Prevention & Best Practices

1. Avoid Caret Ranges for Security-Sensitive Libraries

The caret (^) in "adm-zip": "^0.5.16" is convenient for getting patch updates automatically, but it also means your lockfile can drift to a vulnerable minor version if you regenerate it. For libraries that handle untrusted binary formats (ZIP, tar, PDF, image files), consider exact pins or at minimum tilde ranges (~0.5.16, which only allows patch updates).

2. Use pnpm overrides (or npm overrides / yarn resolutions) Proactively

Transitive dependency vulnerabilities are the hardest to catch manually. Adding an overrides block to your lockfile configuration ensures that even if a dependency you don't control pulls in a vulnerable version, your project's install will enforce the safe version.

# pnpm-lock.yaml
overrides:
  adm-zip: ">=0.6.0"

3. Run SCA Scans in CI

Trivy detected this vulnerability by matching the installed adm-zip@0.5.18 against its CVE database. Integrate a Software Composition Analysis (SCA) scanner into your CI pipeline:

# Example: Trivy filesystem scan
trivy fs --scanners vuln ./source/plugins/deepseek-ivideo

This catches known-vulnerable dependency versions before they reach production.

4. Validate ZIP Files Before Parsing

Even with a patched library, apply defense-in-depth for any code that processes ZIP files from untrusted sources:

import AdmZip from 'adm-zip';
import fs from 'fs';

const MAX_ZIP_SIZE_BYTES = 50 * 1024 * 1024; // 50 MB
const MAX_ENTRIES = 1000;

function safeExtract(zipPath, outputDir) {
  const stats = fs.statSync(zipPath);
  if (stats.size > MAX_ZIP_SIZE_BYTES) {
    throw new Error(`ZIP file exceeds maximum allowed size: ${stats.size}`);
  }

  const zip = new AdmZip(zipPath);
  const entries = zip.getEntries();

  if (entries.length > MAX_ENTRIES) {
    throw new Error(`ZIP contains too many entries: ${entries.length}`);
  }

  zip.extractAllTo(outputDir, true);
}

5. Reference Standards

  • CWE-400: Uncontrolled Resource Consumption — the root cause classification for this vulnerability.
  • OWASP A05:2021 — Security Misconfiguration: Outdated or misconfigured dependencies fall under this category.
  • OWASP Dependency-Check: An open-source SCA tool that integrates with Node.js projects.

Key Takeaways

  • The ^ version range in package.json was the enabler: "adm-zip": "^0.5.16" allowed pnpm to resolve 0.5.18, the vulnerable release. Exact pins prevent this class of drift.
  • A pnpm overrides block is essential in monorepos: Without overrides: adm-zip: 0.6.0 in pnpm-lock.yaml, transitive dependencies in the deepseek-ivideo workspace could still resolve the vulnerable version independently.
  • ZIP header metadata is attacker-controlled data: The uncompressedSize field in a ZIP's central directory is entirely user-supplied. Any library that allocates memory based on this field without validation is vulnerable to this exact attack pattern.
  • A single malformed file can crash the entire Node.js process: Unlike SQL injection or XSS, this DoS requires no authentication — just the ability to supply a ZIP file to the application.
  • SCA scanning (Trivy) caught what code review would miss: The vulnerability is not in the plugin's own source code; it is in a pinned transitive dependency version. Static code analysis alone would not have found it.

How Orbis AppSec Detected This

  • Source: A ZIP archive processed by the deepseek-ivideo plugin — potentially fetched from a remote URL or supplied by a user as a file upload.
  • Sink: adm-zip's internal Buffer.alloc(entry.header.size) call site, where entry.header.size is read directly from the ZIP's central directory metadata without bounds validation.
  • Missing control: No upper-bound check on uncompressedSize before heap allocation; no rejection of entries where declared size is disproportionate to compressed size.
  • CWE: CWE-400 — Uncontrolled Resource Consumption.
  • Fix: The dependency specifier in package.json was changed from "^0.5.16" to "0.6.0", and a pnpm overrides entry was added to pnpm-lock.yaml to enforce adm-zip@0.6.0 across the entire dependency graph.

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 most dangerous vulnerabilities in modern applications often live not in your own code, but in the libraries your code depends on — and the libraries those libraries depend on. A single caret in a package.json file was all it took to expose the deepseek-ivideo plugin to a heap-exhaustion attack requiring nothing more than a crafted ZIP file.

The fix is surgical: pin adm-zip to 0.6.0, add a lockfile override to close the transitive dependency gap, and let the patched library's internal validation do the rest. More broadly, treat any library that parses binary formats from untrusted sources as a high-value security target — keep it pinned, keep it patched, and scan it automatically.


References

Frequently Asked Questions

What is a Denial of Service via ZIP parsing?

It is an attack where a specially crafted ZIP file contains header metadata (such as an inflated uncompressed-size field) that tricks a parser into allocating far more memory than is actually needed, exhausting available RAM and crashing the process.

How do you prevent uncontrolled resource consumption in Node.js ZIP handling?

Use a ZIP library version that validates header metadata against reasonable limits before allocating buffers, keep dependencies pinned to patched versions, and enforce file-size and entry-count limits before passing archives to any parser.

What CWE is this Denial of Service vulnerability?

CWE-400 — Uncontrolled Resource Consumption. It describes situations where a program does not properly limit the resources it allocates in response to external input.

Is rate-limiting enough to prevent this DoS vulnerability?

Rate-limiting reduces the frequency of attacks but does not prevent a single crafted ZIP from exhausting memory in one request. The root cause must be fixed at the library level.

Can static analysis detect this vulnerability?

Yes. Trivy flagged this exact issue by matching the installed adm-zip version against its CVE database. SCA (Software Composition Analysis) tools are the most reliable way to catch known-vulnerable dependency versions.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #2

Related Articles

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 dependabot-missing-cooldown happens in GitHub Actions/Node.js and how to fix it

The repository's `.github/dependabot.yml` had no cooldown period configured, meaning Dependabot could immediately propose updates to newly published package versions with zero time for the community to flag malware or instability. The fix adds a `cooldown` block with `default-days: 7` to both the `npm` and `github-actions` ecosystems, forcing a 7-day waiting period before new releases are surfaced as update PRs.

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.

critical

How Remote Code Execution Happens in Handlebars Template Compilation and How to Fix It

CVE-2026-33937 is a critical remote code execution vulnerability in Handlebars.js that allows attackers to execute arbitrary code by passing maliciously crafted Abstract Syntax Tree (AST) objects to the compile() function. The vulnerability was patched in version 4.7.9, and we've upgraded to protect against this threat vector.

critical

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) in the `tar` npm package allowed attackers to craft malicious gzip archives that could exhaust memory or CPU during decompression. The fix upgrades `tar` from 7.5.11 to 7.5.21 across `package.json` and `package-lock.json`, closing the resource-exhaustion path without changing any application code.