Back to Blog
high SEVERITY5 min read

How Denial of Service via Crafted ZIP File 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 that allows attackers to crash Node.js applications by uploading maliciously crafted ZIP files. The fix upgrades adm-zip from version 0.5.16 to 0.6.0, which adds proper memory bounds checking to prevent excessive allocation during archive extraction.

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published September 9, 2026•Reviewed September 9, 2026

Answer Summary

CVE-2026-39244 is a Denial of Service (DoS) vulnerability in adm-zip versions before 0.6.0, affecting Node.js applications that process ZIP archives. Classified under CWE-400 (Uncontrolled Resource Consumption), the vulnerability allows attackers to cause excessive memory allocation and application crashes by submitting crafted ZIP files with manipulated headers. The fix requires upgrading adm-zip to version 0.6.0 or later, which implements proper resource limits during archive parsing.

Vulnerability at a Glance

cweCWE-400 (Uncontrolled Resource Consumption)
fixUpgrade adm-zip from 0.5.16 to 0.6.0
riskApplication crash or unresponsiveness due to memory exhaustion
languageJavaScript/Node.js
root causeMissing validation of ZIP header claims allowing inflated memory allocation
vulnerabilityDenial of Service via Crafted ZIP File

Introduction

In a routine dependency audit of a Node.js backend service, Orbis AppSec's automated scanning identified a high-severity vulnerability lurking in backend/package-lock.json. The adm-zip package at version 0.5.16—listed under optionalDependencies—contained CVE-2026-39244, a denial of service flaw that could transform an ordinary file upload feature into an application-killing attack vector.

The vulnerability resided in how adm-zip parsed ZIP file headers. Unlike many compression-related vulnerabilities that require extracting massive payloads, this flaw triggers during header inspection—before any decompression occurs. An attacker could craft a ZIP file with headers claiming astronomical uncompressed sizes, causing adm-zip to pre-allocate memory based on these fraudulent claims. The result: instantaneous memory exhaustion and application crashes, even with minimal upload bandwidth.

The Vulnerability Explained

ZIP files contain metadata headers that declare properties of compressed entries, including the uncompressed size. The adm-zip library uses these headers to prepare extraction buffers. In versions prior to 0.6.0, this preparation lacked sanity checks—meaning a 1KB ZIP file could claim to contain 4GB of uncompressed data, and adm-zip would attempt to allocate that memory immediately.

Here's the vulnerable dependency declaration in backend/package.json:

"optionalDependencies": {
  "adm-zip": "^0.5.16"
}

And the locked version in backend/package-lock.json:

"node_modules/adm-zip": {
  "version": "0.5.16",
  "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz",
  "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==",
  "license": "MIT",
  "optional": true,
  "engines": {
    "node": ">=12.0"
  }
}

The optional: true flag is particularly insidious here—it means this dependency might not be installed in all environments, making vulnerability scanning inconsistent across development, staging, and production deployments.

Attack Scenario: The Header Bomb

Consider a backend route that accepts ZIP uploads for processing:

// Hypothetical vulnerable code pattern
const AdmZip = require('adm-zip');

app.post('/upload', (req, res) => {
  const zip = new AdmZip(req.body.zipBuffer);
  const entries = zip.getEntries(); // Triggers header parsing

  entries.forEach(entry => {
    // Memory already allocated here based on entry.header.size
    const data = entry.getData(); // Could crash process
  });
});

An attacker crafts a ZIP file with this structure:
- Local file header declares uncompressedSize: 0xFFFFFFFF (4,294,967,295 bytes)
- Actual compressed data: 20 bytes of zeros

When adm-zip 0.5.16 parses this, it attempts to allocate ~4GB of Buffer memory. On most Node.js deployments, this triggers an immediate RangeError: Array buffer allocation failed or worse—brings down the entire process through unhandled exceptions.

The Fix

The remediation involved a precise version bump across both dependency manifests. Here's the complete change:

package.json Changes

   "optionalDependencies": {
-    "adm-zip": "^0.5.16"
+    "adm-zip": "^0.6.0"
   },

package-lock.json Changes

@@ -26,7 +27,7 @@
         "node": ">=22.12"
       },
       "optionalDependencies": {
-        "adm-zip": "^0.5.16"
+        "adm-zip": "^0.6.0"
       }
     },
     "node_modules/@asamuzakjp/css-color": {
@@ -626,13 +627,13 @@
       }
     },
     "node_modules/adm-zip": {
-      "version": "0.5.16",
-      "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz",
-      "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==",
+      "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",
       "optional": true,
       "engines": {
-        "node": ">=12.0"
+        "node": ">=14.0"
       }
     },
     "node_modules/ajv": {

Notice two critical improvements in 0.6.0:

  1. Memory bounds validation: The new version validates claimed uncompressed sizes against reasonable limits before allocation
  2. Node.js engine requirement: Raised from >=12.0 to >=14.0, ensuring modern memory management APIs are available

The integrity hash change (sha512-XleryMhbuksd... → sha512-XleryMhbuksd...) confirms a complete package replacement, not just a metadata update.

Key Takeaways

  • Optional dependencies require mandatory scrutiny: The optional: true flag in package.json doesn't reduce security risk—it merely makes detection harder
  • Header claims are attacker-controlled input: Never trust size metadata in file formats; validate against actual resource constraints
  • Pre-allocation attacks bypass size limits: This vulnerability demonstrates that checking compressed file size is insufficient when libraries allocate based on uncompressed claims
  • Engine version bumps signal security changes: The Node.js >=14.0 requirement in adm-zip 0.6.0 indicates the fix relies on modern runtime capabilities
  • Lock file integrity hashes prevent tampering: The integrity field in package-lock.json ensures the exact patched version is installed

How Orbis AppSec Detected This

  • Source: User-influenced file upload data entering through HTTP request bodies containing ZIP archive buffers
  • Sink: The adm-zip package's header parsing logic in node_modules/adm-zip, specifically where entry.header.size values drive Buffer allocation without upper bounds checking
  • Missing control: Absent validation of ZIP header uncompressed size claims against configurable or hardcoded maximum memory thresholds
  • CWE: CWE-400: Uncontrolled Resource Consumption
  • Fix: Upgraded adm-zip from 0.5.16 to 0.6.0 via backend/package.json and backend/package-lock.json modifications, leveraging the patched version's built-in size validation and modern Node.js memory management APIs

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 exemplifies how even "optional" dependencies can introduce critical attack surfaces. The gap between versions 0.5.16 and 0.6.0 of adm-zip demonstrates that security fixes sometimes require breaking changes—here, dropping Node.js 12 support—to implement proper resource controls.

For development teams, this case reinforces that dependency management is security management. Every entry in package.json and package-lock.json represents potential execution of third-party code with full application privileges. Automated scanning, prompt patching, and defense-in-depth validation remain essential practices for maintaining secure Node.js deployments.

Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #247

Related Articles

high

CVE-2026-4800: lodash Template Imports Allow Code Execution

CVE-2026-4800 affects lodash's `_.template()` templating API, where untrusted input reaching the `imports` option can lead to arbitrary code execution. The fix was shipped as a dependency upgrade from lodash 4.17.21 to 4.18.1 in the project's lockfile, though the PR itself notes it was never verified against the actual code paths in this repository.

critical

proxy-addr 2.0.7 IP Spoofing: CVE-2026-90711 Trust Bypass

A critical vulnerability in proxy-addr 2.0.7 allowed attackers to spoof client IP addresses by manipulating X-Forwarded-For headers when the trust chain evaluation contained specific misconfigurations. The fix in version 2.0.8 hardens the trust evaluation logic to prevent IP address falsification in Express.js applications relying on this common middleware dependency.

high

TweenMax `_applyCycle` Prototype Pollution via vars.cycle Keys

A bundled copy of the TweenMax animation library copied attacker-influenceable `vars.cycle` property names straight onto a tween configuration object using an unguarded `for...in` loop, so a key named `__proto__`, `constructor`, or `prototype` was written through to the object's prototype chain. The fix adds an explicit key denylist to both copies of the `_applyCycle` helper so those three names are skipped during the merge. No CVE or GHSA is assigned; the issue is tracked as CWE-1321 (Improperl

high

picomatch 2.3.1 ReDoS: Extglob Pattern Catastrophic Backtracking

picomatch versions below 2.3.2, 3.0.2, or 4.0.4 contain a Regular Expression Denial of Service vulnerability in extglob pattern parsing. An attacker can cause catastrophic backtracking with patterns containing nested alternations and quantifiers, freezing any Node.js process that evaluates untrusted glob expressions.

critical

pet-window.js Dynamic Code Evaluation: CWE-94 Hardening via Number

The pet-window module constructed dynamic JavaScript by embedding raw configuration values into code strings. An attacker with local access could inject arbitrary JavaScript by modifying stored configuration. The fix replaces string interpolation with explicit Number() coercion and NaN validation for all numeric configuration parameters.

high

sanitizeUnicodeInput(): Fullwidth U+ Bypasses Codepoint Validation

The `sanitizeUnicodeInput()` helper used by the project character-range settings screen rewrote `U+` prefixes to `0x` and called `parseInt()`, but never normalized its argument first. Compatibility-equivalent forms such as fullwidth `U+`, superscript digits, or mathematical alphanumerics never matched the `/U\+/gi` regex, fell through to the `else return inputString` branch, and were handed back to callers verbatim as "sanitized" values. The fix inserts a `String.prototype.normalize('NFKC')` pas