l3montree-dev / l3montree-dev/devguard-documentation
GHSA-mh99-v99m-4gvg found in npm/brace-expansion@5.0.7
Nobody has claimed this yet.
- Dominant language
- MDX
- Stars
- 7
- Forks
- 5
- Avg merge
- 1d 21h
- Merged PRs (30d)
- 13
Description
GHSA-mh99-v99m-4gvg found in npm/brace-expansion@5.0.7
[!important]
Risk:3.47 (Low)
CVSS:7.5
Description
Summary
expand() bounds the number of results it produces (the max option,
100_000 by default) but not their length. By chaining many brace groups,
an attacker keeps the result count under max while making every result grow
with the number of groups. Building max long results — plus the intermediate
arrays combined at each brace group — exhausts memory and crashes the Node
process with an uncatchable out-of-memory error. try/catch around
expand() does not help: the fatal error terminates the process.
A ~7.5 KB input ('{a,b}'.repeat(1500)) is enough to crash a default Node
process.
Details
For N chained brace groups such as '{a,b}'.repeat(N):
- the result count is
2^N, immediately capped atmax(100_000), so the
maxprotection appears to hold, but - each result is
Ncharacters long, so the total output size is
max × Ncharacters, which grows without bound inN.
expand_ combines each brace set with the fully-expanded tail:
const post = m.post.length ? expand_(m.post, max, false) : ['']
...
for (let j = 0; j < N.length; j++) {
for (let k = 0; k < post.length && expansions.length < max; k++) {
const expansion = pre + N[j] + post[k] // grows one group longer per level
...
expansions.push(expansion)
}
}
The loop guard expansions.length < max limits how many strings are built, but
nothing limits how long they get. Each recursion level materializes another
array of up to max strings, one character longer than the level below, and —
because V8 represents pre + N[j] + post[k] as a cons-string (rope) that
references post[k] — those intermediate strings stay reachable through the
whole chain. Memory therefore scales with max × N.
Measured on 5.0.7 ('{a,b}'.repeat(N), default max):
| groups (N) | input bytes | result count | peak RSS |
|---|---|---|---|
| 20 | 100 | 100,000 | ~80 MB |
| 50 | 250 | 100,000 | ~214 MB |
| 100 | 500 | 100,000 | ~409 MB |
| 300 | 1,500 | 100,000 | ~1,148 MB |
| 1500 | 7,500 | — | OOM crash |
Proof of concept
const { expand } = require('brace-expansion')
// ~7.5 KB input — crashes the process with a fatal, uncatchable OOM:
// FATAL ERROR: ... JavaScript heap out of memory
try {
expand('{a,b}'.repeat(1500))
} catch (e) {
// never reached — the process is already dead
}
Impact
Any application that passes attacker-influenced strings to
brace-expansion.expand() — directly, or transitively via minimatch / glob
brace patterns — can be crashed by a small request. Because the failure is a
fatal V8 out-of-memory error rather than a thrown exception, it cannot be caught
and it takes down the whole worker/process, denying service.
Remediation
Upgrade to a patched release. The fix bounds the total number of characters a
single expand() call may accumulate (EXPANSION_MAX_LENGTH, default
4_000_000, configurable via a new maxLength option), applied inside the
output-building loops so intermediate arrays are bounded too. Once the limit is
reached, output is truncated — consistent with how max already truncates —
instead of growing without bound. The limit sits well above any realistic
expansion (100,000 results hitting max measure ~1M characters), so legitimate
input is unaffected.
After the fix, '{a,b}'.repeat(1500) returns a bounded, truncated result in
~0.7 s using ~340 MB and never crashes, including under a constrained 512 MB
heap.
The fix bounds memory but the algorithm still rebuilds intermediate arrays at
each level (roughly O(N × maxLength) work on this input class). A streaming
rewrite that produces output in O(total output size) can be a non-urgent
follow-up.
If immediate upgrade isn't possible, avoid passing untrusted input to
expand() / glob brace patterns, or pass a small explicit max and
maxLength.
Affected component
The vulnerability is in pkg:npm/brace-expansion@5.0.7, found in artifacts pkg:oci/devguard-documentation?repository_url=ghcr.io/l3montree-dev/devguard-documentation&arch=amd64&tag=main-amd64.
Recommended fix
Upgrade to version 5.0.8 or later.
# Update all vulnerable npm packages
npm audit fix
# Update only this package
npm install brace-expansion@5.0.8
Additional guidance for mitigating vulnerabilities
Visit our guides on devguard.org
See more details...
Path to component
%%{init: { 'theme':'base', 'themeVariables': {
'primaryColor': '#F3F3F3',
'primaryTextColor': '#0D1117',
'primaryBorderColor': '#999999',
'lineColor': '#999999',
'secondaryColor': '#ffffff',
'tertiaryColor': '#ffffff'
} }}%%
flowchart TD
Your_application(["Your application"]) --- pkg_npm_brace_expansion_5_0_7(["pkg:npm/brace-expansion\@5.0.7"])
classDef default stroke-width:2px
| Risk Factor | Value | Description |
|---|---|---|
| Vulnerability Depth | 1 |
The vulnerability is in a direct dependency of your project. |
| EPSS | 0.64 % |
The exploit probability is very low. The vulnerability is unlikely to be exploited in the next 30 days. |
| EXPLOIT | Not available |
We did not find any exploit available. Neither in GitHub repositories nor in the Exploit-Database. There are no script kiddies exploiting this vulnerability. |
| CVSS-BE | 7.5 |
- Exploiting this vulnerability significantly impacts availability. |
| CVSS-B | 7.5 |
- The vulnerability can be exploited over the network without needing physical access. - It is easy for an attacker to exploit this vulnerability. - An attacker does not need any special privileges or access rights. - No user interaction is needed for the attacker to exploit this vulnerability. - The impact is confined to the system where the vulnerability exists. - There is a high impact on the availability of the system. |
More details can be found in DevGuard
Interact with this vulnerability
You can use the following slash commands to interact with this vulnerability:
👍 Reply with this to acknowledge and accept the identified risk.
/accept I accept the risk of this vulnerability, because ...
⚠️ Mark the risk as false positive: Use one of these commands if you believe the reported vulnerability is not actually a valid issue.
/component-not-present The vulnerable component is not included in the artifact.
/vulnerable-code-not-present The component is present, but the vulnerable code is not included or compiled.
/vulnerable-code-not-in-execute-path The vulnerable code exists, but is never executed at runtime.
/vulnerable-code-cannot-be-controlled-by-adversary Built-in protections prevent exploitation of this vulnerability.
/inline-mitigations-already-exist The vulnerable code cannot be controlled or influenced by an attacker.
🔁 Reopen the risk: Use this command to reopen a previously closed or accepted vulnerability.
/reopen ...
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running npm audit fix and inspect the reported brace-expansion dependency. If it is not updated automatically, run npm install brace-expansion@5.0.8; done means the project no longer uses version 5.0.7 and the vulnerability report is resolved.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js
- Domain
- security
- Issue type
- Bug
- Difficulty
- 1/5
- Estimated time
- Under an hour
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 82/100