Back to Blog
high SEVERITY8 min read

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.

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published August 20, 2026•Reviewed August 20, 2026

Answer Summary

CVE-2026-13149 is a Denial of Service vulnerability (CWE-1333) in the `brace-expansion` npm package, affecting versions prior to 1.1.16, 2.1.2, and 5.0.7. The root cause is a regular expression or recursive expansion algorithm with exponential time complexity when processing deeply nested or repeated brace patterns like `{a,b}{a,b}{a,b}...`. The fix is to upgrade `brace-expansion` to a patched version—either directly or via an npm `overrides` field in `package.json`—which caps or linearizes the expansion logic to prevent runaway processing.

Vulnerability at a Glance

cweCWE-1333 (Inefficient Regular Expression Complexity)
fixUpgrade brace-expansion to 5.0.9 (pinned via npm overrides) which bounds expansion complexity
riskAn attacker can crash or freeze a Node.js process with a small crafted input string
languageJavaScript / Node.js
root causebrace-expansion processes nested/repeated brace patterns in exponential time, producing combinatorial output
vulnerabilityDenial of Service via exponential-time brace expansion

How Denial of Service via Exponential Complexity Happens in Node.js and How to Fix It


Vulnerability at a Glance

Field Detail
CVE CVE-2026-13149
Severity High
CWE CWE-1333 – Inefficient Regular Expression Complexity
Package brace-expansion (npm)
Fixed in 1.1.16 / 2.1.2 / 5.0.7+
Affected file cdk-eregs/package-lock.json

Introduction

The cdk-eregs/package-lock.json file locks the entire transitive dependency tree for an AWS CDK infrastructure project. Buried several layers deep in that tree was brace-expansion, a utility that turns shell-style brace patterns like file.{js,ts,css} into lists of strings. It is a foundational package—pulled in by glob, minimatch, and dozens of other widely used tools—which makes it both invisible to most developers and dangerous when it misbehaves.

Trivy's dependency scanner flagged brace-expansion in this repository because the version in use contained a flaw that allows exponential-time processing of crafted brace patterns. An attacker who can influence the strings passed to any code path that eventually calls brace-expansion—even indirectly through a glob or file-watcher—can cause the Node.js event loop to hang indefinitely, effectively taking down the process.


The Vulnerability Explained

What is brace expansion?

Brace expansion converts a compact pattern into a list of strings:

// Input
'{a,b,c}.{js,ts}'

// Output
['a.js', 'a.ts', 'b.js', 'b.ts', 'c.js', 'c.ts']

This is useful for glob matching, file discovery, and CLI tooling. The problem arises when the expansion algorithm is applied to nested or repeated brace groups.

The exponential growth problem

Consider what happens with repeated nested alternatives:

{a,b}{a,b}{a,b}  →  8 strings   (2³)
{a,b}{a,b}...×10 →  1,024 strings (2¹⁰)
{a,b}{a,b}...×30 →  1,073,741,824 strings (2³⁰)

A 60-character input string produces over one billion expansion results. The vulnerable versions of brace-expansion attempt to generate the full Cartesian product in memory before returning, meaning both CPU time and memory consumption grow exponentially with the number of brace groups.

The specific algorithmic pattern that causes this is a recursive Cartesian-product expansion without any bound on output size or recursion depth. Internally, the library builds the result by calling something equivalent to:

// Simplified pseudocode of the vulnerable pattern
function expand(pattern) {
  const parts = parse(pattern); // splits on commas and nested braces
  return cartesianProduct(parts.map(expand)); // unbounded recursion + product
}

Each level of nesting multiplies the output size by the number of alternatives at that level. There is no guard that says "stop if output exceeds N items."

What does the vulnerable code look like in context?

The cdk-eregs/package-lock.json (before the fix) contained entries like:

"brace-expansion": {
  "version": "1.1.11",
  ...
}

or

"brace-expansion": {
  "version": "2.0.1",
  ...
}

These transitive versions—pulled in by tools like glob and minimatch which are themselves dependencies of CDK tooling—were all below the patched thresholds.

Attack scenario for this repository

The cdk-eregs project is an AWS CDK application. CDK's build and synthesis pipeline uses glob patterns extensively to discover assets, Lambda function bundles, and configuration files. If any part of that pipeline accepts external input that feeds into a glob pattern—such as a CI/CD parameter, an environment variable, or a configuration file read from an S3 bucket—an attacker with write access to that input could inject a pattern like:

{a,b,c,d,e}{a,b,c,d,e}{a,b,c,d,e}{a,b,c,d,e}{a,b,c,d,e}{a,b,c,d,e}

This 42-character string would generate 15,625 expansions (5⁶). Extend it to ten groups and you reach nearly 10 million. The CDK synthesis process—or any Lambda that calls glob with this input—would hang, causing deployment pipelines to time out or Lambda invocations to exhaust their memory limit.

Even without a direct injection path, the vulnerability is still relevant: the scanner assessment notes it is "present in dependency tree, not confirmed reachable," but the attack surface of a CDK project spans build tools, test runners, and local developer environments—all of which run this code.


The Fix

What changed

The fix introduced an npm overrides block in cdk-eregs/package.json:

Before (package.json — no overrides):

{
  "name": "cdk-eregs",
  "dependencies": {
    "fs-extra": "11.3.1",
    "path": "0.12.7",
    "source-map-support": "0.5.21"
  }
}

After (package.json — with override):

{
  "name": "cdk-eregs",
  "dependencies": {
    "fs-extra": "11.3.1",
    "path": "0.12.7",
    "source-map-support": "0.5.21"
  },
  "overrides": {
    "brace-expansion": "5.0.9"
  }
}

The overrides field (introduced in npm v8.3) forces every package in the dependency tree—regardless of what version they declare as their own dependency—to use brace-expansion@5.0.9. This is the canonical way to patch a transitive dependency vulnerability without waiting for every intermediate package to release an update.

Why this specific version?

The patched versions are 1.1.16, 2.1.2, and 5.0.7+. The fix in each adds a guard against combinatorial explosion—either by capping the maximum number of expansions, by detecting pathological patterns early and returning them unexpanded, or by rewriting the expansion loop to avoid building the full product set in memory.

By pinning to 5.0.9, the override ensures the fix is applied even if a transitive dependency specifies ^1.x or ^2.x, since npm's override mechanism replaces the resolved version regardless of the semver range declared by the dependent package.

The package-lock.json impact

After adding the override and running npm install, the package-lock.json is regenerated so that all entries for brace-expansion—regardless of which package required them—resolve to 5.0.9. The lock file is the authoritative record that the scanner (Trivy) reads, so this change directly eliminates the CVE finding.


Key Takeaways

  • brace-expansion is a hidden risk in virtually every Node.js project that uses glob or minimatch—check your lock file, not just your direct dependencies.
  • A 60-character crafted string can generate billions of expansions; the attack payload is trivially small, making it easy to embed in configuration files or CI parameters.
  • The overrides field in package.json is the correct surgical fix for transitive dependency CVEs—it forces the patched version across the entire dependency tree without touching unrelated packages.
  • Trivy correctly identified this in cdk-eregs/package-lock.json even though brace-expansion is not a direct dependency, demonstrating why scanning the full resolved lock file matters more than scanning package.json alone.
  • Patched versions (1.1.16, 2.1.2, 5.0.7+) add complexity bounds to the expansion algorithm; upgrading is preferable to application-level workarounds because the fix is in the right place.

How Orbis AppSec Detected This

  • Source: The cdk-eregs/package-lock.json file, which resolves transitive dependency versions including brace-expansion at a vulnerable version, is read during CDK build and synthesis operations that process file-system glob patterns.
  • Sink: Any call to braceExpansion(pattern) within the resolved brace-expansion module—invoked transitively through glob → minimatch → brace-expansion—where pattern contains repeated or deeply nested brace groups.
  • Missing control: No upper bound on the number of expansions or recursion depth in the brace-expansion expansion algorithm prior to versions 1.1.16 / 2.1.2 / 5.0.7.
  • CWE: CWE-1333 – Inefficient Regular Expression Complexity (applies broadly to algorithms with super-linear time growth on adversarial inputs).
  • Fix: An npm overrides directive was added to cdk-eregs/package.json pinning brace-expansion to 5.0.9 across the entire dependency tree, and package-lock.json was regenerated to reflect the patched resolution.

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 is a sharp reminder that the most dangerous vulnerabilities are often the quietest ones. brace-expansion is a tiny utility with no dependencies of its own, yet its presence in nearly every Node.js project's transitive tree makes a flaw in its core algorithm a systemic risk. Exponential-complexity attacks are particularly insidious because the payload is small, the impact is immediate, and the vulnerable code path is rarely something developers think to audit.

The fix here—a single overrides block in package.json—is minimal, surgical, and does not affect any valid input. It is also a template for how to handle transitive dependency CVEs in npm projects generally. Pair it with automated scanning in CI, commit your lock files, and validate pattern inputs at application boundaries, and you have a robust defense against this class of vulnerability.


Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #2226

Related Articles

high

xcb_get_image_reply() NULL Deref on malloc() Failure

The XCB image-reply handler in the screen-streaming bridge allocated a buffer with `malloc()` and immediately wrote to it with `memset()` and `memcpy()` without checking for allocation failure. Under memory pressure, this produces a null-pointer dereference that crashes the process, turning a resource-exhaustion condition into an immediate denial of service. The fix adds a NULL check that frees the intermediate reply and returns early instead of writing to invalid memory.

high

picomatch 2.3.1 ReDoS: Extglob Pattern Catastrophic Backtracking

picomatch versions below 2.3.2, 3.0.2, or 4.0.4 contain a Regular Expression Denial of Service vulnerability in extglob pattern parsing. An attacker can cause catastrophic backtracking with patterns containing nested alternations and quantifiers, freezing any Node.js process that evaluates untrusted glob expressions.

critical

No Rate Limit on /api/uploads/presign Enables DoS

The `/api/uploads/presign` endpoint accepted unlimited concurrent requests to generate storage presigned URLs, giving an attacker a free lever to exhaust storage-provider quotas and server resources. The fix adds an `express-rate-limit` middleware capping each client to 30 requests per minute on that route.

high

brace-expansion Stack Exhaustion: CVE-2026-102276 Patched

A critical stack exhaustion vulnerability in brace-expansion allows attackers to crash Node.js applications by supplying specially crafted brace patterns that trigger unbounded recursion. The fix upgrades the library across all maintained version lines to enforce depth limits on recursive expansion. This vulnerability affects any service that expands user-controlled brace patterns without input validation.

high

js-yaml 5.2.1 DoS: Exponential Parsing in Flow Collections

A denial-of-service vulnerability in js-yaml 5.2.1 allows an attacker to crash the parser by supplying deeply nested flow collections that trigger exponential parsing behavior. The fix in version 5.2.2 improves the parser's handling of these structures, preventing the algorithmic complexity attack. This is critical for any service accepting user-controlled YAML input.

high

image-size 1.2.1 DoS: Zero-Valued Dimensions in Image Buffer Parser

A high-severity denial-of-service vulnerability in image-size 1.2.1 allows attackers to crash Node.js services using malicious image buffers with zero-valued dimensions. The fix removes the vulnerable `queue` dependency and tightens dimension validation in version 2.0.3.