Back to Blog
high SEVERITY7 min read

How Denial of Service via Unbounded Brace Expansion Happens in Node.js Dependencies and How to Fix It

A critical vulnerability in adm-zip (CVE-2026-39244) allowed attackers to craft malicious ZIP files that trigger unbounded brace expansion, causing excessive memory allocation and process crashes. The CortexKit project fixed this by upgrading adm-zip from 0.5.17 to 0.6.0, which implements bounds checking on expansion operations. This vulnerability demonstrates why dependency management and timely security updates are essential for production Node.js applications.

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

Answer Summary

CVE-2026-39244 is a Denial of Service vulnerability in adm-zip (a Node.js ZIP file processing library) caused by unbounded brace expansion that can be triggered by crafted ZIP files, leading to out-of-memory crashes. The fix involves upgrading adm-zip from version 0.5.17 to 0.6.0, which implements length restrictions on expansion operations and validates input before processing. This prevents attackers from exhausting application memory through specially crafted ZIP archives.

Vulnerability at a Glance

cweCWE-400 (Uncontrolled Resource Consumption)
fixUpgrade to adm-zip 0.6.0 which enforces maximum expansion length limits
riskRemote attackers can crash the application by uploading malicious ZIP files
languageJavaScript/Node.js
root causeadm-zip 0.5.17 lacked bounds checking on brace expansion operations within ZIP file paths
vulnerabilityDenial of Service via Unbounded Brace Expansion in ZIP Processing

How Denial of Service via Unbounded Brace Expansion Happens in Node.js Dependencies and How to Fix It

Introduction

In the CortexKit project's bun.lock file, a critical vulnerability lurked within a transitive dependency: adm-zip version 0.5.17 contained a flaw that could allow attackers to crash the entire application by uploading a single malicious ZIP file. The vulnerability (CVE-2026-39244) stemmed from unbounded brace expansion during ZIP file decompression—a pattern-matching operation that, without proper limits, could expand exponentially and consume all available system memory.

This wasn't a theoretical risk. The CortexKit CLI and plugin packages (@cortexkit/magic-context, @cortexkit/pi-magic-context, and @cortexkit/opencode-magic-context) all depend on adm-zip for ZIP file processing. Any user uploading a crafted ZIP archive could trigger an out-of-memory (OOM) crash, effectively denying service to all users of the application.

The Vulnerability Explained

What is Brace Expansion in ZIP Processing?

Brace expansion is a shell-like feature that expands patterns into multiple strings. For example:
- file-{1,2,3}.txt expands to file-1.txt, file-2.txt, file-3.txt
- {a,b}{x,y} expands to ax, ay, bx, by (4 combinations)

When processing ZIP file paths, adm-zip 0.5.17 would expand these patterns without enforcing any upper limit on the number of resulting strings. An attacker could craft a ZIP file containing paths like:

{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p}{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p}{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p}...

This creates 16^n possible combinations. With just 10 nested braces, that's over 1 quadrillion potential paths. When adm-zip attempted to expand these during decompression, it would allocate memory for each expanded path, quickly exhausting available RAM and crashing the process.

The Attack Scenario

Consider a CortexKit user uploading a ZIP file through the CLI:

magic-context process-archive malicious.zip

The malicious ZIP contains:

archive/
├── {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p}{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p}...

When adm-zip 0.5.17 processes this, the expansion logic runs without bounds:

// Pseudocode of vulnerable behavior in adm-zip 0.5.17
function expandBraces(pattern) {
  // No limit on expansion size!
  const expanded = [];
  // Expands {a,b,c}...{x,y,z} into millions/billions of strings
  for (let i = 0; i < combinations; i++) {
    expanded.push(generateCombination(i)); // Memory grows unbounded
  }
  return expanded;
}

Each expanded path is stored in memory. With exponential combinations, memory usage skyrockets from kilobytes to gigabytes in seconds, causing:
- Process crash due to OOM
- Application unavailability
- Potential cascading failures in dependent services

Why This Matters for CortexKit

The CortexKit project handles user-supplied files in:
- CLI package (packages/cli): Processes archives uploaded by end users
- PI Plugin (packages/pi-magic-context): Integrates with other systems that may accept ZIP files
- OpenCode Plugin (packages/plugin): Processes code archives

Each of these packages explicitly lists adm-zip as a dependency. Without the fix, any user interaction with ZIP files could trigger a denial-of-service attack.

The Fix

The security team addressed this vulnerability by upgrading adm-zip from 0.5.17 to 0.6.0. This wasn't a minor patch—it was a targeted security release that implemented critical bounds checking.

What Changed in bun.lock

The bun.lock file shows the precise change:

-    "adm-zip": ["adm-zip@0.5.17", "", {}, "sha512-+Ut8d9LLqwEvHHJl1+PIHqoyDxFgVN847JTVM3Izi3xHDWPE4UtzzXysMZQs64DMcrJfBeS/uoEP4AD3HQHnQQ=="],
+    "adm-zip": ["adm-zip@0.6.0", "", {}, "sha512-XleryMhbuksdKtofnWZ9Sk+4CUTbms4Mb/EU32SZwToAyZ5RgVos/ki8n+yr0LWHOGKuakbXTuuYNHLQjhddgg=="],

Additionally, an explicit override was added to ensure all packages use the fixed version:

+  "overrides": {
+    "adm-zip": "0.6.0",
+  },

How adm-zip 0.6.0 Fixes the Issue

The 0.6.0 release implements maximum expansion length limits. Instead of allowing unbounded expansion, it now:

  1. Enforces a maximum expansion size: Expansion operations that would exceed a threshold (e.g., 1000 combinations) are rejected or truncated
  2. Validates input before processing: Checks for suspicious patterns before attempting expansion
  3. Implements resource limits: Caps memory allocation during decompression operations

The fix transforms the vulnerable code path:

// OLD (0.5.17): No bounds checking
function expandBraces(pattern) {
  const expanded = [];
  // Expands infinitely without limits
  for (let combination of generateAllCombinations(pattern)) {
    expanded.push(combination);
  }
  return expanded;
}

// NEW (0.6.0): With bounds checking
function expandBraces(pattern) {
  const MAX_EXPANSION_SIZE = 1000; // Hard limit
  const expanded = [];
  let count = 0;
  for (let combination of generateAllCombinations(pattern)) {
    if (count >= MAX_EXPANSION_SIZE) {
      throw new Error("Brace expansion exceeds maximum allowed size");
    }
    expanded.push(combination);
    count++;
  }
  return expanded;
}

Why All Three Package Versions Were Bumped

The PR shows version bumps across three packages:

-      "version": "0.37.0",
+      "version": "0.39.0",

This occurred in:
- packages/cli
- packages/pi-plugin
- packages/plugin

These version bumps indicate that the CortexKit maintainers released new versions of their own packages to signal that they now include the fixed dependency. This follows semantic versioning best practices: a security fix in a critical dependency warrants at least a minor version bump.

Prevention & Best Practices

1. Dependency Scanning in CI/CD

The vulnerability was detected using Trivy, a static vulnerability scanner. Integrate Trivy or similar tools into your CI/CD pipeline:

trivy fs . --severity HIGH,CRITICAL

This catches known vulnerabilities before code reaches production.

2. Automated Dependency Updates

Use tools like Dependabot or Renovate to automatically open pull requests for security updates:
- Renovate can auto-merge security patches
- Dependabot provides detailed vulnerability reports
- Both integrate with GitHub, GitLab, and other platforms

3. Resource Limits During File Processing

Even with updated dependencies, implement application-level safeguards:

// Limit memory usage during ZIP processing
const { Worker } = require('worker_threads');
const vm = require('vm');

function processZipWithTimeout(zipPath, timeoutMs = 5000) {
  return new Promise((resolve, reject) => {
    const timeout = setTimeout(() => {
      reject(new Error('ZIP processing timeout'));
    }, timeoutMs);

    try {
      const zip = new AdmZip(zipPath);
      clearTimeout(timeout);
      resolve(zip);
    } catch (err) {
      clearTimeout(timeout);
      reject(err);
    }
  });
}

4. Input Validation

Before processing ZIP files, validate:
- File size limits: Reject files exceeding a threshold
- Entry count limits: Limit the number of files in a ZIP
- Path length validation: Reject entries with suspiciously long paths

function validateZipFile(zipPath, maxSize = 100 * 1024 * 1024) {
  const fs = require('fs');
  const stats = fs.statSync(zipPath);

  if (stats.size > maxSize) {
    throw new Error(`ZIP file exceeds maximum size of ${maxSize} bytes`);
  }

  const zip = new AdmZip(zipPath);
  if (zip.getEntries().length > 10000) {
    throw new Error('ZIP file contains too many entries');
  }

  for (const entry of zip.getEntries()) {
    if (entry.entryName.length > 500) {
      throw new Error(`Entry name exceeds maximum length: ${entry.entryName}`);
    }
  }
}

5. References to Security Standards

This vulnerability relates to:
- CWE-400: Uncontrolled Resource Consumption ('Resource Exhaustion')
- OWASP A01:2021: Broken Access Control (applies to resource exhaustion scenarios)
- OWASP DoS Prevention Cheat Sheet: Covers resource limit strategies

Key Takeaways

  • Brace expansion in adm-zip 0.5.17 had no upper bounds, allowing attackers to craft ZIP files that expand into billions of paths and exhaust application memory
  • The fix enforces maximum expansion limits: adm-zip 0.6.0 rejects or truncates expansions exceeding a threshold, preventing memory exhaustion
  • Dependency scanning caught this before production: Trivy detected CVE-2026-39244 in the dependency tree, enabling proactive remediation
  • Explicit overrides ensure consistency: The "overrides" entry in bun.lock guarantees all transitive dependencies use the fixed version
  • Resource limits are a defense-in-depth strategy: Application-level timeouts and file validation provide additional protection even with updated dependencies

How Orbis AppSec Detected This

Source: ZIP file paths within user-uploaded archives (untrusted input from file system operations)

Sink: Brace expansion logic in adm-zip's path processing during decompression

Missing control: Bounds checking on expansion size; no maximum limit on the number of resulting path combinations

CWE: CWE-400 (Uncontrolled Resource Consumption)

Fix: Upgrade adm-zip from 0.5.17 to 0.6.0, which implements maximum expansion length limits and validates input before 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

The adm-zip DoS vulnerability (CVE-2026-39244) illustrates how even well-maintained open-source libraries can harbor resource exhaustion vulnerabilities. By implementing unbounded brace expansion without limits, adm-zip 0.5.17 created a denial-of-service vector that attackers could exploit with a single malicious ZIP file.

The CortexKit project's rapid upgrade to adm-zip 0.6.0 demonstrates best practices in secure dependency management:
1. Monitor for vulnerabilities using automated scanning (Trivy)
2. Update promptly when security releases are available
3. Communicate the fix through version bumps and explicit overrides
4. Implement defense-in-depth with application-level resource limits

For developers working with ZIP file processing, file uploads, or archive handling, this vulnerability serves as a reminder: always validate resource consumption during decompression, implement timeout mechanisms, and keep your dependencies updated. Unbounded operations on untrusted input are a recipe for denial-of-service vulnerabilities.


References

Frequently Asked Questions

What is unbounded brace expansion in ZIP processing?

Brace expansion is a pattern-matching feature that converts expressions like `{a,b,c}` into multiple strings. Without bounds, attackers can craft ZIP files with deeply nested or exponentially expanding braces that consume all available memory during decompression.

How do you prevent this vulnerability in Node.js applications?

Keep dependencies updated, use dependency scanning tools (like Trivy), implement resource limits on file processing operations, and validate ZIP files before decompression.

What CWE is this vulnerability?

CWE-400 (Uncontrolled Resource Consumption), which covers scenarios where applications fail to properly limit resource usage when processing untrusted input.

Is simply validating file size enough to prevent this?

No. File size validation alone is insufficient because brace expansion can multiply a small compressed file into enormous memory consumption during decompression, bypassing size checks.

Can static analysis detect this vulnerability?

Yes. Trivy (as used in this fix) and similar SAST tools can flag known vulnerable versions of dependencies. However, detecting novel expansion patterns requires runtime analysis or fuzzing.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #359

Related Articles

high

How unsafe pickle deserialization happens in NumPy's np.load() and how to fix it

A high-severity arbitrary code execution vulnerability was discovered in `tools/ardy-engine/retarget.py` where `np.load()` was called with `allow_pickle=True`, enabling attackers to embed malicious pickle payloads in `.npz` files. The fix was a single-character change—switching `allow_pickle=True` to `allow_pickle=False`—that eliminates the deserialization attack vector while preserving the file's legitimate array data loading functionality.

high

How pickle-based arbitrary code execution happens in PyTorch and how to fix it

A high-severity arbitrary code execution vulnerability was discovered in `scripts/export_joyvasa_audio.py` where `torch.load()` was called with `weights_only=False`, allowing any pickle-serialized Python object — including malicious code — to execute during checkpoint loading. The fix switches to `weights_only=True` and explicitly allowlists only the two non-standard classes the checkpoint actually requires: `argparse.Namespace` and `pathlib.PosixPath`. This closes a real code execution path tha

critical

How unsafe token deserialization happens in Node.js Temml parser and how to fix it

A critical vulnerability in the Temml math library's parser allowed unsafe token deserialization that could lead to remote code execution when processing user-supplied mathematical expressions. The fix adds strict type validation on fetched token properties before use, preventing exploitation of malformed or crafted payloads.

high

How unsafe pickle deserialization happens in Keras/TensorFlow notebooks and how to fix it

A high-severity untrusted deserialization vulnerability was discovered in `TransferLearningTF.ipynb`, a transfer learning tutorial notebook that loads VGG16 model weights from the internet without verifying their integrity. Because Keras relies on Python's pickle-based serialization format under the hood, a tampered or substituted weights file could execute arbitrary code with the full privileges of the notebook user. The fix adds a SHA-256 checksum verification step immediately after the weight

high

How DoS via sparse array deserialization happens in Svelte devalue and how to fix it

A high-severity vulnerability (CVE-2026-42570) was discovered in the devalue library version 5.7.1, used by the Astro-powered website. This vulnerability allowed attackers to trigger denial-of-service conditions through maliciously crafted sparse arrays during deserialization. The fix involved upgrading devalue from 5.7.1 to 5.8.1, which implements proper safeguards against sparse array exploitation.

high

How javascript.express.security.audit.express-check-csurf-middleware-usage.express-check-csurf-middleware-usage happens in Express.js and how to fix it

An Express.js application in `src/server.js` was missing CSRF (Cross-Site Request Forgery) protection middleware, leaving all state-changing endpoints vulnerable to forged requests from malicious sites. The fix introduces the `csrf` package to generate and validate tokens on non-GET requests, while exempting API-key-authenticated clients. This defensive hardening raises the bar against automated exploit chaining.