Skip to main content

brace-expansion CVE-2026-69152

| EUVDEUVD-2026-52370 HIGH
Uncontrolled Resource Consumption (CWE-400)
2026-08-03 https://github.com/juliangruber/brace-expansion GHSA-rgw5-rvv9-x895
7.5
CVSS 3.1 · Vendor: https://github.com/juliangruber/brace-expansion
Share

Severity by source

Vendor (https://github.com/juliangruber/brace-expansion) PRIMARY
7.5 HIGH
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
vuln.today AI
7.5 HIGH

Network vector and no privileges because any input channel suffices; High availability from uncatchable process crash; no confidentiality or integrity impact.

3.1 AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
4.0 AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
SUSE
HIGH
qualitative
Red Hat
7.5 HIGH
qualitative

Primary rating from Vendor (https://github.com/juliangruber/brace-expansion).

CVSS VectorVendor: https://github.com/juliangruber/brace-expansion

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
High

Lifecycle Timeline

3
Source Code Evidence Fetched
Aug 03, 2026 - 17:06 vuln.today
Analysis Generated
Aug 03, 2026 - 17:06 vuln.today
CVE Published
Aug 03, 2026 - 16:35 github-advisory
HIGH 7.5

Blast Radius

ecosystem impact
† from your stack dependencies † transitive graph · vuln.today resolves 4-path depth
  • 1 npm packages depend on brace-expansion (1 direct, 0 indirect)

Ecosystem-wide dependent count for version 2.0.0.

DescriptionCVE.org

Summary

The maxLength mitigation added in 5.0.8 for GHSA-mh99-v99m-4gvg / CVE-2026-14257 is incomplete. It bounds the accumulator where results are *combined*, but not the intermediate arrays that feed it. A ~25 KB input still crashes the Node process with an uncatchable out-of-memory error, so try/catch around expand() does not help.

A second, related path in the same function lets a ~400 KB input block the event loop for over two minutes without ever exceeding the memory bound.

Details

maxLength was enforced in combine(), the single place output grows. Two arrays are built *before* combine() runs, and neither was bounded.

1. Comma alternatives accumulate without a running total (memory exhaustion)

Each alternative in {a,b,c,...} is expanded by its own recursive expand_() call, so each receives a full, independent maxLength allowance. The results were then concatenated into a single values array with no cumulative limit:

js
values = []
for (let j = 0; j < n.length; j++) {
  values.push.apply(values, expand_(n[j], max, maxLength, false))
}

acc = combine(acc, pre, values, max, maxLength, ...)

With A alternatives, values can reach A * maxLength characters before combine() gets a chance to truncate it. At the default maxLength of 4,000,000 and 400 alternatives, that is well past any default heap.

2. Padded sequences ignore maxLength while generating (CPU exhaustion)

expandSequence() was bounded by max (the result *count*) but never consulted maxLength. A padded sequence's element width follows the input, so {0...01..100000} with a wide pad generates max elements, each as wide as the input, only for combine() to discard all but a handful.

Memory stays flat here, because V8 represents the padded strings as cons-strings, which is likely why this path was not caught alongside the original issue. The cost is time: work proportional to max * width.

pad widthinput bytesresults kepttime (5.0.8)time (patched)
20,00020 KB199~7.3 s~20 ms
100,000100 KB39~32 s~20 ms
400,000400 KB9~124 s~18 ms

Output is byte-identical before and after the fix; only the wasted work is removed.

Proof of concept

Memory exhaustion, against 5.0.8:

js
import { expand } from 'brace-expansion'

const part = '{' + '0'.repeat(50) + '1..100000}'
const input = '{' + Array(400).fill(part).join(',') + '}'  // ~25 KB

try {
  expand(input)
} catch (e) {
  // never reached - the process is already dead
}
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
Aborted

Event-loop stall, against 5.0.8:

js
import { expand } from 'brace-expansion'

// ~400 KB input, returns 9 results after roughly two minutes of blocking CPU
expand('{' + '0'.repeat(400_000) + '1..100000}')

Impact

Denial of service. Any application that passes attacker-controlled input to expand(), directly or transitively through a glob or pattern-matching library, can be remotely crashed or stalled. The out-of-memory variant terminates the process and cannot be handled with try/catch.

Applications already on 5.0.8 are affected: the 5.0.8 mitigation does not cover these paths.

Patches

Both intermediate arrays are now bounded as they are built, using the same max and maxLength limits already applied in combine():

  • values tracks a running result count and character length while alternatives are appended, and stops once either bound is reached.
  • expandSequence() accepts maxLength and stops generating once the sequence's own characters reach it.

As with the existing limits, output is truncated rather than allowed to grow without bound, which matches how max already behaves. The defaults sit well above any realistic expansion, so legitimate input is unaffected.

Workarounds

If upgrading is not immediately possible, avoid passing untrusted input to expand() or to glob brace patterns, or pass an explicitly small max and maxLength.

Note that a small maxLength alone was not sufficient on affected versions: it was applied per alternative rather than cumulatively, which is the root of the first issue above.

Credits

The memory-exhaustion bypass was reported by Alessio Della Libera, CEO & Co-founder at Numyra.

The sequence-generation issue was found while verifying that report.

AnalysisAI

Remote denial-of-service in the brace-expansion npm library (all active version lines prior to 1.1.18, 2.1.4, 3.0.6, and 5.0.9) allows unauthenticated attackers to either crash the Node.js process via an uncatchable out-of-memory error with a ~25 KB crafted input, or stall the event loop for over two minutes with a ~400 KB padded-sequence input. Both attack paths bypass the incomplete mitigation introduced in version 5.0.8 for CVE-2026-14257, meaning applications that already patched that predecessor vulnerability remain fully exposed. Publicly available proof-of-concept code is included in the GitHub security advisory; no CISA KEV listing is present at time of analysis.

Technical ContextAI

brace-expansion (pkg:npm/brace-expansion) is a JavaScript library that expands Unix brace-notation patterns such as {a,b,c} and {1..5} into arrays of strings; it is a transitive dependency of npm itself, minimatch, glob, and thousands of downstream packages. CWE-400 (Uncontrolled Resource Consumption) applies through two distinct intermediate-data-structure paths inside expand_(). In the first path, each comma-separated alternative in a {a,b,...} pattern is expanded by its own recursive expand_() call, each receiving a full independent maxLength budget, and all results are concatenated into a single values array with no cumulative running total - allowing values to reach A × maxLength characters before combine() ever checks bounds. In the second path, expandSequence() accepted a max (result-count) bound but never consulted maxLength, so padded-numeric sequences of arbitrary width were fully generated before combine() discarded excess results; V8's cons-string representation kept heap flat, hiding the cost as wall-clock CPU time rather than memory pressure. Both paths were introduced by the design of the 5.0.8 fix, which guarded only the single accumulator in combine() rather than the two intermediate arrays that feed it.

RemediationAI

Upgrade brace-expansion to the appropriate fixed release for the installed version line: 1.1.18, 2.1.4, 3.0.6, or 5.0.9. Because brace-expansion is typically installed as a transitive dependency, run npm ls brace-expansion or npm audit to identify all installed copies and the parent packages pinning them to vulnerable versions; upgrade those parent packages accordingly. Patch commits are available at https://github.com/juliangruber/brace-expansion/commit/139d015104e71433ad52a41d19467c48ecbb2c7d and https://github.com/juliangruber/brace-expansion/commit/1e30c930238d7162802d88a94189182def178dac. If immediate upgrade is not possible, the advisory recommends passing an explicitly small max and maxLength together to expand() - for example { max: 1000, maxLength: 10000 } - as a compensating control; however, note that maxLength alone was insufficient on affected versions because it was enforced per-alternative rather than cumulatively, which is the root cause of the first bug. The most reliable workaround is to avoid passing untrusted input to any function that transitively calls expand(), including glob pattern evaluation.

Vendor StatusVendor

SUSE

Severity: Important
Product Status
SUSE Liberty Linux 10 Fixed
SUSE Liberty Linux 8 Fixed
SUSE Linux Enterprise Server 16.0 Affected
SUSE Linux Enterprise Server 16.1 Affected
SUSE Linux Enterprise Server for SAP applications 16.0 Affected

Share

CVE-2026-69152 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy