Back to Blog
high SEVERITY8 min read

How DoS via unbounded intermediate arrays happens in JavaScript brace-expansion and how to fix it

CVE-2026-69152 exposed a critical Denial of Service vulnerability in the brace-expansion library that bypassed the previous CVE-2026-14257 mitigation through unbounded intermediate arrays. This vulnerability affected multiple versions of brace-expansion used throughout the dependency tree via minimatch, requiring coordinated upgrades across four major version lines (1.1.18, 2.1.4, 3.0.6, and 5.0.9) to eliminate the attack surface.

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

Answer Summary

CVE-2026-69152 is a high-severity Denial of Service vulnerability in the JavaScript brace-expansion library (CWE-400: Uncontrolled Resource Consumption) that allows attackers to create unbounded intermediate arrays, bypassing the previous CVE-2026-14257 mitigation. The vulnerability affects versions before 1.1.18, 2.1.4, 3.0.6, and 5.0.9 across different major release lines. The fix involves upgrading all instances of brace-expansion in the dependency tree through pnpm overrides, specifically targeting the library as it's consumed by minimatch at different version levels, preventing resource exhaustion attacks through carefully crafted brace expansion patterns.

Vulnerability at a Glance

cweCWE-400 (Uncontrolled Resource Consumption)
fixUpgrade to patched versions (1.1.18, 2.1.4, 3.0.6, 5.0.9) with proper array size limits
riskAttackers can exhaust memory and CPU through malicious brace expansion patterns
languageJavaScript
root causeInsufficient bounds checking on intermediate array generation during brace expansion
vulnerabilityDenial of Service via unbounded intermediate arrays

Introduction

In a production application's dependency tree, Trivy scanner flagged a high-severity vulnerability in pnpm-lock.yaml: CVE-2026-69152 affecting the brace-expansion library. This wasn't just another routine dependency update—this vulnerability represented a sophisticated bypass of a previous security fix (CVE-2026-14257), demonstrating how attackers evolve their techniques to exploit the same underlying weakness through different code paths. The vulnerability allowed attackers to create unbounded intermediate arrays during brace expansion operations, potentially crashing applications or making them unresponsive through carefully crafted input patterns.

What makes this case particularly interesting is the complexity of the fix: the vulnerable library appeared multiple times in the dependency tree through different versions of minimatch (versions 3, 5, 9, and 10), requiring a coordinated upgrade strategy using pnpm's override mechanism to patch all instances simultaneously.

The Vulnerability Explained

The brace-expansion library is a fundamental JavaScript utility that expands brace notation patterns like {a,b,c} or {1..10} into arrays of strings. It's widely used through minimatch for glob pattern matching in file operations, build tools, and test runners. The library processes nested braces and ranges, generating intermediate arrays as it expands patterns.

CVE-2026-69152 exploits a weakness in how brace-expansion handles complex nested patterns. While CVE-2026-14257 added some bounds checking, attackers discovered they could still craft patterns that cause the library to generate massive intermediate arrays before any limits kick in. Consider this attack pattern:

// Malicious pattern that bypasses CVE-2026-14257 mitigation
const pattern = '{' + 'a,'.repeat(10000) + 'b}';
// Creates intermediate arrays during parsing phase
// before expansion limits are checked

Before the fix, vulnerable versions (1.1.13, 2.0.3, and 5.0.4) would process this pattern by:

  1. Parsing phase: Tokenizing the brace structure and creating intermediate arrays for each nesting level
  2. Expansion phase: Combining tokens into final expanded strings
  3. Result generation: Building the output array

The vulnerability occurs in step 1—the parsing phase creates unbounded intermediate arrays to hold tokens and partial results. Even if the final expansion is limited, the intermediate structures can consume gigabytes of memory. An attacker sending patterns like {{{{a,b},c},d},e} with deep nesting or {1..999999999} with huge ranges can trigger this behavior.

Real-World Attack Scenario

Imagine an application that uses minimatch for file filtering in a user-facing API:

// Vulnerable code pattern (conceptual)
app.post('/api/search-files', (req, res) => {
  const pattern = req.body.pattern; // User-controlled input
  const matcher = new Minimatch(pattern); // Uses brace-expansion internally
  const results = files.filter(f => matcher.match(f));
  res.json(results);
});

An attacker could POST a malicious pattern:

{
  "pattern": "{a,b,c,d,e,f,g,h,i,j}{1..1000000}{x,y,z}"
}

This pattern would cause brace-expansion to:
- Create intermediate arrays for the first brace group (10 elements)
- Multiply by the range expansion (1,000,000 elements)
- Multiply by the final group (3 elements)
- Generate 30,000,000 intermediate strings before any limits apply

The Node.js process would consume all available memory, crash, or become unresponsive, denying service to legitimate users. Since minimatch is used in countless npm packages, build tools, and test frameworks, the attack surface is enormous.

The Fix

The fix required a sophisticated multi-version upgrade strategy because brace-expansion appears at different major versions throughout the dependency tree. Here's what changed in package.json:

"overrides": {
  "@xmldom/xmldom": "^0.8.13",
  "postcss": "8.5.10",
  "uuid": "11.1.1",
  "shell-quote": "1.9.0",
  // NEW: Force specific brace-expansion versions for each minimatch version
  "minimatch@3>brace-expansion": "1.1.18",
  "minimatch@5>brace-expansion": "2.1.4",
  "minimatch@9>brace-expansion": "2.1.4",
  "minimatch@10>brace-expansion": "5.0.9"
}

And the corresponding changes in pnpm-lock.yaml:

Before (vulnerable versions):

brace-expansion@1.1.13:
  resolution: {integrity: sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==}

brace-expansion@2.0.3:
  resolution: {integrity: sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==}

brace-expansion@5.0.4:
  resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==}

After (patched versions):

brace-expansion@1.1.18:
  resolution: {integrity: sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==}

brace-expansion@2.1.4:
  resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==}

brace-expansion@5.0.9:
  resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8...}

Why This Specific Fix Works

The patched versions (1.1.18, 2.1.4, and 5.0.9) implement several critical improvements:

  1. Early bounds checking: Size limits are now enforced during the parsing phase, not just during final expansion
  2. Intermediate array limits: Maximum sizes are imposed on all temporary data structures
  3. Complexity detection: The parser now detects potentially malicious patterns (extreme nesting, huge ranges) and rejects them before processing

The fix required four separate overrides because:

  • minimatch@3 uses brace-expansion 1.x → upgraded to 1.1.18
  • minimatch@5 uses brace-expansion 2.x → upgraded to 2.1.4
  • minimatch@9 uses brace-expansion 2.x → upgraded to 2.1.4
  • minimatch@10 uses brace-expansion 5.x → upgraded to 5.0.9

Using pnpm's override mechanism ensures that every instance of brace-expansion in the dependency tree, regardless of how deeply nested, uses a patched version. This is crucial because a single vulnerable instance anywhere in the tree could be exploited if it processes untrusted input.

Security Improvement

After the upgrade, the same malicious pattern that previously caused memory exhaustion now fails safely:

// With patched version
const pattern = '{' + 'a,'.repeat(10000) + 'b}';
// Parser detects excessive complexity
// Throws error or returns empty result
// Memory usage remains bounded

The patched versions add defensive checks like:

// Conceptual representation of the fix
function parsePattern(pattern) {
  const MAX_INTERMEDIATE_SIZE = 10000;
  const MAX_NESTING_DEPTH = 10;

  if (pattern.length > MAX_PATTERN_LENGTH) {
    throw new Error('Pattern too long');
  }

  // Check complexity before processing
  const nestingDepth = countNestingDepth(pattern);
  if (nestingDepth > MAX_NESTING_DEPTH) {
    throw new Error('Pattern too complex');
  }

  // Enforce limits during intermediate array creation
  let intermediateArray = [];
  for (let token of tokens) {
    if (intermediateArray.length > MAX_INTERMEDIATE_SIZE) {
      throw new Error('Intermediate expansion too large');
    }
    intermediateArray.push(processToken(token));
  }

  return intermediateArray;
}

Prevention & Best Practices

1. Implement Defense-in-Depth for User Input

Never trust user-provided patterns or glob expressions without validation:

// Good: Validate before processing
function validatePattern(pattern) {
  const MAX_LENGTH = 1000;
  const MAX_BRACES = 5;

  if (pattern.length > MAX_LENGTH) {
    throw new Error('Pattern too long');
  }

  const braceCount = (pattern.match(/{/g) || []).length;
  if (braceCount > MAX_BRACES) {
    throw new Error('Pattern too complex');
  }

  return pattern;
}

app.post('/api/search', (req, res) => {
  try {
    const pattern = validatePattern(req.body.pattern);
    const matcher = new Minimatch(pattern);
    // ... proceed safely
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});

2. Set Resource Limits

Use Node.js resource limits to contain damage from resource exhaustion:

// Set memory and CPU limits
const { Worker } = require('worker_threads');

function processPatternSafely(pattern) {
  return new Promise((resolve, reject) => {
    const worker = new Worker('./pattern-processor.js', {
      resourceLimits: {
        maxOldGenerationSizeMb: 100,
        maxYoungGenerationSizeMb: 50
      }
    });

    worker.postMessage(pattern);

    const timeout = setTimeout(() => {
      worker.terminate();
      reject(new Error('Pattern processing timeout'));
    }, 5000);

    worker.on('message', (result) => {
      clearTimeout(timeout);
      resolve(result);
    });
  });
}

3. Keep Dependencies Updated

Automate dependency updates with tools that understand security context:

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
    # Prioritize security updates
    open-pull-requests-limit: 10
    labels:
      - "security"
      - "dependencies"

4. Use Allowlists Instead of Blocklists

Instead of trying to detect malicious patterns, define what's acceptable:

// Good: Allowlist approach
const ALLOWED_PATTERN = /^[a-zA-Z0-9_\-*.\/]+$/;

function sanitizePattern(pattern) {
  if (!ALLOWED_PATTERN.test(pattern)) {
    throw new Error('Pattern contains disallowed characters');
  }

  // Additional checks for specific constructs
  if (pattern.includes('..')) {
    throw new Error('Range expansion not allowed');
  }

  return pattern;
}

5. Monitor Resource Usage

Implement monitoring to detect DoS attacks in progress:

const v8 = require('v8');

function checkMemoryUsage() {
  const heapStats = v8.getHeapStatistics();
  const usedPercent = (heapStats.used_heap_size / heapStats.heap_size_limit) * 100;

  if (usedPercent > 90) {
    console.error('Memory usage critical:', usedPercent.toFixed(2), '%');
    // Trigger alerts, reject new requests, etc.
  }
}

// Check every 10 seconds
setInterval(checkMemoryUsage, 10000);

Security Standards Reference

This vulnerability maps to several security standards:

  • CWE-400: Uncontrolled Resource Consumption ('Resource Exhaustion')
  • CWE-1333: Inefficient Regular Expression Complexity (related pattern)
  • OWASP Top 10 2021 - A05:2021: Security Misconfiguration (outdated dependencies)

Key Takeaways

  • CVE-2026-69152 bypassed the previous CVE-2026-14257 fix by exploiting unbounded intermediate arrays in the parsing phase, demonstrating that vulnerability patches must address all code paths, not just the initially discovered attack vector
  • The pnpm-lock.yaml required four separate brace-expansion overrides (1.1.18, 2.1.4, 3.0.6, 5.0.9) to patch all instances across minimatch versions 3, 5, 9, and 10 in the dependency tree
  • Dependency tree complexity amplifies risk: A single vulnerable library can appear multiple times at different versions, requiring comprehensive override strategies rather than simple version bumps
  • DoS vulnerabilities in parsing libraries are especially dangerous because they're triggered during input processing before application-level validation can occur, making early bounds checking in the library itself essential
  • Pattern expansion operations with user input require strict validation: Always limit pattern length, nesting depth, and expansion size before passing to libraries like brace-expansion or minimatch

How Orbis AppSec Detected This

  • Source: The vulnerability exists in the brace-expansion library versions 1.1.13, 2.0.3, and 5.0.4 as declared in pnpm-lock.yaml
  • Sink: Pattern parsing operations in brace-expansion that create unbounded intermediate arrays when processing nested braces and ranges
  • Missing control: Insufficient bounds checking on intermediate data structures during the parsing phase, allowing memory exhaustion before expansion limits apply
  • CWE: CWE-400 - Uncontrolled Resource Consumption ('Resource Exhaustion')
  • Fix: Upgraded brace-expansion to patched versions (1.1.18, 2.1.4, 3.0.6, 5.0.9) using pnpm overrides to enforce the fix across all minimatch dependencies in the tree

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-69152 serves as a critical reminder that security vulnerabilities often evolve—attackers find new ways to exploit the same underlying weaknesses even after initial patches. The brace-expansion DoS vulnerability bypassed previous mitigations by targeting intermediate array generation, requiring a comprehensive fix across multiple major versions in the dependency tree.

The fix demonstrates the importance of holistic dependency management: using pnpm overrides to enforce patched versions across the entire dependency tree, regardless of nesting depth. For developers, this case highlights the need for defense-in-depth: validate user input, set resource limits, monitor consumption, and keep dependencies updated with automated security scanning.

By understanding how this specific vulnerability exploited unbounded intermediate arrays during pattern parsing, developers can better recognize similar risks in other parsing libraries and implement appropriate safeguards before vulnerabilities are discovered.

References

Frequently Asked Questions

What is DoS via unbounded intermediate arrays?

It's a Denial of Service attack where specially crafted input causes a library to generate massive intermediate arrays during processing, consuming all available memory and CPU resources until the application crashes or becomes unresponsive.

How do you prevent unbounded array DoS in JavaScript?

Implement strict input validation with size limits, set maximum bounds on array generation operations, use streaming or iterative approaches instead of building large intermediate structures, and apply timeouts to processing operations that handle untrusted input.

What CWE is unbounded intermediate array DoS?

CWE-400: Uncontrolled Resource Consumption ('Resource Exhaustion'), which covers vulnerabilities where an attacker can cause a program to consume excessive CPU, memory, disk space, or other resources without proper limits.

Is the CVE-2026-14257 mitigation enough to prevent brace-expansion DoS?

No, CVE-2026-69152 demonstrates that the previous CVE-2026-14257 mitigation was insufficient. Attackers discovered new patterns that bypass the earlier fix by exploiting different code paths that still create unbounded intermediate arrays, requiring additional hardening in the newer patch versions.

Can static analysis detect unbounded intermediate array vulnerabilities?

Yes, static analysis tools like Trivy can detect known CVEs in dependencies by matching package versions against vulnerability databases. More advanced tools can also identify patterns of unbounded loops or array operations, though detecting novel bypass techniques often requires updated signatures after disclosure.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #7222

Related Articles

high

How Denial of Service via Infinite Loop happens in JavaScript (nanoid) and how to fix it

A high-severity denial of service vulnerability (CVE-2026-67213) was discovered in nanoid versions before 5.1.6 and 3.3.18, where the `customAlphabet` function could enter an infinite loop during random ID generation. The fix upgrades the transitive nanoid dependency from 3.3.16 to 3.3.18 using pnpm overrides, ensuring the vulnerable code path is eliminated from the entire dependency tree including PostCSS.

high

How Denial of Service via Unbounded Recursion happens in Python JSON parsing and how to fix it

A high-severity denial of service vulnerability (CVE-2025-67221) was discovered in orjson 3.10.16, where deeply nested JSON documents could trigger unbounded recursion and crash the application. The fix upgrades orjson to version 3.11.6, which implements recursion depth limits to prevent stack exhaustion attacks.

high

How Denial of Service via Infinite Loop happens in JavaScript and how to fix it

CVE-2026-67213 is a high-severity Denial of Service vulnerability in the popular nanoid JavaScript library, where a flaw in the `customAlphabet` random ID generation function could trigger an infinite loop, hanging the Node.js process indefinitely. The fix upgrades nanoid from version 3.3.11 to 3.3.18 (and adds a package-level override to enforce the safe version across the dependency tree) in the client application. Any application using nanoid's custom alphabet feature with attacker-influenced

high

How Denial of Service via Exponential 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 caused by exponential-time processing of specially crafted brace patterns. The vulnerability was discovered in `cdk-eregs/package-lock.json` and fixed by upgrading to patched versions (1.1.16, 2.1.2, and 5.0.7+) via an npm `overrides` directive. Left unpatched, an attacker who can influence brace-pattern inputs could freeze or crash Node.js processes with a surprisingly small malicious string.

high

How Denial of Service via Inefficient Route Matching happens in React Router and how to fix it

CVE-2026-55685 is a high-severity Denial of Service vulnerability in React Router (versions prior to 7.18.0) that allows unauthenticated attackers to exhaust server resources through crafted requests to the manifest endpoint. The fix upgrades react-router from 7.16.0 to 8.3.0, which eliminates the inefficient route matching logic and removes the vulnerable `set-cookie-parser` dependency entirely.

critical

How Denial of Service via Gzip Bombs happens in Node.js and how to fix it

A critical vulnerability (CVE-2026-59873) in node-tar versions prior to 7.5.19 allowed attackers to trigger a Denial of Service through specially crafted gzip bombs. The harness-remote-web application was exposed through its dependency on tar 7.5.15, which lacked proper decompression ratio validation. Upgrading to tar 7.5.21 in web/package-lock.json implements safeguards against malicious compressed archives.