Skip to content

Update dependency brace-expansion@1 to v5 [SECURITY] - autoclosed#228

Closed
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/npm-brace-expansion1-vulnerability
Closed

Update dependency brace-expansion@1 to v5 [SECURITY] - autoclosed#228
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/npm-brace-expansion1-vulnerability

Conversation

@renovate

@renovate renovate Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Change Age Confidence
brace-expansion@1 ^1.1.16^5.0.0 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@1)

v5.0.8

Compare Source

v5.0.7

Compare Source

v5.0.6

Compare Source

v5.0.5

Compare Source

v5.0.4

Compare Source

v5.0.3

Compare Source

v5.0.2

Compare Source

v4.0.1

Compare Source


v4.0.0

Compare Source

As a precaution to not risk breaking anything with 278132b, this is a new semver major release

v3.0.2

Compare Source

v3.0.1

Compare Source


v3.0.0

Compare Source

v2.1.2

Compare Source

v2.1.1

Compare Source


v2.1.0

Compare Source

v2.0.3

Compare Source

v2.0.2

Compare Source


v2.0.1

Compare Source

v2.0.0

Compare Source


Configuration

📅 Schedule: (in timezone UTC)

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

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update 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 mishushakov as a code owner July 23, 2026 16:57
@cla-bot cla-bot Bot added the cla-signed label Jul 23, 2026
@renovate renovate Bot changed the title Update dependency brace-expansion@1 to v5 [SECURITY] Update dependency brace-expansion@1 to v5 [SECURITY] - autoclosed Jul 23, 2026
@renovate renovate Bot closed this Jul 23, 2026
@renovate
renovate Bot deleted the renovate/npm-brace-expansion1-vulnerability branch July 23, 2026 17:00

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 959a2e08ba

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread package.json
"js-yaml@3": "^3.15.0",
"js-yaml@4": "^4.3.0",
"brace-expansion@1": "^1.1.16",
"brace-expansion@1": "^5.0.0",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep minimatch 3 on the 1.x brace-expansion API

This override forces the existing minimatch@3.1.5 entries in pnpm-lock.yaml to resolve brace-expansion: 5.0.7; however minimatch 3 treats require('brace-expansion') itself as the callable expansion function, while brace-expansion 5 exposes expand as a named export. In any repo script or dependency path that asks minimatch 3 to expand a brace pattern, this becomes TypeError: expand is not a function, so the security override should stay on the patched 1.x line rather than crossing the major API boundary.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants