Back to Blog
high SEVERITY6 min read

How Denial of Service via Exponential Time Complexity happens in brace-expansion and how to fix it

A high-severity Denial of Service vulnerability (CVE-2026-13149) was discovered in the brace-expansion npm package, where specially crafted input patterns could trigger exponential time complexity, potentially freezing Node.js applications. The fix upgrades multiple versions of brace-expansion (1.1.18 → 1.1.16, 2.1.1 → 2.1.2, and 5.0.6 → 5.0.7) through yarn resolutions to ensure all dependency paths use patched versions.

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

Answer Summary

CVE-2026-13149 is a Denial of Service vulnerability in the brace-expansion npm package (CWE-1333: Inefficient Regular Expression Complexity) where malicious brace patterns cause exponential processing time. The vulnerability affects versions before 1.1.16, 2.1.2, and 5.0.7. Fix it by adding yarn resolutions in package.json to force all transitive dependencies to use patched versions: `"brace-expansion@npm:^1.1.7": "1.1.16"`, `"brace-expansion@npm:^2.0.1": "2.1.2"`, and `"brace-expansion@npm:^5.0.5": "5.0.7"`.

Vulnerability at a Glance

cweCWE-1333 (Inefficient Regular Expression Complexity)
fixUpgrade brace-expansion to versions 1.1.16, 2.1.2, or 5.0.7 via yarn resolutions
riskApplication freeze or crash from malicious input patterns
languageJavaScript/Node.js
root causeExponential time algorithm in brace expansion parsing
vulnerabilityDenial of Service via Exponential Time Complexity

Introduction

In this project's dependency tree, Trivy flagged a high-severity vulnerability lurking in yarn.lock: the brace-expansion package at versions 1.1.18, 2.1.1, and 5.0.6 contained CVE-2026-13149—a Denial of Service vulnerability caused by exponential time complexity in the brace expansion algorithm.

The brace-expansion package is a foundational dependency used by glob matching libraries like minimatch and micromatch, which in turn power countless build tools, file watchers, and CLI utilities. When an attacker can influence input that flows through brace expansion—such as file patterns in configuration or user-provided glob strings—they could craft patterns that cause the application to hang indefinitely.

Looking at the vulnerable dependency chain in yarn.lock:

"brace-expansion@npm:^1.1.7":
  version: 1.1.18

"brace-expansion@npm:^2.0.1, brace-expansion@npm:^2.0.2":
  version: 2.1.1

"brace-expansion@npm:^5.0.5":
  version: 5.0.6

Multiple semver ranges were resolving to vulnerable versions, creating several attack vectors throughout the dependency tree.

The Vulnerability Explained

Brace expansion is a shell-like feature that expands patterns like {a,b,c} into a b c or {1..5} into 1 2 3 4 5. The brace-expansion npm package implements this functionality for JavaScript applications.

How Exponential Complexity Attacks Work

The vulnerability occurs when the parsing algorithm processes deeply nested or specially crafted brace patterns. Consider a pattern like:

{a{b{c{d{e{f{g{h{i{j}}}}}}}}}

Each level of nesting can cause the algorithm to explore an exponentially growing number of combinations. In vulnerable versions, the algorithm lacks proper safeguards against this explosion, leading to:

  1. CPU exhaustion: The event loop blocks while processing the malicious pattern
  2. Memory pressure: Intermediate results accumulate exponentially
  3. Application freeze: The Node.js process becomes unresponsive

Real-World Attack Scenario

Imagine this application uses a glob library (which depends on brace-expansion) to process user-provided file patterns—perhaps in a file upload feature, build configuration, or search functionality:

const minimatch = require('minimatch');

// User-provided pattern from API request
const userPattern = req.body.filePattern;

// This could hang if userPattern contains malicious braces
const matches = files.filter(f => minimatch(f, userPattern));

An attacker could submit a pattern like {,,,,,,,,,,,,,,,,,,,,,,,,,} or deeply nested braces, causing the server to freeze. Even a single malicious request could take down the entire Node.js process.

Why This Is High Severity

The vulnerability is rated HIGH because:
- Low attack complexity: Crafting malicious input is trivial
- No authentication required: Any input path that reaches brace expansion is vulnerable
- Full availability impact: The application becomes completely unresponsive
- Wide attack surface: brace-expansion is a transitive dependency of many popular packages

The Fix

The fix uses Yarn's resolution feature to force all semver ranges to resolve to patched versions. Here's the before and after:

Before (package.json)

"resolutions": {
  "uuid": "^14.0.0",
  "yargs": "^18.1.0"
}

After (package.json)

"resolutions": {
  "uuid": "^14.0.0",
  "yargs": "^18.1.0",
  "brace-expansion@npm:^1.1.7": "1.1.16",
  "brace-expansion@npm:^2.0.1": "2.1.2",
  "brace-expansion@npm:^2.0.2": "2.1.2",
  "brace-expansion@npm:^5.0.5": "5.0.7"
}

Why Multiple Resolution Entries?

The dependency tree contains multiple packages requesting different semver ranges of brace-expansion:
- Some packages request ^1.1.7 (1.x compatibility)
- Others request ^2.0.1 or ^2.0.2 (2.x compatibility)
- Newer packages request ^5.0.5 (5.x compatibility)

Each resolution entry ensures that regardless of which range a package requests, Yarn resolves it to a patched version:
- ^1.1.71.1.16 (was resolving to vulnerable 1.1.18)
- ^2.0.1 and ^2.0.22.1.2 (was resolving to vulnerable 2.1.1)
- ^5.0.55.0.7 (was resolving to vulnerable 5.0.6)

The yarn.lock Changes

The lockfile updates reflect the version pinning:

-"brace-expansion@npm:^1.1.7":
-  version: 1.1.18
+"brace-expansion@npm:1.1.16":
+  version: 1.1.16

-"brace-expansion@npm:^2.0.1, brace-expansion@npm:^2.0.2":
-  version: 2.1.1
+"brace-expansion@npm:2.1.2":
+  version: 2.1.2

-"brace-expansion@npm:^5.0.5":
-  version: 5.0.6
+"brace-expansion@npm:5.0.7":
+  version: 5.0.7

The patched versions (1.1.16, 2.1.2, 5.0.7) include algorithmic improvements that prevent the exponential blowup, likely through:
- Input length limits
- Recursion depth guards
- Optimized parsing that avoids exponential branching

Prevention & Best Practices

1. Use Dependency Scanning in CI/CD

Integrate tools like Trivy, Snyk, or npm audit into your pipeline:

# GitHub Actions example
- name: Run Trivy vulnerability scanner
  uses: aquasecurity/trivy-action@master
  with:
    scan-type: 'fs'
    scan-ref: '.'
    severity: 'HIGH,CRITICAL'

2. Leverage Package Manager Resolutions

Both Yarn and npm support overriding transitive dependency versions:

Yarn (package.json):

"resolutions": {
  "vulnerable-package": "^patched.version"
}

npm (package.json):

"overrides": {
  "vulnerable-package": "^patched.version"
}

3. Audit Dependencies Regularly

# npm
npm audit

# yarn
yarn audit

# With automatic fix attempts
npm audit fix

4. Implement Input Validation

When accepting user input that flows to glob/pattern matching:

const MAX_PATTERN_LENGTH = 100;
const MAX_BRACE_DEPTH = 3;

function validatePattern(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('Pattern too complex');
  }

  return pattern;
}

5. Consider Timeouts for Parsing Operations

const { setTimeout } = require('timers/promises');

async function safeGlobMatch(pattern, files, timeoutMs = 1000) {
  const controller = new AbortController();

  const result = await Promise.race([
    performMatch(pattern, files),
    setTimeout(timeoutMs, null, { signal: controller.signal })
  ]);

  if (result === null) {
    throw new Error('Pattern matching timed out');
  }

  return result;
}

Key Takeaways

  • Transitive dependencies matter: The vulnerable brace-expansion wasn't a direct dependency but came through packages like minimatch—always scan the full dependency tree
  • Multiple version ranges require multiple resolutions: This fix needed four separate resolution entries to cover all semver ranges (^1.1.7, ^2.0.1, ^2.0.2, ^5.0.5)
  • Algorithmic complexity is a real attack vector: DoS vulnerabilities don't require memory corruption or code execution—exponential algorithms are exploitable
  • Yarn resolutions provide surgical fixes: Rather than waiting for every intermediate package to update, resolutions let you patch vulnerabilities immediately
  • The assessment noted "not confirmed reachable": Even without confirmed exploitation paths, upgrading is the right call—attack surface reduction is proactive security

How Orbis AppSec Detected This

  • Source: Transitive dependency brace-expansion in yarn.lock resolved to vulnerable versions (1.1.18, 2.1.1, 5.0.6)
  • Sink: Any code path using glob matching, file pattern expansion, or minimatch functionality that accepts external input
  • Missing control: No version pinning or resolution overrides to enforce patched versions across the dependency tree
  • CWE: CWE-1333 (Inefficient Regular Expression Complexity) / CWE-400 (Uncontrolled Resource Consumption)
  • Fix: Added yarn resolutions in package.json to force all brace-expansion semver ranges to resolve to patched versions (1.1.16, 2.1.2, 5.0.7)

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 demonstrates how algorithmic complexity vulnerabilities in foundational packages can create widespread risk. The brace-expansion package, while small, sits at the base of the npm dependency pyramid—used by glob matching libraries that power build tools, test runners, and countless CLI utilities.

The fix was straightforward: yarn resolutions that pin all semver ranges to patched versions. This approach is immediately effective, doesn't require waiting for intermediate packages to update, and ensures the entire dependency tree is protected.

For developers: treat dependency updates as security hygiene. Automated scanning catches these issues early, and package manager resolutions give you the tools to fix them quickly—even in complex dependency trees.

References

Frequently Asked Questions

What is exponential time complexity DoS?

It's a vulnerability where specially crafted input causes an algorithm to run in exponential time (O(2^n)), consuming excessive CPU resources and potentially freezing or crashing the application.

How do you prevent exponential time complexity DoS in Node.js?

Keep dependencies updated, use yarn/npm resolutions to enforce patched versions across transitive dependencies, implement input length limits, and consider timeouts for parsing operations.

What CWE is exponential time complexity?

CWE-1333 (Inefficient Regular Expression Complexity) or more broadly CWE-400 (Uncontrolled Resource Consumption) covers algorithmic complexity vulnerabilities that enable DoS attacks.

Is input validation enough to prevent brace-expansion DoS?

While input validation helps, the safest approach is upgrading to patched versions. Malicious patterns can be subtle and difficult to filter without understanding the exact vulnerability trigger.

Can static analysis detect exponential time complexity vulnerabilities?

Yes, tools like Trivy, Snyk, and npm audit can detect known vulnerable package versions. However, detecting novel algorithmic complexity issues in custom code requires specialized analysis.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #2408

Related Articles

high

How Remote Code Execution via serialize-javascript happens in Node.js and how to fix it

The `serialize-javascript` package version 6.0.2 contained a high-severity Remote Code Execution (RCE) vulnerability (GHSA-5c6j-r48x-rmvq) exploitable through crafted `RegExp.flags` and `Date.prototype.toISOString()` payloads. Upgrading to version 7.0.3 eliminates the vulnerable serialization logic and removes the `randombytes` dependency that was part of the attack surface. This fix was applied via a `package.json` override and `package-lock.json` update.

critical

How unvalidated URL input handling happens in SvelteKit with Tauri and how to fix it

A critical vulnerability in `src/routes/+page.svelte` allowed attackers to supply arbitrary URLs—including `http://` and local file paths—through query parameters and drag-drop events, which were then fetched without validation. The fix restricts input to HTTPS-only URLs and removes the dangerous local file fetch path entirely, eliminating both SSRF and local file disclosure attack vectors.

critical

How SQL injection happens in Node.js string interpolation and how to fix it

A critical SQL injection vulnerability was discovered in the `getScript()` method of `src/core/statistics.js`, where the `metadata_id` variable was directly interpolated into DELETE and UPDATE SQL statements without any validation. An attacker controlling this parameter could inject malicious SQL payloads to delete entire tables or exfiltrate sensitive data. The fix implements strict input validation using `parseInt()` and regex patterns to ensure only safe values reach the database queries.

critical

How Command Injection happens in Python Flask and how to fix it

A critical command injection vulnerability was discovered in a Flask application's `/abc2xml` endpoint where user-supplied ABC music notation data could be weaponized to execute arbitrary shell commands. The `run_command` function used `subprocess.run()` with `shell=True` and string concatenation, allowing attackers to inject shell metacharacters. The fix switches to a list-based command invocation with `shell=False`, eliminating the injection vector entirely.

critical

How credential header disclosure happens in electron-updater and how to fix it

A critical vulnerability in electron-updater (CVE-2026-54673) allowed OAuth tokens and API credentials to leak when HTTP redirects occurred during application updates. The fix upgrades electron-updater from version 6.3.0 to 6.8.9, which properly strips sensitive authorization headers before following redirects to external domains.

high

How Unicode Normalization Infinite Loops Happen in Go and How to Fix CVE-2026-56852

CVE-2026-56852 is a high-severity vulnerability in golang.org/x/text that allows the Unicode normalization iterator to enter an infinite loop when processing specially crafted input. This fix upgrades the dependency from v0.37.0 to v0.39.0, tightening input validation and preventing denial-of-service attacks in applications that process untrusted Unicode text.