How Denial of Service via Unbounded Arrays in brace-expansion Happens and How to Fix CVE-2026-69152
Introduction
In build pipelines and Node.js applications worldwide, the brace-expansion library quietly handles string pattern expansion—converting patterns like {a,b,c} into arrays of possible values. This simple utility is depended upon by thousands of npm packages. But in CVE-2026-69152, researchers discovered a critical flaw: the library could be abused to allocate unbounded intermediate arrays during expansion, causing denial of service attacks that crash applications or exhaust memory. Worse, this vulnerability bypassed the mitigation implemented for the previous CVE-2026-14257, demonstrating that the original fix was incomplete.
This matters because brace-expansion appears deep in dependency trees—it's pulled in by glob patterns, test runners, and build tools. An attacker who can influence the input to a brace expansion operation (through a filename, configuration value, or command-line argument) can trigger this vulnerability without your code ever directly calling the library.
The Vulnerability Explained
What Makes This Different: Bypassing the Previous Fix
The brace-expansion library had already been patched once for CVE-2026-14257, which addressed unbounded expansion. However, CVE-2026-69152 reveals that the previous fix didn't account for intermediate arrays created during the expansion algorithm itself.
Here's the critical distinction: when brace-expansion processes a pattern like {1..1000000}, it doesn't just create one massive final array. The internal algorithm creates temporary arrays at each step of expansion. The previous fix might have capped the final result, but the intermediate working arrays could still consume all available memory before that cap was reached.
The Attack Scenario
Consider a build system that accepts user-provided glob patterns:
// Vulnerable code path (simplified)
const braceExpand = require('brace-expansion');
// Attacker provides this pattern via a config file or CLI argument
const userPattern = '{0..999999999999999}';
// This creates unbounded intermediate arrays during expansion
const expanded = braceExpand(userPattern);
// Result: Application crashes as intermediate array allocation fails
// or memory is exhausted before the function completes
An attacker could craft patterns with deeply nested expansions or extremely large ranges. Each intermediate step of the algorithm would allocate temporary arrays. In the vulnerable version (prior to 1.1.18 / 2.1.4 / 3.0.6 / 5.0.9), there was no mechanism to bound these intermediate allocations, allowing memory exhaustion.
Real-World Impact
For applications that:
- Process user-provided glob patterns in build systems
- Accept filename patterns from external sources
- Use brace-expansion indirectly through glob, minimatch, or similar libraries
- Run in memory-constrained environments (containers, serverless functions)
...an attacker could trigger a denial of service simply by providing a maliciously crafted brace expansion pattern. In a CI/CD pipeline, this could block all builds. In a web service, this could crash worker processes.
The Fix
The fix addresses this vulnerability by implementing bounded intermediate array allocation with strict limits on expansion size at every step of the algorithm, not just the final result.
Changes Made
The PR updated dependency versions across multiple package management files:
In package.json:
"pnpm": {
"overrides": {
- "whatwg-url": "13.0.0"
+ "whatwg-url": "13.0.0",
+ "brace-expansion": "1.1.18"
},
In pnpm-lock.yaml:
overrides:
whatwg-url: 13.0.0
+ brace-expansion: 1.1.18
And crucially, the vulnerable versions were removed from the lock file:
- brace-expansion@1.1.12:
- resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
-
- brace-expansion@5.0.5:
- resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==}
- engines: {node: 18 || 20 || >=22}
+ brace-expansion@1.1.18:
+ resolution: {integrity: sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==}
How This Solves the Problem
The patched versions (1.1.18, 2.1.4, 3.0.6, and 5.0.9) implement checks that:
- Limit intermediate array size: During the expansion algorithm, before allocating a temporary array, the code now checks if the size would exceed a reasonable bound
- Fail early: If a pattern would require excessive expansion, the function raises an error rather than attempting to allocate unbounded memory
- Preserve valid expansions: Legitimate brace expansion patterns still work correctly—only pathologically large patterns are rejected
The use of npm overrides ("brace-expansion": "1.1.18" in package.json) ensures that even if other dependencies specify older versions, they will all receive the patched version. This is crucial because brace-expansion appears transitively in many dependency chains.
The change is "behavior preserving" for legitimate inputs—all valid, real-world brace expansion patterns continue to work as expected. Only attack patterns are rejected.
Prevention & Best Practices
1. Keep Dependencies Updated Regularly
The most direct prevention is to maintain current versions of all dependencies. Set up automated dependency scanning with tools like:
- npm audit (built-in)
- Snyk (continuous monitoring)
- Trivy (the scanner that detected this CVE)
2. Use npm Overrides for Transitive Dependencies
When a vulnerability exists in a transitive dependency (as with brace-expansion), use overrides to enforce a patched version:
{
"pnpm": {
"overrides": {
"brace-expansion": "1.1.18"
}
}
}
This prevents any version of brace-expansion other than the patched one from being installed, even if a different version is requested by a dependent package.
3. Validate External Input
Even with patches applied, apply defense-in-depth:
// Validate patterns before expansion
function safeBraceExpand(pattern) {
// Reject obviously malicious patterns
if (!pattern || pattern.length > 1000) {
throw new Error('Pattern too long');
}
// Reject patterns with excessive nesting depth
const nestingDepth = (pattern.match(/\{/g) || []).length;
if (nestingDepth > 5) {
throw new Error('Pattern too deeply nested');
}
try {
return braceExpand(pattern);
} catch (error) {
// Handle expansion failures gracefully
console.error('Brace expansion failed:', error);
return [pattern]; // Return pattern as-is if expansion fails
}
}
4. Monitor Resource Usage
In production, monitor memory usage during pattern expansion operations:
const memBefore = process.memoryUsage().heapUsed;
const expanded = braceExpand(userPattern);
const memAfter = process.memoryUsage().heapUsed;
if (memAfter - memBefore > THRESHOLD) {
console.warn('Suspicious memory allocation during expansion');
}
5. CWE and OWASP References
This vulnerability relates to:
- CWE-770: Allocation of Resources Without Limits or Throttling — the root cause
- OWASP A06:2021 Vulnerable and Outdated Components — the attack vector
- OWASP A01:2021 Broken Access Control — if pattern source is untrusted
Key Takeaways
-
CVE-2026-69152 is not just a regression: This vulnerability specifically bypasses the previous CVE-2026-14257 fix, demonstrating that the original mitigation was incomplete. If you patched only CVE-2026-14257, you're still vulnerable.
-
Intermediate arrays are the culprit: The attack doesn't rely on the final expanded array size—it exploits the temporary arrays created during the expansion algorithm itself. Bounds checking must occur at every step, not just at the end.
-
npm overrides are essential for transitive dependencies: Because brace-expansion appears deep in dependency trees (often indirectly), you can't rely on direct dependency updates alone. Use
pnpmoverrides ornpmresolutions to enforce the patched version globally. -
Legitimate patterns continue to work: The fix doesn't change behavior for real-world brace expansion use cases—only pathologically large patterns designed for exploitation are rejected.
-
Defense-in-depth applies here too: Even with the patch installed, validating user-provided patterns and monitoring resource usage adds extra protection against DoS attacks.
How Orbis AppSec Detected This
Source: Untrusted brace expansion patterns originating from user-provided filenames, configuration values, or CLI arguments that eventually reach brace-expansion library calls through glob(), minimatch, or direct function invocations.
Sink: The internal expansion algorithm in brace-expansion package, specifically the array allocation loops within the expansion handler (pre-1.1.18 versions).
Missing control: Input validation on pattern length and nesting depth; bounds checking on intermediate array allocation during the expansion algorithm; no early-exit mechanism when allocation would exceed reasonable limits.
CWE: CWE-770 (Allocation of Resources Without Limits or Throttling)
Fix: Upgrade brace-expansion to 1.1.18, 2.1.4, 3.0.6, or 5.0.9 (depending on your major version in use) which implements strict bounds on intermediate array allocation and fails early when patterns would cause excessive memory use.
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-69152 is a sobering reminder that security fixes sometimes require iterative refinement. The first patch for brace-expansion addressed one attack vector, but attackers adapted by exploiting intermediate array allocation. This vulnerability demonstrates the importance of:
- Following up on previous CVEs — if you fixed CVE-2026-14257, ensure you also fix CVE-2026-69152
- Automated dependency scanning — catching these issues before they reach production
- Defense-in-depth — combining multiple protective layers (patched code, input validation, monitoring)
- Transitive dependency management — using npm overrides to enforce security patches across your entire dependency tree
By upgrading brace-expansion and implementing the practices outlined above, you eliminate a critical denial of service vector in your applications. Security is an ongoing process, and staying current with patches is the foundation of that process.