Sep 30, 2026·7 min read·1 visit
A denial-of-service vulnerability in brace-expansion allows unauthenticated remote attackers to crash Node.js applications by submitting maliciously nested or long comma-separated brace patterns, bypassing application-level limits.
CVE-2026-102276 is a high-severity Denial of Service (DoS) vulnerability impacting the 'brace-expansion' library, a popular Node.js utility designed to expand brace patterns into combinatorial lists. Due to uncontrolled recursion and argument-list stack exhaustion within the internal parseCommaParts function, remote attackers can trigger an unhandled RangeError that abruptly terminates the Node.js process.
The npm package brace-expansion is a structural library used to generate combinatorial string expansions, such as translating file{1..3}.txt into distinct filenames. Because it serves as a core utility, it is transitively incorporated into widely used matching and file-system globbing dependencies such as minimatch and glob. Consequently, applications processing user-controlled file paths, query filters, or router patterns are indirectly exposed to inputs parsed by this library.
CVE-2026-102276 is a high-severity Denial of Service vulnerability arising from the package's internal parsing methodology. The parser exposes an attack surface where maliciously structured strings trigger uncontrolled resource consumption before any configuration-level size or length checks can occur. This makes traditional mitigations, such as configuring maximum output boundaries, completely ineffective.
An attacker can exploit this weakness by submitting targeted payloads that drive the V8 JavaScript engine's call stack to exhaustion. This leads to an unhandled RangeError: Maximum call stack size exceeded and terminates the host process.
The root cause of CVE-2026-102276 lies within the internal parseCommaParts function, which splits comma-delimited segments of a brace-enclosed string while preserving internal nested brace blocks.
The parsing engine uses a helper library named balanced to identify matching pairs of curly braces, separating the input string into a prefix (pre), a nested body (body), and a suffix (post). Once identified, the engine executes recursive operations and array flattening techniques that are vulnerable to stack depletion.
There are two distinct exhaustion vectors within the unpatched implementation:
Vector 1: Uncontrolled Recursion (CWE-674)
When evaluating a pattern, the function extracts the outermost balanced group and invokes itself recursively on the remaining suffix:
var postParts = parseCommaParts(post);
For highly nested or chained sequences of brace groupings (such as thousands of sequential nested blocks), each recursive iteration pushes a new execution context onto the call stack. Because this parsing occurs prior to output length validations, V8's physical stack limit is breached at approximately 7,000 nested structures, resulting in a process crash.
Vector 2: Argument-List Stack Exhaustion (CWE-400)
When flattening flat arrays of comma-delimited parts, the library utilizes JavaScript's native argument application mechanism:
p.push.apply(p, postParts);
In V8, calling .apply(context, array) maps the elements of the target array directly onto the CPU-managed argument registry stack. V8 imposes a finite limit on the maximum length of an argument list. If an attacker passes a payload containing more than 125,000 flat, comma-separated values, calling .apply tries to allocate all entries onto the call stack at once. This triggers an immediate, non-recursive stack overflow.
Analyzing the vulnerable implementation highlights the structural difference between recursive and iterative design patterns.
function parseCommaParts(str) {
if (!str)
return [''];
var parts = [];
var m = balanced('{', '}', str);
if (!m)
return str.split(',');
var pre = m.pre;
var body = m.body;
var post = m.post;
var p = pre.split(',');
p[p.length-1] += '{' + body + '}';
// CRITICAL FLUSH: Recursive execution pushes a new stack frame for each 'post' element
var postParts = parseCommaParts(post);
if (post.length) {
p[p.length-1] += postParts.shift();
// CRITICAL FLUSH: Large arrays exhaust argument registers on the CPU stack
p.push.apply(p, postParts);
}
parts.push.apply(parts, p);
return parts;
}The resolved code entirely decouples parser progress from the JavaScript call stack. The recursion is flattened into an iterative state loop, and .apply() allocations are replaced by an iterative index-based helper:
// Safe replacement for push.apply, preserving flat execution
function pushAll(target, items) {
for (var i = 0; i < items.length; i++) {
target.push(items[i]);
}
}
function parseCommaParts(str) {
var parts = [];
var carry = '';
// Iterative processing loop reduces stack depth consumption to O(1)
for (;;) {
var m = balanced('{', '}', str);
if (!m) {
var tail = str.split(',');
tail[0] = carry + tail[0];
pushAll(parts, tail); // Safe execution
return parts;
}
var pre = m.pre;
var body = m.body;
var post = m.post;
var p = pre.split(',');
p[0] = carry + p[0];
p[p.length-1] += '{' + body + '}';
if (!post.length) {
pushAll(parts, p);
return parts;
}
carry = p.pop();
pushAll(parts, p);
str = post; // Advances state forward without recursing
}
}Exploitation of CVE-2026-102276 requires no special system authentication or privileges. It requires only that the target application accepts and parses user-supplied pattern matching strings.
An attacker can trigger Vector 1 by supplying a payload consisting of deep sequential brace nests:
const expand = require('brace-expansion');
// Construct 7,000 nested brace chains within an outer brace block
const recursivePayload = '{' + '{a},'.repeat(7000) + 'b}';
console.log("Executing recursive payload...");
expand(recursivePayload); // Node.js crashes with RangeErrorTo trigger Vector 2, the attacker forces the system to execute an excessively wide split operation, overloading the function's argument processing stack:
const expand = require('brace-expansion');
// Generate a massive array of flat comma values
const argumentPayload = '{{x},' + 'a,'.repeat(125000) + 'b}';
console.log("Executing argument list payload...");
expand(argumentPayload); // Node.js crashes with RangeErrorThe security impact of CVE-2026-102276 is categorized as a High Severity Denial of Service (DoS) vulnerability. Node.js operates on a single-threaded event loop. If an unhandled execution stack overflow occurs within this thread, the entire process crashes immediately.
While process monitors like PM2 or container runtimes such as Kubernetes are configured to restart crashed processes, an attacker can exploit this to create a continuous crash loop. This loop exhausts system resources, increases host CPU utilization, and results in a sustained service outage for legitimate users.
Furthermore, because the parsing engine does not implement validation checks prior to recursive execution, security constraints placed on downstream processing logic cannot intercept or mitigate this issue. This exposes the application layer to direct, unauthenticated disruption.
Remediation requires upgrading the brace-expansion library to fixed versions that use iterative parsing logic.
Update the dependency based on the major version branch currently in use:
Because brace-expansion is often pulled in transitively by upstream utilities like minimatch, you can enforce the use of safe versions across your entire project. To do this, add a resolutions or overrides block to your package.json file:
"overrides": {
"brace-expansion": "^5.0.10"
}If immediate dependency patching is not possible, implement validation checks on incoming user strings before passing them to matching functions. Reject inputs that exceed a maximum nesting depth or string length threshold:
function validatePattern(input) {
// Reject inputs with excess brace characters to block nesting payloads
const braceCount = (input.match(/{/g) || []).length;
if (braceCount > 100) {
throw new Error('Invalid query pattern: Nesting depth exceeded');
}
return true;
}CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
brace-expansion Julian Gruber | < 1.1.19 | 1.1.19 |
brace-expansion Julian Gruber | >= 2.0.0, < 2.1.5 | 2.1.5 |
brace-expansion Julian Gruber | >= 3.0.0, < 3.0.7 | 3.0.7 |
brace-expansion Julian Gruber | >= 4.0.0, < 5.0.10 | 5.0.10 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-400, CWE-674 |
| Attack Vector | Network (AV:N) |
| CVSS v3.1 | 7.5 (High) |
| EPSS Score | 0.0035 (26.16th percentile) |
| Impact Category | Denial of Service (Process Termination) |
| Exploit Status | Proof-of-concept available |
| CISA KEV Status | Not listed |
The software does not properly control the allocation and maintenance of a limited resource, enabling an actor to influence the amount of resources consumed and leading to resource exhaustion.
A stack-based Denial of Service (DoS) vulnerability via uncontrolled recursion in the brace-expansion library prior to versions 1.1.20, 2.1.6, 3.0.8, and 5.0.11 allows unauthenticated remote attackers to trigger native stack exhaustion, terminating the Node.js process via a crafted payload containing deeply nested brace groups.
An uncontrolled resource consumption vulnerability exists in the brace-expansion JavaScript library. Due to an algorithmic flaw in parsing a legacy Bash-compatibility quirk involving {a},b}-shaped expansion structures, untrusted inputs containing many trailing closing braces trigger successive full-input rescans. This behavior yields quadratic CPU time complexity and high memory overhead, allowing remote, unauthenticated attackers to cause a Denial of Service (DoS) by blocking the single-threaded Node.js event loop.
Moment.js versions 2.29.2 through 2.30.1 are vulnerable to a Path Traversal flaw (CWE-27) on server-side Node.js environments when dynamic locales are configured. The vulnerability stems from an object-coercion bypass in the locale-name sanitization routine, which assumes incoming variables are string primitives. An attacker can pass a structured object with custom 'match' and 'toString' properties to bypass regex-based directory checks, leading to arbitrary file loading via Node's internal 'require()' call.
A denial-of-service vulnerability exists in the ip-address npm package prior to version 10.7.1. The library fails to limit the length of input strings parsed by the Address4 and Address6 constructors. When parsing highly malformed addresses, the diagnostic parser runs a synchronous regular expression search-and-replace that generates descriptive HTML error messages. Passing an excessively long string containing invalid characters causes severe memory amplification and CPU starvation, resulting in a thread hang or process crash in Node.js applications.
The ip-address library is vulnerable to a logical bypass in its subnet containment methods. The functions isInSubnet() and isHostInSubnet() do not verify that compared addresses belong to the same IP family before performing masking checks. Under specific circumstances, an IPv6 address can share leading bit patterns with an IPv4 subnet, causing the containment check to evaluate as true. This allows attackers to bypass access control lists, firewalls, and server-side request forgery protection layers in applications relying on the library.
CVE-2026-101894 is a critical path traversal vulnerability in @xhmikosr/decompress before versions 10.2.2 and 11.1.4, stemming from an incomplete hardening bypass of CVE-2026-53486 where static lexical containment checks fail to detect kernel-level resolution of crafted symlink chains, allowing arbitrary local file modification and execution.