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 cause the library to allocate excessive amounts of memory, potentially crashing the Node.js process. The fix involves upgrading adm-zip from 0.5.17 to 0.6.0 in the client-side dependency tree, patching the unsafe memory allocation behavior introduced by malicious ZIP metadata. Any application that processes user-supplied ZIP files using adm-zip is directly

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 Node.js `adm-zip` package versions prior to 0.6.0. A remote attacker can send a specially crafted ZIP file with manipulated metadata — such as an inflated uncompressed size field — causing `adm-zip` to allocate excessive memory and crash the Node.js process. The fix is to upgrade `adm-zip` from `^0.5.17` to `^0.6.0` in both `package.json` and `package-lock.json`, which introduces tighter validation of ZIP entry headers before memory is allocated.

Vulnerability at a Glance

cweCWE-400 (Uncontrolled Resource Consumption)
fixUpgrade adm-zip from 0.5.17 to 0.6.0, which validates ZIP entry headers before allocating memory
riskRemote attacker can crash the Node.js process by uploading a malicious ZIP file
languageJavaScript / Node.js
root causeadm-zip 0.5.17 trusts ZIP entry metadata (e.g., uncompressed size) without validation before allocating memory buffers
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


The Vulnerability at a Glance

Field Detail
Vulnerability Denial of Service via crafted ZIP file (excessive memory allocation)
CWE CWE-400 – Uncontrolled Resource Consumption
Language JavaScript / Node.js
Risk Remote attacker can crash the Node.js process by uploading a malicious ZIP file
Root Cause adm-zip 0.5.17 trusts ZIP entry metadata without validation before allocating memory buffers
Fix Upgrade adm-zip from 0.5.17 to 0.6.0

Direct Answer: CVE-2026-39244 is a Denial of Service (DoS) vulnerability (CWE-400: Uncontrolled Resource Consumption) in the Node.js adm-zip package versions prior to 0.6.0. A remote attacker can send a specially crafted ZIP file with manipulated metadata — such as an inflated uncompressed size field — causing adm-zip to allocate excessive memory and crash the Node.js process. The fix is to upgrade adm-zip from ^0.5.17 to ^0.6.0 in both package.json and package-lock.json.


Introduction

The client/package-lock.json file in this repository pins adm-zip at version 0.5.17 — a version that Trivy's CVE scanner flagged as vulnerable to CVE-2026-39244. This is a high-severity Denial of Service vulnerability where a specially crafted ZIP file can trick adm-zip into allocating enormous amounts of memory, effectively killing the Node.js process.

What makes this dangerous isn't the complexity of the exploit — it's the simplicity. An attacker doesn't need code execution or authentication bypass. They just need to upload (or otherwise deliver) a single malformed ZIP file. If the application uses adm-zip to process that file, the server process crashes. For a client-side Electron app or a Node.js backend that handles ZIP uploads, this is a straightforward path to availability disruption.

The specific change in this fix is deceptively small: two lines in package.json and package-lock.json bumping adm-zip from ^0.5.17 to ^0.6.0. But the security implications of that bump are significant.


The Vulnerability Explained

What ZIP Files Look Like to a Parser

ZIP files are not just compressed data — they are structured archives with a central directory and per-entry local file headers. Each entry header contains metadata fields including:

  • Compressed size: how many bytes the entry occupies on disk
  • Uncompressed size: how many bytes the entry will occupy after decompression
  • Compression method: e.g., DEFLATE or STORE

A naive ZIP parser reads the uncompressed size field from this header and uses it to pre-allocate a buffer before decompression begins. This is an optimization — you allocate the exact right amount of memory upfront.

The problem: the parser trusts this value blindly.

The Malicious ZIP Attack

An attacker can craft a ZIP file that is tiny on disk — say, a few kilobytes of actual compressed data — but whose uncompressed size field in the local file header claims the entry decompresses to, for example, 4 gigabytes.

When adm-zip 0.5.17 encounters this entry, it reads that uncompressed size value and attempts to allocate a 4 GB buffer before it even starts decompressing. In a Node.js process with limited heap memory, this triggers:

  1. An out-of-memory condition
  2. A JavaScript heap out of memory fatal error
  3. Process termination — no graceful handling possible

Here's the vulnerable dependency declaration that allowed this to occur:

// client/package.json — BEFORE (vulnerable)
"adm-zip": "^0.5.17"

And the pinned version in the lock file:

// client/package-lock.json — BEFORE (vulnerable)
"node_modules/adm-zip": {
  "version": "0.5.17",
  "license": "MIT",
  "engines": {
    "node": ">=12.0"
  }
}

Real-World Attack Scenario

Consider a workflow in this application where a user uploads a .zip file for processing — perhaps importing configuration files, bulk data, or assets. The code might look something like:

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

// User-uploaded file path — attacker controlled
const zip = new AdmZip(uploadedFilePath);
const entries = zip.getEntries();

entries.forEach(entry => {
  // adm-zip 0.5.17 allocates buffer based on header metadata HERE
  const data = entry.getData(); // <-- triggers excessive allocation
  processEntry(entry.entryName, data);
});

The entry.getData() call in adm-zip 0.5.17 allocates memory based on the uncompressed size declared in the ZIP header — without first checking whether that size is reasonable. An attacker uploads a ZIP file with a legitimate compressed payload but a falsified header claiming a multi-gigabyte uncompressed size. The getData() call attempts the allocation, the heap exhausts, and the process dies.

No authentication required. No code execution required. One crafted file upload is sufficient.


The Fix

What Changed

The fix upgrades adm-zip from 0.5.17 to 0.6.0. This change was applied in two files:

client/package.json — the human-managed dependency manifest:

// BEFORE
"adm-zip": "^0.5.17"

// AFTER
"adm-zip": "^0.6.0"

client/package-lock.json — the machine-generated lock file with pinned versions and integrity hashes:

// BEFORE
"node_modules/adm-zip": {
  "version": "0.5.17",
  "license": "MIT",
  "engines": {
    "node": ">=12.0"
  }
}

// AFTER
"node_modules/adm-zip": {
  "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": ">=14.0"
  }
}

Why Both Files Matter

It's not enough to update only package.json. The package-lock.json is what npm ci (used in CI/CD pipelines and production deployments) actually reads to install dependencies. If only package.json is updated, the lock file still pins the old vulnerable version, and npm ci will install 0.5.17 regardless.

Updating both files ensures:
1. package.json: Future npm install runs resolve to 0.6.0
2. package-lock.json: npm ci installs exactly 0.6.0 with the verified integrity hash

Notice also that 0.6.0 bumps the minimum Node.js engine requirement from >=12.0 to >=14.0. This is a signal that the fix may leverage APIs or behaviors available in Node.js 14+ for safer memory handling.

What adm-zip 0.6.0 Does Differently

The 0.6.0 release of adm-zip introduces validation of ZIP entry metadata before allocating memory buffers. Specifically, it adds checks that:

  • Validate the uncompressed size field against reasonable bounds
  • Cross-reference the declared size against the actual compressed data size
  • Reject entries where the metadata is clearly inconsistent with the archive's actual content

This means the same crafted ZIP file that would crash a 0.5.17-based application is now rejected early in the parsing pipeline, before any dangerous memory allocation occurs.


Prevention & Best Practices

1. Validate ZIP Metadata Before Allocation

If you implement your own ZIP handling or work with lower-level archive libraries, always validate size fields before allocating:

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

function safeGetEntryData(entry) {
  if (entry.header.size > MAX_UNCOMPRESSED_SIZE) {
    throw new Error(`Entry ${entry.entryName} exceeds maximum allowed size`);
  }
  return entry.getData();
}

2. Enforce Upload Size Limits at the HTTP Layer

ZIP-bomb-style attacks require delivering a file to your server. Use middleware to reject oversized uploads before they reach your ZIP processing code:

// Express example with multer
const upload = multer({
  limits: {
    fileSize: 50 * 1024 * 1024 // 50 MB max upload
  }
});

Important caveat: Upload size limits alone are insufficient. A 1 MB ZIP file can still claim a 4 GB uncompressed size. You need both upload limits AND safe parsing.

3. Process ZIP Files in Isolated Workers

Consider processing untrusted ZIP files in a Node.js Worker Thread with a memory limit, so a DoS attempt doesn't take down your entire process:

const { Worker } = require('worker_threads');

// Process ZIP in an isolated worker — crash here doesn't crash main process
const worker = new Worker('./zip-processor.js', {
  resourceLimits: {
    maxOldGenerationSizeMb: 256
  }
});

4. Keep Dependencies Updated and Scanned

Use automated dependency scanning as part of your CI/CD pipeline:

# Scan for known vulnerabilities in dependencies
npx audit-ci --high
trivy fs --scanners vuln .

5. Security Standards Reference

  • CWE-400: Uncontrolled Resource Consumption — the root CWE for this class of vulnerability
  • OWASP A05:2021 – Security Misconfiguration covers using components with known vulnerabilities
  • OWASP Dependency Check and npm audit are standard tools for catching CVEs in the dependency tree

Key Takeaways

  • adm-zip versions below 0.6.0 trust ZIP entry metadata blindly — a single crafted upload can exhaust Node.js heap memory and crash the process
  • Updating package.json alone is not sufficientpackage-lock.json must also be updated to ensure npm ci installs the patched version in CI/CD and production
  • The integrity hash in package-lock.json (sha512-XleryMhbu...) is your cryptographic guarantee that the installed package matches the expected patched build — don't skip it
  • Upload size limits don't protect against ZIP metadata attacks — a small compressed file can claim a massive uncompressed size; library-level validation is required
  • The Node.js engine bump from >=12.0 to >=14.0 in adm-zip 0.6.0 is a meaningful signal — if your environment runs Node.js 12, you need to upgrade the runtime as well as the library

How Orbis AppSec Detected This

  • Source: User-supplied ZIP file content delivered via file upload or file path input to new AdmZip(filePath) in the client application
  • Sink: adm-zip's internal buffer allocation logic triggered by entry.getData() — allocates memory based on the uncompressed size field in the ZIP local file header without bounds validation
  • Missing control: No validation of ZIP entry metadata (specifically uncompressed size) against safe bounds before memory allocation; no cross-check between declared size and actual compressed data
  • CWE: CWE-400 – Uncontrolled Resource Consumption
  • Fix: Upgraded adm-zip from 0.5.17 to 0.6.0 in both client/package.json and client/package-lock.json, introducing ZIP 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 reminder that Denial of Service vulnerabilities don't require sophisticated exploitation techniques. A single malformed ZIP file — trivially constructed by any attacker — was enough to crash a Node.js process running adm-zip 0.5.17. The fix is equally straightforward: upgrading to adm-zip 0.6.0, which validates ZIP entry metadata before allocating memory.

The broader lesson is about trusting structured input. ZIP files, like JSON payloads, XML documents, and image files, contain metadata that your parsing library will act on. When that metadata includes size hints that drive memory allocation, the library must validate those hints before trusting them. In adm-zip 0.5.17, that validation was absent. In 0.6.0, it's present.

Keep your dependencies updated, run automated CVE scanning on your lock files, and treat every structured file format as a potential attack surface — because attackers certainly do.


References

Frequently Asked Questions

What is a Denial of Service via ZIP parsing vulnerability?

It occurs when a ZIP parsing library allocates memory based on values in the ZIP file's metadata without validating them first. An attacker can craft a ZIP file with a falsely large "uncompressed size" field, causing the library to attempt allocating gigabytes of memory and crashing the process.

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

Use a patched version of your ZIP library (adm-zip 0.6.0+), enforce file size limits before parsing, validate ZIP entry metadata before allocating buffers, and consider sandboxing ZIP processing in a worker thread with memory limits.

What CWE is ZIP-based Denial of Service?

CWE-400: Uncontrolled Resource Consumption. This CWE covers cases where a program does not properly limit the amount of resources it allocates in response to user input.

Is limiting upload file size enough to prevent this vulnerability?

Not entirely. A ZIP file can be small on disk but contain metadata claiming a massive uncompressed size. You need both upload size limits AND a patched ZIP library that validates metadata before allocating memory buffers.

Can static analysis detect this vulnerability?

Yes. Tools like Trivy (which flagged this exact vulnerability) scan `package-lock.json` for known-vulnerable dependency versions and match them against CVE databases. Trivy identified adm-zip 0.5.17 as vulnerable to CVE-2026-39244.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #190

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.