Introduction
In this repository's dependency tree, Trivy flagged a high-severity vulnerability lurking in package-lock.json: the brace-expansion package at version 5.0.6 contained CVE-2026-13149, an algorithmic complexity flaw that could bring down a Node.js application with a single malicious input string.
The brace-expansion package is a foundational utility used by glob pattern matching libraries like minimatch and micromatch. It expands brace patterns like {a,b,c} into arrays ['a', 'b', 'c']. This functionality appears everywhere—from build tools to file system operations—making this vulnerability particularly concerning given its position deep in most Node.js dependency trees.
Looking at the package-lock.json, we can see the vulnerable version pinned:
"node_modules/brace-expansion": {
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
This version contained the exponential-time complexity bug that CVE-2026-13149 addresses.
The Vulnerability Explained
What is Exponential-Time Complexity?
The brace-expansion library parses patterns like {1..5} or {a,b,c} and expands them into arrays. However, version 5.0.6 and earlier contained an algorithm that, when given specially crafted nested brace patterns, would exhibit exponential time complexity.
Consider a pattern like {a{b{c{d{e{f{g{h{i{j}}}}}}}}}. Each level of nesting multiplies the processing time. An attacker could craft a pattern where each additional character doubles (or worse) the computation time, creating what's known as a "billion laughs" style attack.
The Attack Vector
Here's how an attacker could exploit this:
- Identify an input path: Any application feature that uses glob patterns, file matching, or brace expansion with user-controlled input becomes a target
- Craft a malicious pattern: Create a deeply nested or specially structured brace pattern
- Submit the payload: Send the pattern through an API endpoint, file upload name, or configuration input
- Cause resource exhaustion: The server's CPU spikes to 100% processing the expansion, blocking the event loop and making the application unresponsive
For example, if this application uses minimatch (which depends on brace-expansion) to validate file paths or process user-provided glob patterns, an attacker could submit:
{a{b{c{d{e{f{g{h{i{j{k{l{m{n{o{p}}}}}}}}}}}}}}}
This single string could lock up the Node.js process for minutes or hours, effectively creating a Denial of Service.
Real-World Impact
Since brace-expansion sits deep in the dependency tree (often pulled in by glob, minimatch, or build tools), the vulnerable code path may be exercised in unexpected places:
- Build systems: Processing user-provided file patterns
- File upload handlers: Validating or filtering filenames
- API endpoints: Any route accepting glob-style patterns
- Configuration parsers: Reading user-provided config files
The Fix
The fix involves two coordinated changes to ensure the vulnerable version is completely replaced throughout the dependency tree.
Before (Vulnerable)
package-lock.json:
"node_modules/brace-expansion": {
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
...
"engines": {
"node": "18 || 20 || >=22"
}
}
package.json:
"overrides": {
"tar": "7.5.21"
}
After (Fixed)
package-lock.json:
"node_modules/brace-expansion": {
"version": "5.0.9",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
"integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
...
"engines": {
"node": "20 || >=22"
}
}
package.json:
"overrides": {
"tar": "7.5.21",
"brace-expansion": "5.0.9"
}
Why npm Overrides?
The critical addition is the overrides entry in package.json:
"overrides": {
"tar": "7.5.21",
"brace-expansion": "5.0.9"
}
This is essential because brace-expansion is typically a transitive dependency—it's not directly listed in your dependencies, but pulled in by other packages like glob or minimatch. Without the override, npm might still install the vulnerable version to satisfy another package's version requirements.
The overrides field tells npm: "Regardless of what version other packages request, always use version 5.0.9 of brace-expansion." This ensures complete remediation across the entire dependency tree.
Additional Change: @capacitor/core
The diff also shows a small change to @capacitor/core:
- "peer": true,
This removes the peer designation, ensuring the package is installed directly rather than relying on peer dependency resolution. This change helps stabilize the dependency tree and ensures consistent version resolution.
Prevention & Best Practices
1. Regular Dependency Auditing
Run security audits as part of your CI/CD pipeline:
npm audit
npx trivy fs --scanners vuln .
2. Use Lock Files and Overrides Strategically
- Always commit
package-lock.jsonto version control - Use
overrides(npm) orresolutions(yarn) to force secure versions of transitive dependencies - Review your lock file changes in PRs
3. Input Validation
Even with patched dependencies, implement defense in depth:
// Limit pattern complexity before passing to glob/minimatch
function validateGlobPattern(pattern) {
const maxLength = 200;
const maxNestingDepth = 5;
if (pattern.length > maxLength) {
throw new Error('Pattern too long');
}
const nestingDepth = (pattern.match(/{/g) || []).length;
if (nestingDepth > maxNestingDepth) {
throw new Error('Pattern too complex');
}
return pattern;
}
4. Monitor for New CVEs
Subscribe to security advisories:
- GitHub Dependabot alerts
- npm security advisories
- Snyk vulnerability database
Key Takeaways
- Transitive dependencies are attack surface:
brace-expansionwasn't a direct dependency, yet it created a high-severity vulnerability in the application - npm overrides are essential for complete remediation: Simply running
npm updatemay not fix transitive dependencies—useoverridesto force specific versions - Algorithmic complexity attacks don't require authentication: A single malicious string can DoS an application without any credentials
- The fix narrowed Node.js version support: Version 5.0.9 dropped Node 18 support (
"node": "20 || >=22"), which may require consideration for legacy deployments - Defense in depth matters: Even with patched libraries, validate and limit the complexity of user-provided patterns
How Orbis AppSec Detected This
- Source: The
brace-expansionpackage version 5.0.6 in the dependency tree, potentially receiving user-influenced input through glob pattern processing - Sink: The brace expansion algorithm in
brace-expansion/index.jsthat processes nested brace patterns - Missing control: No complexity limits on the expansion algorithm, allowing exponential-time processing
- CWE: CWE-1333 (Inefficient Regular Expression Complexity) / CWE-400 (Uncontrolled Resource Consumption)
- Fix: Upgraded
brace-expansionto version 5.0.9 via npm overrides to ensure all transitive dependencies use the patched version with optimized algorithm
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 in brace-expansion demonstrates how a vulnerability in a small utility package can have outsized impact due to its position in the npm ecosystem. The exponential-time complexity bug could turn a simple string into a weapon capable of bringing down production servers.
The fix—upgrading to version 5.0.9 and using npm overrides—ensures complete remediation across the dependency tree. But beyond this specific CVE, this incident reinforces the importance of continuous dependency monitoring, understanding your transitive dependencies, and implementing input validation as defense in depth.
Keep your dependencies updated, audit regularly, and remember: security is everyone's responsibility.