Back to Blog
high SEVERITY5 min read

How Exponential-Time Complexity Causes Denial of Service in brace-expansion and How to Fix It

A critical vulnerability in brace-expansion versions 1.1.13 and earlier allowed attackers to cause denial of service through crafted brace pattern inputs. The fix upgrades to patched versions 1.1.16, 2.1.2, and 5.0.7, eliminating the exponential-time complexity that made exploitation possible.

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

Answer Summary

CVE-2026-13149 is a Regular Expression Denial of Service (ReDoS) vulnerability in brace-expansion 1.1.13, a JavaScript library used for brace pattern matching in glob expressions. The vulnerability stems from exponential-time complexity in brace pattern parsing (CWE-400: Uncontrolled Resource Consumption). The fix upgrades brace-expansion to patched versions 1.1.16, 2.1.2, and 5.0.7 using npm overrides, which implement algorithmic improvements to prevent catastrophic backtracking on malicious inputs.

Vulnerability at a Glance

cweCWE-400 (Uncontrolled Resource Consumption)
fixUpgrade brace-expansion to patched versions (1.1.16, 2.1.2, 5.0.7) via npm overrides
riskDenial of Service through CPU exhaustion
languageJavaScript/Node.js
root causeExponential-time complexity in brace pattern expansion algorithm
vulnerabilityRegular Expression Denial of Service (ReDoS) / Algorithmic Complexity Attack

Introduction

In a production web application handling user-influenced file patterns, we discovered a HIGH severity denial of service vulnerability lurking in package-lock.json. The brace-expansion package at version 1.1.13—a dependency likely pulled in through glob-matching utilities—contained an algorithmic flaw that could freeze your entire Node.js process with a single malicious input.

The vulnerable code in node_modules/brace-expansion at line 9603 of package-lock.json showed:

"node_modules/brace-expansion": {
  "version": "1.1.13",
  "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
  "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",

This version string—1.1.13—is the smoking gun. When brace-expansion processes nested brace patterns, the algorithm's time complexity grows exponentially with input depth. For a web application accepting user-provided glob patterns, this creates a trivial denial of service vector.

The Vulnerability Explained

What brace-expansion Does

Brace-expansion is a core JavaScript library that expands brace patterns like file-{1,2,3}.txt into file-1.txt, file-2.txt, file-3.txt. It's a dependency of glob, minimatch, and countless other packages—making this vulnerability extremely widespread.

The Algorithmic Flaw

The vulnerability in version 1.1.13 stems from exponential-time complexity in the brace expansion algorithm. Consider this pattern:

{a,b}{a,b}{a,b}{a,b}{a,b}{a,b}{a,b}{a,b}{a,b}{a,b}

Each nested brace doubles the computation. With 10 levels, this generates 2^10 = 1,024 combinations. With 25 levels, you get 33,554,432 combinations. The algorithm doesn't implement:

  • Depth limiting
  • Memoization to avoid redundant computations
  • Early termination on excessive expansion

Real-World Attack Scenario

In our web application context, imagine an endpoint accepting a pattern parameter for file search:

// Hypothetical vulnerable endpoint
app.get('/search', (req, res) => {
  const pattern = req.query.pattern;  // User-controlled!
  const files = glob.sync(pattern);   // Uses brace-expansion internally
  res.json(files);
});

An attacker sends:

GET /search?pattern={a,b}{a,b}{a,b}{a,b}{a,b}{a,b}{a,b}{a,b}{a,b}{a,b}{a,b}{a,b}{a,b}{a,b}{a,b}{a,b}{a,b}{a,b}{a,b}{a,b}

The server hangs. CPU spikes to 100%. Other requests timeout. Depending on the Node.js configuration, this could crash the process or exhaust resources until manual intervention.

The package-lock.json entry at line 9603 confirmed this vulnerable version was in the production dependency tree—not devDependencies, meaning it reached actual users.

The Fix

The remediation involved two coordinated changes to enforce patched versions across the dependency tree.

Change 1: Direct Dependency Update in package-lock.json

Before (vulnerable):

"node_modules/brace-expansion": {
  "version": "1.1.13",
  "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
  "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",

After (patched):

"node_modules/brace-expansion": {
  "version": "1.1.16",
  "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
  "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",

Version 1.1.16 (and the parallel releases 2.1.2 and 5.0.7) implement algorithmic fixes:

  • Linear-time expansion: Restructured recursion to avoid exponential blowup
  • Depth limiting: Maximum brace nesting prevents abuse
  • Early termination: Stops expansion when output would exceed reasonable bounds

Change 2: npm Overrides in package.json

The package.json received a critical addition at line 93:

,
  "overrides": {
    "brace-expansion": "1.1.16"
  }
}

This overrides field (npm 8.3+) forces all transitive dependencies to use the specified version, regardless of what their own package.json requests. This is essential because:

  • glob might require brace-expansion ^1.1.0
  • minimatch might require brace-expansion ^2.0.0
  • Other packages might pull in vulnerable versions

Without overrides, you'd need to wait for every upstream maintainer to update. The override cuts through the dependency tree immediately.

Why Three Versions?

The PR mentions 5.0.7, 1.1.16, and 2.1.2 because brace-expansion has multiple major version lines in active use:

Version Line Use Case Fix Version
1.x Legacy glob/minimatch 1.1.16
2.x Modern minimatch 2.1.2
5.x Current standalone 5.0.7

The overrides in this project targets 1.1.16 specifically for the 1.x line used by its direct dependencies.

Prevention & Best Practices

Dependency Management

  1. Audit regularly: Run npm audit and tools like Trivy in CI/CD
  2. Pin with purpose: Use exact versions for security-critical packages
  3. Override aggressively: Don't wait for upstream—use overrides or resolutions (Yarn) to force security patches

Input Handling

// Defensive pattern for user-provided globs
const MAX_PATTERN_LENGTH = 500;
const MAX_BRACE_DEPTH = 5;

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

  const braceDepth = (pattern.match(/\{/g) || []).length;
  if (braceDepth > MAX_BRACE_DEPTH) {
    throw new Error('Excessive brace nesting');
  }

  return pattern;
}

Detection Tools

Tool Command Purpose
Trivy trivy fs . Scans package-lock.json for CVEs
npm audit npm audit Native Node.js vulnerability check
Snyk snyk test Dependency vulnerability scanning
Semgrep semgrep --config=auto Custom ReDoS pattern detection

Standards & References

  • CWE-400: Uncontrolled Resource Consumption
  • CWE-1333: Inefficient Regular Expression Complexity (related)
  • OWASP: ReDoS Cheat Sheet

Key Takeaways

  • The package-lock.json at line 9603 contained brace-expansion@1.1.13, which has exponential-time complexity on nested brace patterns—always audit your lockfile, not just package.json
  • Use npm overrides (or Yarn resolutions) to force security patches when upstream dependencies lag, rather than waiting for transitive updates
  • Algorithmic complexity attacks bypass traditional input validation—the input looks valid but consumes excessive resources; depth limits and timeouts are essential
  • Multiple major version lines require parallel fixes—brace-expansion 1.x, 2.x, and 5.x all needed separate patches, so verify which line your project uses
  • Production code assessment matters—Trivy correctly flagged this as "likely exploitable" because the dependency was in the production dependency tree, not dev-only

How Orbis AppSec Detected This

Source: User-influenced input entering through HTTP request parameters (e.g., pattern query parameter) that flow into glob-matching operations

Sink: The brace-expansion package's pattern expansion algorithm in node_modules/brace-expansion/index.js, invoked through transitive dependencies like glob or minimatch

Missing control: No input length limits, no brace nesting depth validation, and no execution timeouts on pattern expansion operations; the package-lock.json contained the vulnerable version 1.1.13 without overrides

CWE: CWE-400: Uncontrolled Resource Consumption (Algorithmic Complexity)

Fix: Upgraded brace-expansion to patched versions 1.1.16, 2.1.2, and 5.0.7 and added an overrides entry in package.json to force all transitive dependencies onto secure versions

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-13149 exemplifies how a seemingly simple utility library can become a critical attack vector. The brace-expansion vulnerability didn't require exotic exploits—just a basic understanding of algorithmic complexity and a crafted HTTP request. The fix demonstrates modern dependency management best practices: don't just upgrade direct dependencies, use overrides to secure your entire dependency tree immediately.

For Node.js developers, this is a reminder that package-lock.json is a security surface. Tools like Trivy caught this vulnerability in CI, and automated fixes like Orbis AppSec can apply the patches before attackers exploit them. Keep your dependencies updated, implement defense-in-depth with input limits, and never underestimate the damage from exponential complexity.


References

Frequently Asked Questions

What is brace-expansion ReDoS?

A denial of service vulnerability where specially crafted brace patterns (like `{a,b}{a,b}{a,b}...`) cause exponential computation time, freezing the application.

How do you prevent ReDoS in JavaScript?

Use libraries with linear-time algorithms, implement input length limits, apply timeouts on pattern matching, and keep dependencies updated.

What CWE is brace-expansion ReDoS?

CWE-400: Uncontrolled Resource Consumption, specifically through algorithmic complexity.

Is input validation alone enough to prevent brace-expansion ReDoS?

No, while helpful, the root fix is upgrading to patched library versions that fix the underlying algorithmic flaw.

Can static analysis detect brace-expansion ReDoS?

Yes, scanners like Trivy can detect vulnerable dependency versions in package-lock.json and package.json files.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #736

Related Articles

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 dependabot-missing-cooldown happens in GitHub Actions/Node.js and how to fix it

The repository's `.github/dependabot.yml` had no cooldown period configured, meaning Dependabot could immediately propose updates to newly published package versions with zero time for the community to flag malware or instability. The fix adds a `cooldown` block with `default-days: 7` to both the `npm` and `github-actions` ecosystems, forcing a 7-day waiting period before new releases are surfaced as update PRs.

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.

critical

How Remote Code Execution Happens in Handlebars Template Compilation and How to Fix It

CVE-2026-33937 is a critical remote code execution vulnerability in Handlebars.js that allows attackers to execute arbitrary code by passing maliciously crafted Abstract Syntax Tree (AST) objects to the compile() function. The vulnerability was patched in version 4.7.9, and we've upgraded to protect against this threat vector.