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` Node.js package (versions prior to 0.6.0) where a specially crafted ZIP file can trigger excessive memory allocation, potentially crashing the host process. The vulnerability was present in the Haven self-hosted chat application, which used `adm-zip ^0.5.16` as a direct dependency. The fix upgrades the dependency to `^0.6.0`, which includes hardened ZIP entry parsing that prevents unbounded memory allocation from

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 before 0.6.0. When a Node.js application parses a specially crafted ZIP file using the vulnerable version, the library can allocate excessive memory based on attacker-controlled values in the ZIP central directory, crashing the process. The fix is to upgrade `adm-zip` from `^0.5.16` to `^0.6.0` in `package.json` and regenerate `package-lock.json`, which introduces bounds checking on ZIP entry metadata to prevent runaway memory allocation.

Vulnerability at a Glance

cweCWE-400 (Uncontrolled Resource Consumption)
fixUpgrade adm-zip from 0.5.16 to 0.6.0, which validates ZIP entry metadata before allocating buffers
riskAn attacker who can supply a ZIP file to the application can crash the Node.js process, causing full service unavailability
languageJavaScript / Node.js
root causeadm-zip 0.5.16 trusted attacker-controlled size fields in ZIP central directory headers without upper-bound validation, leading to unbounded memory allocation
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
CVE CVE-2026-39244
Severity High
Package adm-zip (npm)
Affected versions < 0.6.0
Fixed version 0.6.0
CWE CWE-400: Uncontrolled Resource Consumption
Application Haven (self-hosted chat)

Summary

CVE-2026-39244 is a high-severity Denial of Service vulnerability in the adm-zip Node.js package (versions prior to 0.6.0) where a specially crafted ZIP file can trigger excessive memory allocation, potentially crashing the host process. The vulnerability was present in the Haven self-hosted chat application, which used adm-zip ^0.5.16 as a direct dependency. The fix upgrades the dependency to ^0.6.0, which includes hardened ZIP entry parsing that prevents unbounded memory allocation from malicious archives.


Introduction

The package-lock.json file in Haven — a self-hosted private chat platform — locked adm-zip at version 0.5.16. This library handles ZIP file creation and extraction throughout the application. Because Haven is a chat platform, it's reasonable to expect that users can upload or share files, making ZIP parsing a user-facing attack surface. A single maliciously constructed .zip file uploaded by an unauthenticated or low-privileged user could have been enough to bring the entire Haven server process down.

The vulnerability isn't in Haven's own code — it lives inside adm-zip's ZIP central directory parser, which trusted attacker-controlled size fields without enforcing any upper bound before allocating memory. This is a textbook case of why dependency version hygiene is a first-class security concern, not just a maintenance chore.


The Vulnerability Explained

How ZIP Files Are Structured (and Why That Matters)

A ZIP archive is not just a bag of compressed files. It contains a central directory at the end of the file — a table of contents that lists every entry along with metadata: file name length, extra field length, comment length, and crucially, the uncompressed size of each entry.

When a ZIP library opens an archive, it reads these metadata fields first and uses them to allocate buffers before decompressing anything. This is a performance optimization: pre-allocate the right amount of memory, then decompress into it.

The problem? Those size fields are entirely attacker-controlled. Nothing in the ZIP specification prevents a malicious actor from writing 0xFFFFFFFF (4,294,967,295 bytes — about 4 GB) as the uncompressed size of a 10-byte file.

What adm-zip 0.5.16 Did Wrong

In adm-zip version 0.5.16, the library read these metadata fields from the ZIP central directory and used them to drive memory allocation without enforcing a reasonable upper bound. A crafted ZIP file with absurdly large size values in its central directory entries would cause the Node.js process to attempt allocating gigabytes of memory — either triggering an out-of-memory crash or consuming enough resources to make the application unresponsive.

The vulnerable dependency declaration in package.json:

// BEFORE — package.json (vulnerable)
"dependencies": {
  "adm-zip": "^0.5.16",
  ...
}

And in package-lock.json, the resolved version:

// BEFORE — package-lock.json (vulnerable)
"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"
  }
}

A Concrete Attack Scenario

Imagine a Haven user uploads a file called documents.zip through the chat interface. The file is only 200 bytes on disk, but its central directory contains a single entry with an uncompressed size field set to 0xFFFFFFFF (4 GB).

When Haven's server-side code calls something like:

const AdmZip = require('adm-zip');
const zip = new AdmZip(uploadedFilePath); // triggers central directory parsing
const entries = zip.getEntries();          // iterates entries, may allocate per-entry buffers

The adm-zip 0.5.16 parser reads the 4 GB size field and attempts to allocate a 4 GB buffer. On most Node.js deployments, this either:

  1. Crashes the process with a fatal JavaScript heap out of memory error, taking down the entire Haven instance for all users.
  2. Triggers aggressive garbage collection and CPU thrashing, causing severe latency for all concurrent users.

Because this is a Node.js single-threaded event loop, one malicious upload affects every connected user simultaneously — making this a highly effective single-shot DoS attack.


The Fix

What Changed

The fix is a two-file change: package.json and package-lock.json. Together they upgrade adm-zip from 0.5.16 to 0.6.0.

package.json — before:

"adm-zip": "^0.5.16",

package.json — after:

"adm-zip": "^0.6.0",

package-lock.json — before:

"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"
  }
}

package-lock.json — 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 This Fix Works

adm-zip 0.6.0 introduces validation of ZIP entry metadata fields before using them to drive memory allocation. The library now checks that declared sizes are consistent with the actual archive size and enforces reasonable limits, preventing a 200-byte ZIP file from claiming it contains 4 GB of data.

The engines field change from >=12.0 to >=14.0 is also notable — Node.js 14 introduced improved Buffer allocation APIs and better out-of-memory handling, which the new version of adm-zip leverages for safer buffer management.

Why Both Files Need to Change

  • package.json defines the acceptable version range (^0.6.0). Without this change, running npm install in a fresh environment would still resolve to 0.5.x.
  • package-lock.json pins the exact resolved version and its integrity hash. This is what actually guarantees reproducible, secure installs in CI/CD pipelines and production deployments. The new integrity hash (sha512-XleryMhbuksdKtofnWZ9Sk...) cryptographically ensures that only the legitimate 0.6.0 package is installed — not a tampered substitute.

Prevention & Best Practices

1. Treat Archive Metadata as Untrusted Input

Any field in a ZIP, TAR, or other archive format that influences memory allocation is a potential DoS vector. When evaluating ZIP libraries, check whether they validate:

  • Uncompressed entry size vs. actual archive size
  • Number of entries vs. central directory size
  • File name length vs. remaining header bytes

2. Set File Size Limits Before Parsing

Even with a patched library, add application-level guards:

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

if (uploadedFile.size > MAX_ZIP_SIZE_BYTES) {
  throw new Error('Archive exceeds maximum allowed size');
}

const zip = new AdmZip(uploadedFilePath);

This won't stop all attacks, but it raises the bar significantly.

3. Run Dependency Scanners in CI

The Trivy scanner caught this vulnerability by matching the adm-zip version in package-lock.json against its CVE database. Add dependency scanning to your CI pipeline:

# Example: GitHub Actions with Trivy
- name: Run Trivy vulnerability scanner
  uses: aquasecurity/trivy-action@master
  with:
    scan-type: 'fs'
    scan-ref: '.'
    severity: 'HIGH,CRITICAL'

4. Use Lock Files and Verify Integrity

The package-lock.json integrity hash (sha512-...) is your last line of defense against supply chain attacks. Always commit your lock file and use npm ci (not npm install) in production and CI environments — it verifies integrity hashes and refuses to install if they don't match.

# Use this in CI/CD — it respects the lock file exactly
npm ci

# NOT this — it may update versions
npm install

5. Monitor for New CVEs in Your Dependencies

Subscribe to security advisories for your direct dependencies:

Relevant Standards

  • CWE-400: Uncontrolled Resource Consumption — the root cause category for this vulnerability.
  • OWASP A06:2021 – Vulnerable and Outdated Components — using adm-zip 0.5.16 after CVE-2026-39244 was published falls squarely into this category.

Key Takeaways

  • ZIP metadata is attacker-controlled data: The uncompressed size fields in a ZIP central directory are written by whoever created the archive. adm-zip 0.5.16 used these values to allocate memory without validation — a design assumption that breaks entirely when handling untrusted uploads.
  • A single malicious upload could crash Haven for all users: Because Node.js runs on a single-threaded event loop, one OOM-triggering ZIP extraction blocks or kills the process for every connected user simultaneously.
  • Both package.json and package-lock.json must be updated together: Changing only package.json leaves the old pinned version in the lock file; changing only package-lock.json means the next npm install can revert to the vulnerable range.
  • The engines field bump (>=14.0) signals a meaningful internal change: adm-zip 0.6.0's Node.js 14 minimum requirement reflects that it uses newer, safer buffer allocation APIs — not just a cosmetic version bump.
  • Trivy caught this from package-lock.json alone: You don't need runtime instrumentation to detect this class of vulnerability. Static dependency scanning on your lock file is sufficient — and fast enough to run on every pull request.

How Orbis AppSec Detected This

  • Source: A user-supplied ZIP file processed by the Haven application, passed to adm-zip's archive parser.
  • Sink: adm-zip's internal ZIP central directory reader in node_modules/adm-zip (version 0.5.16), which allocated buffers sized by attacker-controlled metadata fields.
  • Missing control: No upper-bound validation on ZIP entry size fields before memory allocation; no application-level archive size cap before invoking the parser.
  • CWE: CWE-400 — Uncontrolled Resource Consumption.
  • Fix: Upgraded adm-zip from 0.5.16 to 0.6.0 in both package.json and package-lock.json, replacing the vulnerable parser with one that validates ZIP entry metadata before allocating buffers.

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 the attack surface of your application includes every line of code in your node_modules directory, not just the code you write yourself. A single outdated dependency — adm-zip 0.5.16 — was enough to expose Haven's entire user base to a trivial single-request Denial of Service attack.

The fix is surgical and low-risk: two files changed, one version number bumped, and the vulnerability is closed. The harder lesson is systemic: dependency scanning needs to be a continuous, automated process, not a quarterly audit. Every ZIP, TAR, or archive your application processes is a potential resource exhaustion attack waiting to happen if the parsing library doesn't validate what it reads.

Keep your lock files committed, run npm ci in production, and let automated scanners watch your dependency tree so you can focus on building features rather than chasing CVEs.


References

Frequently Asked Questions

What is a Denial of Service via crafted ZIP file?

It is an attack where a malicious archive contains manipulated metadata (such as inflated size fields) that tricks the ZIP parser into allocating far more memory than is needed, exhausting system resources and crashing the application.

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

Use a ZIP library version that validates entry size fields against configurable limits before allocating buffers, and always treat archive metadata as untrusted input. Upgrade adm-zip to 0.6.0 or later.

What CWE is this Denial of Service vulnerability?

CWE-400: Uncontrolled Resource Consumption — the application fails to limit the amount of memory consumed when processing attacker-supplied ZIP metadata.

Is input validation alone enough to prevent ZIP-based DoS?

Application-level input validation (e.g., checking file extension) is insufficient because the malicious payload is inside the ZIP structure itself. The fix must be in the parsing library, which is why upgrading adm-zip to 0.6.0 is the correct remediation.

Can static analysis detect this vulnerability?

Yes — dependency scanners like Trivy and Snyk can flag known-vulnerable package versions in package-lock.json. In this case, Trivy's rule CVE-2026-39244 identified the vulnerable adm-zip 0.5.16 entry directly.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #5473

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.