Back to Blog
high SEVERITY6 min read

How Denial of Service via unbounded brace expansion happens in Node.js and how to fix it

A high-severity Denial of Service vulnerability (CVE-2026-14257) in the `brace-expansion` package version 1.1.12 allowed attackers to craft malicious brace patterns that caused exponential-time complexity, leading to out-of-memory process crashes. The fix upgrades the dependency to version 1.1.16 using npm overrides to ensure the patched version is used throughout the entire dependency tree.

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

Answer Summary

CVE-2026-14257 is a Denial of Service (DoS) vulnerability in the Node.js `brace-expansion` package (versions prior to 1.1.16) caused by unbounded expansion length that leads to exponential-time complexity and out-of-memory crashes (CWE-1333). The fix involves upgrading `brace-expansion` to version 1.1.16 via an npm `overrides` field in `package.json`, which enforces expansion limits and prevents malicious input from consuming unbounded memory.

Vulnerability at a Glance

cweCWE-1333 (Inefficient Regular Expression Complexity) / CWE-400 (Uncontrolled Resource Consumption)
fixUpgrade brace-expansion to 1.1.16 via npm overrides to enforce expansion bounds
riskApplication crash via out-of-memory when processing crafted brace patterns
languageJavaScript (Node.js)
root causebrace-expansion 1.1.12 lacks limits on expansion output length, enabling exponential growth
vulnerabilityDenial of Service via unbounded brace expansion (ReDoS/algorithmic complexity)

Introduction

In a Node.js project's package-lock.json, we discovered a high-severity Denial of Service vulnerability lurking in a transitive dependency: brace-expansion version 1.1.12. This package — used by minimatch and other glob-matching libraries — is deeply embedded in the Node.js ecosystem, powering file path matching in build tools, test runners, and application code alike.

The vulnerability (CVE-2026-14257) allows an attacker to provide a specially crafted brace pattern that triggers unbounded expansion, causing exponential memory consumption and ultimately crashing the process with an out-of-memory error. Because brace-expansion often processes patterns derived from user input (file paths, glob patterns in APIs, configuration strings), this isn't just a theoretical concern — it's an exploitable denial-of-service vector.

The locked dependency in package-lock.json pinned brace-expansion at version 1.1.12:

"node_modules/brace-expansion": {
  "version": "1.1.12",
  "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
  "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="
}

This matters for any developer relying on glob patterns, file matching, or build tooling in their Node.js applications — which is nearly everyone.

The Vulnerability Explained

How brace-expansion works

The brace-expansion library takes a string like {a,b,c} and expands it into an array ['a', 'b', 'c']. It also handles nested and sequential patterns: {a,b}{1,2} becomes ['a1', 'a2', 'b1', 'b2'].

The exponential blowup

The problem in version 1.1.12 is that there is no limit on the number of expansions generated. Consider a pattern like:

{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}

Each pair of braces doubles the output. With 20 pairs, you get 2²⁰ = 1,048,576 strings. With 30 pairs, you get over a billion. The library in version 1.1.12 will attempt to compute and store all of these in memory, with no safeguard.

Attack scenario specific to this project

This project (identified by its package.json metadata as a KETI-authored project related to oneM2M IoT standards) likely processes resource identifiers or path patterns. An attacker could:

  1. Submit a crafted resource path or query parameter containing deeply nested brace patterns
  2. The application's glob-matching logic (via minimatchbrace-expansion) processes the malicious input
  3. Memory consumption spikes exponentially
  4. The Node.js process crashes with an OOM error, denying service to all users

Even if the code path isn't directly user-facing, any route where untrusted strings reach minimatch or similar glob utilities creates an attack surface.

Why the locked version was dangerous

The package-lock.json explicitly resolved brace-expansion to version 1.1.12:

"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",

This version lacks the expansion length limits introduced in 1.1.16, meaning every npm install would faithfully install the vulnerable code.

The Fix

The fix involves two coordinated changes across package.json and package-lock.json:

1. Adding npm overrides in package.json

Before:

{
  "author": "KETI",
  "license": "BSD-3-Clause"
}

After:

{
  "author": "KETI",
  "license": "BSD-3-Clause",
  "overrides": {
    "brace-expansion": "1.1.16"
  }
}

The overrides field is critical. Because brace-expansion is a transitive dependency (pulled in by minimatch, which is pulled in by other packages), simply upgrading a direct dependency wouldn't guarantee the fix propagates. The overrides field forces npm to resolve brace-expansion to version 1.1.16 everywhere in the dependency tree, regardless of what version ranges other packages specify.

2. Updating package-lock.json

Before:

"node_modules/brace-expansion": {
  "version": "1.1.12",
  "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
  "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="
}

After:

"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=="
}

Why version 1.1.16 fixes the issue

Version 1.1.16 of brace-expansion introduces bounds checking on the expansion output. Before generating results, it validates that the total number of expansions won't exceed a safe threshold. If the expansion would produce an unreasonable number of results, it short-circuits and returns the input unexpanded rather than consuming unbounded memory.

Why both files needed to change

  • package.json: The overrides field ensures future npm install runs always resolve to the safe version, even if upstream packages haven't updated their dependency ranges.
  • package-lock.json: The lockfile must reflect the actual resolved version so that CI/CD pipelines and other developers get the patched version immediately without needing to run npm install with --force.

Prevention & Best Practices

1. Audit your dependency tree regularly

npm audit
npx trivy fs --scanners vuln .

These commands catch known CVEs in both direct and transitive dependencies.

2. Use npm overrides (or yarn resolutions) proactively

When a transitive dependency has a vulnerability and the intermediate package hasn't released an update, overrides lets you force the fix:

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

3. Validate input before glob processing

If your application accepts user input that eventually reaches glob-matching functions, validate it first:

// Limit brace nesting depth before passing to minimatch
function isSafePattern(pattern) {
  const braceCount = (pattern.match(/\{/g) || []).length;
  return braceCount <= 5; // Reasonable limit for legitimate use
}

4. Set resource limits

Use Node.js --max-old-space-size flags and container memory limits to prevent a single request from taking down your entire infrastructure.

5. Pin and lock dependencies

Always commit package-lock.json and review dependency changes in PRs. A version bump in a lockfile should trigger the same scrutiny as a code change.

Relevant standards

  • CWE-400: Uncontrolled Resource Consumption
  • CWE-1333: Inefficient Regular Expression Complexity
  • OWASP: Application Denial of Service

Key Takeaways

  • brace-expansion 1.1.12 has no expansion limit — a pattern with N brace pairs generates 2^N outputs, enabling trivial OOM crashes
  • Transitive dependencies require overrides to fix — updating your direct dependencies won't help if minimatch still pulls in the old brace-expansion
  • The package-lock.json integrity hash change (from sha512-9T9UjW3r... to sha512-IDw48K2...) confirms the actual binary content of the package changed, not just metadata
  • IoT/oneM2M projects processing resource identifiers are particularly at risk since path patterns may be influenced by external devices or APIs
  • A two-file fix (package.json + package-lock.json) is the minimum — the override ensures persistence, and the lockfile ensures immediate effect

How Orbis AppSec Detected This

  • Source: Transitive dependency resolution in package-lock.json pulling brace-expansion@1.1.12 into the project's node_modules
  • Sink: Any code path invoking minimatch() or similar glob utilities that internally call brace-expansion's expand() function with potentially unbounded input
  • Missing control: No expansion length limit in brace-expansion 1.1.12; no npm override enforcing a patched version
  • CWE: CWE-400 (Uncontrolled Resource Consumption)
  • Fix: Added "overrides": { "brace-expansion": "1.1.16" } to package.json and updated package-lock.json to resolve version 1.1.16, which enforces expansion bounds

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

This vulnerability is a textbook example of how algorithmic complexity attacks exploit unbounded computation in seemingly innocuous utility libraries. The brace-expansion package is downloaded millions of times per week and sits deep in the dependency trees of most Node.js projects. Version 1.1.12's lack of expansion limits meant that any application processing untrusted glob patterns was one crafted string away from a complete denial of service.

The fix — upgrading to 1.1.16 via npm overrides — is minimal in code change but maximal in security impact. It demonstrates that modern application security isn't just about the code you write; it's about the entire supply chain of packages you depend on. Regular dependency auditing, lockfile hygiene, and automated vulnerability detection are essential practices for any Node.js project.

References

Frequently Asked Questions

What is a Denial of Service via unbounded brace expansion?

It's a vulnerability where a specially crafted brace pattern (e.g., `{a,b}{a,b}{a,b}...` nested deeply) causes the brace-expansion library to generate an exponentially large number of strings, consuming all available memory and crashing the Node.js process.

How do you prevent algorithmic complexity DoS in Node.js?

Keep dependencies updated, use npm overrides to enforce patched versions across transitive dependencies, validate and limit the length/complexity of user-supplied input before passing it to expansion functions, and monitor for CVEs in your dependency tree.

What CWE is unbounded resource consumption?

CWE-400 (Uncontrolled Resource Consumption) covers general resource exhaustion, while CWE-1333 (Inefficient Regular Expression Complexity) covers the specific pattern of exponential-time processing that can be triggered by crafted input.

Is simply updating package.json enough to prevent this vulnerability?

Not always. Transitive dependencies may still resolve to vulnerable versions. Using npm `overrides` (or yarn `resolutions`) ensures the patched version is used everywhere in the dependency tree, which is exactly what this fix demonstrates.

Can static analysis detect this vulnerability?

Yes. Tools like Trivy, Snyk, and npm audit scan `package-lock.json` for known vulnerable dependency versions and flag them with their associated CVE identifiers, as demonstrated in this detection.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #28

Related Articles

high

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

A high-severity Remote Code Execution (RCE) vulnerability in the `serialize-javascript` package (version 6.0.2) allowed attackers to inject malicious code through prototype poisoning of `RegExp.flags` and `Date.prototype.toISOString()`. The fix upgrades the dependency to version 7.0.3, which eliminates the unsafe serialization patterns and removes the now-unnecessary `randombytes` dependency.

critical

How Credential Exposure Over HTTP Happens in Python Requests and How to Fix It

A critical vulnerability was discovered in the Bitbucket catalog connector where pagination URLs from API responses were followed without HTTPS validation, potentially exposing HTTP Basic Authentication credentials over unencrypted connections. The fix enforces HTTPS-only URLs for pagination and adds request timeouts to prevent resource exhaustion attacks.

critical

How Insecure API Key Transmission Happens in JavaScript Browser Extensions and How to Fix It

A critical vulnerability in `utils/common.js` allowed API keys to be transmitted over unencrypted HTTP connections to remote servers, exposing them to network interception. The `buildModelApiRequest` function at line 490 constructed API requests without validating the transport protocol, enabling man-in-the-middle attacks. The fix enforces HTTPS for all remote API endpoints while preserving HTTP access for local development servers on loopback addresses.

critical

How URL Injection via Unvalidated User Input happens in Node.js and how to fix it

A critical URL injection vulnerability in the QQ info lookup feature allowed attackers to manipulate API request parameters by sending specially crafted messages. Without proper input validation, user-controlled data was directly embedded into external API URLs, potentially exposing sensitive authentication credentials (skey and pskey) to attacker-controlled servers.

high

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

A high-severity denial of service vulnerability (CVE-2026-14257) was discovered in the brace-expansion package within the zeroshot-oecp Docker container's dependency tree. The vulnerability allows attackers to craft malicious input patterns that trigger exponential-time processing, potentially freezing or crashing Node.js applications. This fix upgrades the nested brace-expansion dependency to version 5.0.9 using a targeted Dockerfile modification.

high

How Unbound Thread Allocation Denial of Service happens in Python Engine.IO and how to fix it

A high-severity vulnerability (CVE-2026-48802) in python-engineio 4.12.2 allowed attackers to exhaust system resources through unbound thread allocation, leading to denial of service. The fix upgrades the dependency to version 4.13.2, which implements thread pool limits to prevent resource exhaustion attacks against real-time WebSocket applications.