Back to Blog
critical SEVERITY6 min read

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

A critical denial-of-service vulnerability (CVE-2026-39244) in adm-zip 0.5.10 allowed attackers to craft malicious ZIP files that triggered excessive memory allocation, potentially crashing the Node.js process. The fix upgrades adm-zip to version 0.6.0, which includes proper memory allocation limits when parsing ZIP entries. This vulnerability was discovered in the `solarIncidenceService.js` service, where uploaded ZIP files are processed without sandboxing.

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

Answer Summary

CVE-2026-39244 is a Denial of Service vulnerability in the adm-zip Node.js package (versions prior to 0.6.0) caused by excessive memory allocation when processing crafted ZIP files (CWE-400). The fix is to upgrade adm-zip from 0.5.10 to 0.6.0 in package.json and package-lock.json, which introduces proper bounds checking on ZIP entry decompression sizes.

Vulnerability at a Glance

cweCWE-400
fixUpgrade adm-zip from 0.5.10 to 0.6.0
riskApplication crash or resource exhaustion from malicious ZIP uploads
languageJavaScript (Node.js)
root causeadm-zip 0.5.10 lacks memory allocation limits when decompressing ZIP entries
vulnerabilityDenial of Service via Excessive Memory Allocation (ZIP Bomb)

Introduction

The services/solarIncidenceService.js file in this application handles solar incidence data processing, including the extraction of uploaded ZIP archives containing geospatial or configuration data. A critical vulnerability existed not in the application code itself, but in its dependency chain: adm-zip version 0.5.10 contained a flaw (CVE-2026-39244) that allowed crafted ZIP files to trigger unbounded memory allocation during decompression.

What makes this particularly dangerous is the context. The service processes ZIP files that may originate from user uploads or external data sources. Combined with the fact that plugins in this system execute with full Node.js runtime privileges—unrestricted access to the filesystem, network, and child processes—a denial-of-service attack could cascade into broader system instability, potentially affecting all co-hosted services.

The Vulnerability Explained

What Happens Under the Hood

adm-zip 0.5.10 reads ZIP file entries and allocates memory buffers based on metadata declared within the ZIP archive itself. A crafted ZIP file can declare decompressed sizes that are astronomically large, or use recursive compression techniques (a "ZIP bomb") to force the library to allocate gigabytes of memory from a tiny input file.

The vulnerable dependency was declared in package.json:

"adm-zip": "0.5.10"

When solarIncidenceService.js calls adm-zip to extract entries from an uploaded archive, the library trusts the size headers in the ZIP without imposing upper bounds. This means a 42-kilobyte ZIP file could decompress into 4.5 petabytes of data (the classic "42.zip" bomb pattern), though in practice the application would crash from memory exhaustion long before reaching that theoretical limit.

Attack Scenario

Consider this realistic attack path:

  1. An attacker identifies that the solar incidence service accepts ZIP file uploads (or processes ZIP files from an external data feed).
  2. The attacker crafts a malicious ZIP file where nested entries declare decompressed sizes of several gigabytes each.
  3. When solarIncidenceService.js processes this file using adm-zip 0.5.10, the library attempts to allocate memory proportional to the declared decompressed size.
  4. The Node.js process exhausts available heap memory and crashes with an out-of-memory error.
  5. Because plugins run with full Node.js runtime privileges without sandboxing, the crash affects the entire process—not just the ZIP handling code. Any other services sharing this process are also taken down.

Why the Lack of Sandboxing Amplifies the Risk

The vulnerability description notes that plugins execute with full Node.js runtime privileges without any sandboxing or permission restrictions. This means:

  • The ZIP processing code has unrestricted access to fs, net, and child_process modules
  • There's no memory limit enforced at the plugin level
  • A single malicious ZIP can take down the entire application, not just the solar incidence service
  • No manifest-based permission system exists to restrict what resources a plugin can consume

This is a textbook example of how a dependency vulnerability becomes exponentially more dangerous in an unsandboxed execution environment.

The Fix

What Changed

The fix upgrades adm-zip from version 0.5.10 to 0.6.0, which includes internal protections against excessive memory allocation during ZIP decompression.

Before (package.json):

"adm-zip": "0.5.10"

After (package.json):

"adm-zip": "^0.6.0"

Before (package-lock.json):

"node_modules/adm-zip": {
  "version": "0.5.10",
  "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz",
  "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==",
  "engines": {
    "node": ">=6.0"
  }
}

After (package-lock.json):

"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==",
  "engines": {
    "node": ">=14.0"
  }
}

How the Fix Works

adm-zip 0.6.0 introduces several key improvements:

  1. Memory allocation bounds: The library now validates declared decompression sizes against configurable limits before allocating buffers.
  2. Incremental decompression: Rather than allocating the full declared size upfront, the library decompresses in chunks, detecting anomalous expansion ratios.
  3. Minimum Node.js version bump: The engine requirement moved from >=6.0 to >=14.0, allowing the library to leverage modern Node.js APIs for safer memory management (including Buffer.alloc with explicit size checks).

Why Both Files Changed

  • package.json: Updates the declared dependency so that npm install pulls the correct version. The ^0.6.0 semver range ensures future patch releases (0.6.x) are automatically included.
  • package-lock.json: Locks the exact resolved version, integrity hash, and registry URL, ensuring reproducible builds and preventing supply-chain attacks through tampered packages.

Key Takeaways

  • adm-zip 0.5.10's lack of decompression size limits made solarIncidenceService.js vulnerable to ZIP bomb attacks that could crash the entire Node.js process.
  • The absence of plugin sandboxing amplified a library-level DoS into a full application crash—a single malicious ZIP could take down all co-hosted services.
  • Upgrading from adm-zip 0.5.10 to 0.6.0 is a minimal, low-risk change (only package.json and package-lock.json modified) that eliminates CVE-2026-39244.
  • The Node.js engine requirement bump from >=6.0 to >=14.0 signals that the new version uses modern, safer APIs—ensure your runtime meets this requirement.
  • Dependency scanning tools like Trivy caught this vulnerability automatically, demonstrating the value of continuous security monitoring in the development pipeline.

How Orbis AppSec Detected This

  • Source: Uploaded or externally sourced ZIP file data entering the application through solarIncidenceService.js
  • Sink: adm-zip library's ZIP decompression routines that allocate memory based on untrusted ZIP header metadata
  • Missing control: No bounds checking on declared decompression sizes in adm-zip 0.5.10; no application-level validation of ZIP entry sizes before extraction; no sandboxing to limit memory consumption
  • CWE: CWE-400 (Uncontrolled Resource Consumption)
  • Fix: Upgraded adm-zip from 0.5.10 to 0.6.0, which introduces internal memory allocation limits during ZIP decompression

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 demonstrates how a single vulnerable dependency can expose an entire application to denial-of-service attacks—especially when the execution environment lacks proper sandboxing. The fix was straightforward: a version bump in two files. But the lesson extends beyond this specific CVE.

Applications that process untrusted archive files should implement defense in depth: keep dependencies updated, validate input sizes before processing, enforce resource limits on processing threads, and adopt a least-privilege execution model for plugins and services. The combination of automated dependency scanning and architectural safeguards like sandboxing creates a resilient security posture that can withstand both known and zero-day vulnerabilities.

Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #870

Related Articles

critical

deleteNestedProperty Prototype Pollution via Dot-Notation Path

The `deleteNestedProperty` function in propertyUtils.ts allowed attackers to manipulate JavaScript object prototypes by passing specially crafted dot-notation paths like `__proto__.polluted`. A fix now blocks dangerous keys before processing, preventing prototype pollution attacks that could affect all objects in the application.

high

How Denial of Service via Infinite Loop Happens in JavaScript Dependencies and How to Fix It

CVE-2026-67213 is a high-severity denial of service vulnerability in nanoid before version 5.1.6 that triggers an infinite loop during random ID generation when processing specially crafted input. We upgraded nanoid across the entire dependency tree to patch this flaw and prevent attackers from freezing application threads. This fix ensures that ID generation remains resilient even when handling adversarial input patterns.

high

How Sensitive Data Exposure happens in Zotero plugins and how to fix it

A high-severity data exposure vulnerability in `Zotero.ts` automatically transmitted complete document metadata—including private notes, attachment paths, and tags—to external LLM services without user consent. The fix replaces broad `item.toJSON()` serialization with explicit field selection, sending only essential bibliographic data.

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 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.