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 Denial of Service via Unbounded Intermediate Arrays happens in JavaScript and how to fix it

CVE-2026-69152 is a high-severity Denial of Service vulnerability in the `brace-expansion` npm package (versions prior to 1.1.18/2.1.4/3.0.6/5.0.9) that allows attackers to crash a Node.js application by crafting glob patterns that generate unbounded intermediate arrays, effectively bypassing the earlier CVE-2026-14257 mitigation. The fix upgrades `brace-expansion` from 1.1.14 to 1.1.18 in `frontend/package-lock.json`, closing the bypass and restoring safe memory bounds during pattern expansion.

high

How Quadratic CPU Consumption happens in JavaScript YAML parsing and how to fix it

A high-severity denial-of-service vulnerability (GHSA-5p4m-2wfm-xmqj) was discovered in js-yaml affecting both the 3.x and 4.x branches, where parsing YAML documents containing `!!omap` tags triggers quadratic CPU consumption. The fix upgrades js-yaml from `^4.1.1` to `5.2.0` in the project's GitHub Actions workflow dependencies, closing the attack surface for any untrusted YAML input processed by CI/CD tooling.

critical

How Missing Rate Limiting happens in Express.js and how to fix it

Two public API endpoints in `server.js` — `/api/health` and `/api/contact` — were exposed without any rate limiting middleware, allowing attackers to exhaust server resources or spam an SMTP server with unlimited requests. The fix adds rate limiting to both endpoints, with stricter controls on the resource-intensive `/api/contact` route that triggers email sending operations. This change closes a directly exploitable denial-of-service vector in a production web service.

high

How Denial of Service via Specific Input Sequence happens in JavaScript (marked) and how to fix it

CVE-2026-41680 is a high-severity Denial of Service vulnerability in the marked Markdown parsing library, affecting versions prior to 18.0.2. By supplying a crafted input sequence to the parser, an attacker can cause the application to hang or exhaust resources, making the frontend unavailable. Upgrading marked from 18.0.0 to 18.0.2 in both `package.json` and `package-lock.json` closes the vulnerability without affecting valid Markdown rendering.

high

How Quadratic CPU Consumption happens in JavaScript YAML parsing and how to fix it

A high-severity denial-of-service vulnerability in js-yaml (GHSA-5p4m-2wfm-xmqj) caused quadratic CPU consumption when resolving `!!omap` YAML types in both the 3.x and 4.x branches. The fix upgrades js-yaml from 3.14.2 to 3.15.1 and from 4.1.1 to 4.3.1, eliminating the algorithmic complexity exploit while leaving all valid YAML inputs unaffected.

high

How Denial of Service via Unbounded Data Happens in JavaScript and how to fix it

CVE-2025-58754 is a high-severity Denial of Service vulnerability in the popular axios HTTP client library, caused by the absence of a data size check on incoming response or request payloads. An attacker who can influence the size of data processed by axios could exhaust server memory or CPU, bringing down dependent Node.js applications. The fix upgrades axios from version 1.8.4 to 1.18.0, closing the unbounded data processing path.