Vulnerability details

7.5/10
CVSS 3.1
GHSA-rgw5-rvv9-x895
not_affected Published 2026-08-03 Updated 2026-08-03
Vulnerability Data from Dependency-Track
Description
### 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 width | input bytes | results kept | time (5.0.8) | time (patched) | |---|---|---|---|---| | 20,000 | 20 KB | 199 | ~7.3 s | ~20 ms | | 100,000 | 100 KB | 39 | ~32 s | ~20 ms | | 400,000 | 400 KB | 9 | ~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](https://numyra.ai/). The sequence-generation issue was found while verifying that report.
Same vulnerability as
CVE-2026-69152 not_affected according to EUVD-2026-52370
Analysis Read only

CVE is a trademark and the CVE logo is a registered trademark of The MITRE Corporation. CVE data may be incomplete, delayed, or inaccurate. This website and all information provided are offered on an “as is” basis without warranties of any kind. This site is not affiliated with, endorsed by, or sponsored by the CVE Program or The MITRE Corporation.

This site uses data from the National Vulnerability Database (NVD), obtained through Dependency-Track, but is not endorsed or certified by the NVD or the National Institute of Standards and Technology (NIST). NVD data may be incomplete, delayed, or inaccurate.

CVSS (Common Vulnerability Scoring System) is owned and managed by FIRST.Org, Inc. and used by permission. The links on CVSS vectors open the FIRST CVSS calculators (CVSS 3.0, 3.1 and 4.0), or the NVD calculator for CVSS 2.0. This site is not affiliated with, endorsed by, or sponsored by FIRST.

GitHub is a trademark of GitHub, Inc. GHSA identifiers and advisory data come from the GitHub Advisory Database, licensed under CC-BY 4.0. GHSA data may be incomplete, delayed, or inaccurate. This site is not affiliated with, endorsed by, or sponsored by GitHub, Inc.

Source of the EUVD data: European Vulnerability Database (EUVD), © European Union Agency for Cybersecurity (ENISA), reused with acknowledgement of the source as provided by the ENISA legal notice and IPR policy. Changes: CVEManager does not display the EUVD records as such; it uses their aliases to link the CVE and GHSA identifiers designating the same vulnerability. EUVD data may be incomplete, delayed, or inaccurate. This site is not affiliated with, endorsed by, or sponsored by ENISA or the European Union.