Skip to content

chore(deps): update dependency brace-expansion@>=4.0.0 <5.0.5 to v5.0.7 [security] - #6379

Merged
renovate[bot] merged 1 commit into
mainfrom
renovate/npm-brace-expansion-=4.0.0-5.0.5-vulnerability
Jul 26, 2026
Merged

chore(deps): update dependency brace-expansion@>=4.0.0 <5.0.5 to v5.0.7 [security]#6379
renovate[bot] merged 1 commit into
mainfrom
renovate/npm-brace-expansion-=4.0.0-5.0.5-vulnerability

Conversation

@renovate

@renovate renovate Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Change Age Confidence
brace-expansion@>=4.0.0 <5.0.5 [5.0.55.0.7](https://renovatebot.com/diffs/npm/brace-expansion@>=4.0.0 <5.0.5/5.0.5/5.0.7) age confidence

brace-expansion: DoS via exponential-time expansion of consecutive non-expanding {} groups

CVE-2026-13149 / GHSA-3jxr-9vmj-r5cp

More information

Details

Summary

brace-expansion's expand() exhibits exponential-time - O(2ⁿ) - behavior in the number of consecutive non-expanding {} groups. A short, all-ASCII input (~90 bytes/30 groups) blocks the calling thread for minutes; a slightly longer input hangs it effectively indefinitely. Because the dominant consumers run on Node's single-threaded event loop, one small input can fully stall a worker/process.

In expand_, post is computed unconditionally at the top of the function, before the early-return branches that don't use it:

const post = m.post.length ? expand_(m.post, max, false) : [''];   // always recurses
  ...
if (!isSequence && !isOptions) {
  if (m.post.match(/,(?!,).*\}/)) {
    str = m.pre + '{' + m.body + escClose + m.post;
    return expand_(str, max, true); // restart — `post` discarded
  }
  return [str];
}

For input like a{},{},…, the first {} is non-expanding, so control reaches the {a},b} rewrite branch - but expand_ has already recursed into post over the entire remaining tail, only to throw the result away.
Each level therefore spawns two recursive expansions over essentially the same remaining work: T(n) = 2·T(n−1) ⇒ O(2ⁿ).

The max option does not mitigate this: max only bounds the output-building loops; neither the post recursion nor the rewrite recursion consults it.

Measured on 5.0.6:

groups (n) input bytes time
20 60 130 ms
24 72 1.9 s
26 78 7.8 s
30 (PoC) 90 ~2 min
Proof of concept
const { expand } = require('brace-expansion');
// 30 non-expanding groups, ~90 bytes — blocks for minutes:
expand('a{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}');
Impact

Any application that passes attacker-influenced strings to brace-expansion.expand() - directly or transitively via minimatch/glob brace patterns - can be driven into a multi-minute-to-indefinite CPU hang by a tiny request, denying service on that thread/process.

Remediation

Upgrade to a patched release. The fix:

  1. Defers computing post until after the early-return branches (and computes it locally in the $-suffix branch), so post is only expanded when a brace set actually expands and the value is used. This alone removes the exponential.
  2. Converts the {a},b} rewrite from recursion to an in-function loop, so a long run of rewrites cannot grow the call stack.

Verified: the PoC drops from ~2 min to 0.55 ms, 5,000 groups complete in ~344 ms, and output is identical to 5.0.6 across a behavioral-equivalence suite (sequences, padding, $-prefix, a{},b}c, {},a}b, x{{a,b}}y, etc.). Post-fix complexity is ~O(n²) on this input class - acceptable for the security fix; a linear rewrite can be a non-urgent follow-up.

If immediate upgrade isn't possible, avoid passing untrusted input to expand() / glob brace patterns, or run such expansion under a timeout/worker.

Severity

  • CVSS Score: 7.7 / 10 (High)
  • Vector String: CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:P/S:N/AU:Y/R:U/V:D/RE:M/U:Amber

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Release Notes

juliangruber/brace-expansion (brace-expansion@>=4.0.0 <5.0.5)

v5.0.7

Compare Source

v5.0.6

Compare Source


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Enabled.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about these updates again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate
renovate Bot requested a review from a team as a code owner July 26, 2026 02:18
@renovate
renovate Bot enabled auto-merge (squash) July 26, 2026 02:18
@alwaysmeticulous

alwaysmeticulous Bot commented Jul 26, 2026

Copy link
Copy Markdown

✅ Meticulous spotted 0 visual differences across 289 screens tested: view results.

Meticulous evaluated ~4 hours of user flows against your PR.

Expected differences? Click here. Last updated for commit 8d88db4 chore(deps): update dependency brace-expansion@>=4.0.0 <5.0.5 to v5.0.7 .... This comment will update as new commits are pushed.

@renovate
renovate Bot force-pushed the renovate/npm-brace-expansion-=4.0.0-5.0.5-vulnerability branch from c8dc777 to 523f1d4 Compare July 26, 2026 05:07
@renovate
renovate Bot force-pushed the renovate/npm-brace-expansion-=4.0.0-5.0.5-vulnerability branch from 523f1d4 to 8d88db4 Compare July 26, 2026 08:34
@renovate
renovate Bot merged commit 4d9a7b6 into main Jul 26, 2026
38 checks passed
@renovate
renovate Bot deleted the renovate/npm-brace-expansion-=4.0.0-5.0.5-vulnerability branch July 26, 2026 12:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants