fix: patch brace-expansion DoS vulnerability (CVE-2026-13149) - #2695
fix: patch brace-expansion DoS vulnerability (CVE-2026-13149)#2695jainakanksha-msft with Copilot wants to merge 3 commits into
Conversation
|
Done (commit
|
|
@copilot, do you have any other suggestions to fix the vulnerability. |
Here's a review of all viable approaches with pros and cons: Option 1 — Current:
|
| ✅ | Surgical — only touches the one vulnerable version |
| ✅ | No changes to direct dependencies or test behavior |
| ✅ | Standard npm feature designed for exactly this use case |
| Version-specific: if mocha ever pins a different pre-2.1.2 version, the override won't catch it | |
overrides can be surprising to new contributors unfamiliar with the context |
Option 2 — Broader npm overrides
"overrides": { "brace-expansion": "^2.1.2" }How it works: Forces all brace-expansion resolutions to >=2.1.2, regardless of what any transitive dependency requests.
| ✅ | More future-proof — covers any attempt to use a pre-2.1.2 version |
| ✅ | No changes to direct dependencies or test behavior |
Overrides brace-expansion for glob and minimatch (v1) as well — those currently use unrelated major versions (5.x and 1.x), so constraining them to ^2.1.2 could break their glob/expand behaviour |
Verdict: Riskier than Option 1 because it would force the
glob@10/minimatch@3paths onto a version they were not designed for.
Option 3 — Add brace-expansion as a direct devDependency
"devDependencies": { "brace-expansion": "^2.1.2" }How it works: npm hoisting gives the explicit direct dependency priority over transitive resolutions.
| ✅ | Visible in devDependencies — intent is clear |
Adding a package you never directly require() is considered bad practice |
|
| Hoisting behaviour can differ in workspaces/monorepos and non-npm package managers |
Option 4 — Upgrade mocha 10 → 11 (recommended)
mocha 11 uses minimatch@^9.0.5, which requires brace-expansion@^2.0.2. npm resolves this to 2.1.4 naturally — no override needed.
Node engine compatibility: mocha 11 requires ^18.18.0 || ^20.9.0 || >=21.1.0. Azurite already requires >=21.0.0, so this is fully compatible.
| ✅ | Eliminates the vulnerability cleanly by updating the direct dependency |
| ✅ | No override hacks — dependency tree is transparent |
| ✅ | Stays on the actively-maintained mocha release line |
Major version bump — mocha 11 has some breaking changes (e.g. --reporter-option flag renamed, --timeout default unchanged but some internal APIs changed). Existing Azurite tests would need to be verified. |
|
| Higher risk change requiring test-run validation |
Recommendation
If you prefer zero test-runner risk: Option 1 (current) is the standard, idiomatic npm way to patch a transitive vulnerability. The overrides field exists precisely for this purpose and is widely used (the repo already uses it for undici).
If you prefer no override hacks at all: Option 4 (upgrade mocha to 11) is the cleanest long-term solution, but requires verifying the full test suite still passes.
Let me know which direction you'd like to take and I'll implement it.
|
@copilot , 1. rebase the branch with main |
brace-expansion@2.1.1(transitive viamocha → minimatch) has an O(2ⁿ) CPU-hang vulnerability where consecutive non-expanding{}groups cause exponential recursion, allowing a ~90-byte input to block the event loop for minutes.Changes
package.json: Added targeted npm override to replacebrace-expansion@2.1.1with2.1.2:package-lock.json: Regenerated — allbrace-expansion@2.xinstances now resolve to2.1.4;brace-expansionno longer appears innpm audit.Reachability Assessment
Not reachable at runtime (medium confidence). The vulnerable package is a transitive dev dependency only (
mocha→minimatch→brace-expansion). Azurite's production code usesglob-to-regexpfor pattern matching and never callsbrace-expansion.expand()on user input. This fix eliminates the scanner alert and mitigates any exposure during test runs.Original prompt
This section details the Dependabot vulnerability alert you should resolve
<alert_title>brace-expansion: DoS via exponential-time expansion of consecutive non-expanding {} groups</alert_title>
<alert_description>### 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_,postis computed unconditionally at the top of the function, before the early-return branches that don't use it: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:
Proof of concept
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:
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.</alert_description>
high
https://github.com/juliangruber/brace-expansion/security/advisories/GHSA-3jxr-9vmj-r5cp https://nvd.nist.gov/vuln/detail/CVE-2026-13149 https://github.com/juliangruber/brace-expansion/pull/122 https://github.com/juliangruber/brace-expansion/pull/123 https://github.com/juliangruber/brace-expansion/commit/835d6be91201122d9adffb0c0c8c094189ace265 https://github.com/juliangruber/brace-expansion/commit/c7e33ec13ac1a684c116720843ce24e208611754 https://github.com/juliangruber/brace-expansion/commit/d74e63030c012e3b7ae81657b8d665619cd51b95 https://github.com/juliangruber/brace-expansion/releases/tag/v1.1.16 https://github.com/juliangruber/brace-expansion/releases/tag/v2.1.2 https://github.com/juliangruber/brace-expansion/releases/tag/v5.0.7 https://www.npmjs.com/package/brace-expansion https://github.com/advisories/GHSA-3jxr-9vmj-r5cpGHSA-3jxr-9vmj-r5cp, CVE-2026-13149
brace-expansion
npm
<vulnerable_versions>2.1.1</vulnerable_versions>
<patched_version>2.1.2</patched_version>
<manifest_path>package-lock.json</manifest_path>
<agent_instructions>@copilot please go through the issues mentioned here, identify all issues, and assess whether they can be fixed.
Recommend the necessary changes.
If it is not a breaking change, let's log the issue.
Please verify all test cases and validate the runs.
</agent_instructions>
<task_instructions>Resolve this alert by updating the affected package to a non-vulnerable version. Prefer the lowest non-vulnerable version (see the patched_version field above) over the latest to minimize breaking changes. Include a Reachability ...