Skip to content

fix: update brace-expansion to 2.1.4 within minimatch's declared range (CVE-2026-14257, CVE-2026-69152) - #2

Open
anupamme wants to merge 1 commit into
rsheyd:mainfrom
anupamme:fix-repo-google-docs-markdown-sync-cve-2026-14257-brace-expansion
Open

fix: update brace-expansion to 2.1.4 within minimatch's declared range (CVE-2026-14257, CVE-2026-69152)#2
anupamme wants to merge 1 commit into
rsheyd:mainfrom
anupamme:fix-repo-google-docs-markdown-sync-cve-2026-14257-brace-expansion

Conversation

@anupamme

@anupamme anupamme commented Sep 2, 2026

Copy link
Copy Markdown

Summary

Update brace-expansion from 2.1.2 to 2.1.4 — an in-range lockfile update. package.json is unchanged from main, and the overrides block from the original revision of this PR has been removed entirely.

The original revision forced brace-expansion@5.0.9 across six overrides entries. That was wrong: minimatch@9.0.9 declares brace-expansion@^2.0.2, and the cross-major override broke it at runtime, as @rsheyd demonstrated. This revision replaces that approach.

Advisories

Two advisories affect the installed 2.1.2:

Advisory CVE Severity Affected (2.x) Patched
GHSA-mh99-v99m-4gvg CVE-2026-14257 High (7.5, CWE-400/770) >=2.0.0 <2.1.3 2.1.3
GHSA-rgw5-rvv9-x895 CVE-2026-69152 High (7.5, CWE-400/770) >=2.0.0 <2.1.4 2.1.4

CVE-2026-69152 explicitly bypasses the CVE-2026-14257 mitigation, so 2.1.4 is the correct target — 2.1.3 would be re-flagged immediately.

Dependency path

brace-expansion has exactly one depender in the graph:

google-docs-markdown-sync
└─ googleapis@160.0.0                       (root dependency: ^160.0.0)
   └─ googleapis-common@8.0.3               (^8.0.0)
      └─ gaxios@7.1.3                       (exact pin "7.1.3")
         └─ rimraf@5.0.10                   (^5.0.1)
            └─ glob@10.5.0                  (^10.3.7)
               └─ minimatch@9.0.9           (^9.0.4)
                  └─ brace-expansion@2.1.2  (^2.0.2)  ← vulnerable

Reachability

Not reachable during normal GDMS operation. This confirms @rsheyd's assessment, with a further finding:

  1. No GDMS source imports glob, minimatch, rimraf, or brace-expansion (grep across src/, test/, scripts/, raycast-extension/, cloudflare/ returns nothing).
  2. gaxios@7.1.3 never loads rimraf at all. Its published tarball ships only build/, and rimraf appears solely in its package.json — zero references in shipped code. rimraf is declared in gaxios 7.1.3 alone: 7.1.2 did not have it, and 7.1.4, 7.1.5, 7.1.6, 7.2.0 and 7.3.x all dropped it again. It looks like a stray dependency, so this entire subtree is installed but never required at runtime.

Why no parent upgrade is available

googleapis-common@8.0.3 pins gaxios to the exact string "7.1.3" — the one gaxios release carrying rimraf — so npm update cannot move off it. The escape is googleapis-common@9.x (which uses gaxios ^7.3.0, no rimraf), but even googleapis@178.0.0, the current latest, still declares googleapis-common ^8.0.0, and 8.0.3 is the newest 8.x. No parent upgrade — in-range or via a googleapis major bump — removes the path today.

Remediating at brace-expansion itself is therefore the only option, and an in-range one exists.

Changes

npm update brace-expansion --package-lock-only produces a 3-line lockfile diff:

 "node_modules/brace-expansion": {
-  "version": "2.1.2",
+  "version": "2.1.4",
   "license": "MIT",
   "dependencies": { "balanced-match": "^1.0.0" }
 }
  • package.jsonreverted to main; no overrides.
  • package-lock.json — the 3 lines above only. balanced-match stays at 1.0.2 (its bump to 4.0.4 in the original revision was only a consequence of the 5.x override).
  • test/dependencies.test.js — new regression test (below).
  • AGENTS.md — file-map entry for the new test.

brace-expansion@2.1.4 depends on balanced-match@^1.0.0 (unchanged) and declares no engines constraint.

Regression test

Since the vulnerable path is not reachable, a behavioral exploit test would not be meaningful. Instead, test/dependencies.test.js asserts two lockfile invariants (node:test, no new dependencies, no network):

  1. The resolved brace-expansion is >= 2.1.4.
  2. The resolved version satisfies every declared range in the lockfile.

Both failure modes are verified to be caught:

# with 5.0.9 (the original revision of this PR):
✖ the resolved brace-expansion satisfies every declared dependency range
  AssertionError: brace-expansion 5.0.9 does not satisfy ^2.0.2 declared by node_modules/minimatch

# with 2.1.2 (unpatched):
✖ brace-expansion is patched for CVE-2026-14257 and CVE-2026-69152
  AssertionError: brace-expansion 2.1.2 is older than the patched 2.1.4

Test 2 would have blocked the original revision of this PR before review.

Verification

  • npm run check — passes: 222 tests, syntax check, Raycast typecheck.
  • npm audit — no longer reports brace-expansion; the only high-severity finding is cleared.
  • Your reproduction now passes instead of throwing:
    node -e 'import("minimatch").then(m => console.log(m.minimatch("a.js", "*.{js,ts}")))'
    → true
    

Notes

  • No version bump or CHANGELOG.md entry, per AGENTS.md ("internal-maintenance changes do not require a version bump").
  • The original description listed patched versions "5.0.8, 3.0.3, 2.1.3, 1.1.17" — those are CVE-2026-14257's four per-release-line patch versions. The automation picked the 5.x line and then over-shot to 5.0.9, rather than the 2.x line matching the installed tree.
  • Out of scope, for your awareness: npm audit also reports 3 unrelated moderate findings on main (qs array-limit bypass, and uuid via gaxios). Happy to open a separate PR if useful.

@rsheyd

rsheyd commented Sep 4, 2026

Copy link
Copy Markdown
Owner

Thanks for flagging this. Before I can evaluate the proposed override, could you please clarify or provide the following?

  • A link to the authoritative advisory, including its affected and patched version ranges.
  • The exact dependency path through which brace-expansion is installed, along with any evidence that the vulnerable function is reachable through GDMS.
  • Why a normal update to the nearest direct dependency is not available or sufficient.
  • Evidence that forcing brace-expansion@5.0.9 is compatible with each parent dependency’s declared range and API expectations.
  • A focused regression test covering brace expansion through the affected dependency path.
  • An explanation of why the description mentions patched versions 5.0.8, 3.0.3, 2.1.3, and 1.1.17, while the submitted lockfile resolves only 5.0.9.

In my testing, minimatch@9.0.9 declares brace-expansion@^2.0.2, and forcing v5.0.9 causes a basic call such as minimatch("a.js", "*.{js,ts}") to throw TypeError: brace_expansion_1.default is not a function. This suggests that the cross-major override may not be compatible even though installation and the existing test suite succeed.

I’m open to a compatible remediation if the vulnerability is reachable, but I’d prefer an ordinary upstream or direct-dependency update over a transitive override where possible.

@rsheyd

rsheyd commented Sep 4, 2026

Copy link
Copy Markdown
Owner

For context, I’ve reviewed the current dependency path and the immediate risk to GDMS users appears low. brace-expansion is installed transitively through googleapis → gaxios → rimraf → glob → minimatch, but GDMS does not directly use the glob or brace-expansion functionality. I have not found evidence that user-controlled input can reach the vulnerable function during normal GDMS operation.

I’m leaving this PR open for now to allow time for clarification or a compatible revision. I don’t want to merge the current cross-major override because it causes a reproducible compatibility failure in minimatch.

If there is evidence that the vulnerable path is reachable in GDMS, or a compatible upstream/direct-dependency update is available, I’m happy to reassess. Otherwise, I expect to wait for the dependency chain to be updated upstream.

@anupamme

anupamme commented Sep 4, 2026

Copy link
Copy Markdown
Author

Thanks for the detailed review. I agree that the cross-major override to brace-expansion@5.x is not appropriate given minimatch@9.x’s declared ^2.x dependency and the reproducible runtime failure.

I’ll rework the PR:

  • verify the authoritative advisory and affected/patched ranges;
  • document the complete dependency path to brace-expansion;
  • determine whether the vulnerable expansion path is actually reachable during normal GDMS operation;
  • check whether a compatible upgrade of the nearest parent dependency can resolve the issue without an override; and
  • add a focused regression test if there is a reachable affected path.

If the vulnerability is not reachable and there is no compatible upstream remediation yet, I agree that waiting for the dependency chain to update upstream is preferable to introducing a cross-major compatibility regression.

I’ll update the PR once I’ve verified those points.

brace-expansion 2.1.2 is affected by CVE-2026-14257 (GHSA-mh99-v99m-4gvg,
patched 2.1.3) and CVE-2026-69152 (GHSA-rgw5-rvv9-x895, patched 2.1.4), which
bypasses the CVE-2026-14257 mitigation. Version 2.1.4 is the first 2.x release
patched for both.

It enters the tree only through googleapis > googleapis-common > gaxios >
rimraf > glob > minimatch, which declares ^2.0.2. 2.1.4 satisfies that range,
so this is an in-range lockfile update: package.json is unchanged and no
dependency override is required.

Add test/dependencies.test.js to assert the resolved version stays patched and
keeps satisfying every declared range in the lockfile.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7oXdhMLN1izUhx4JJbGWc
@anupamme
anupamme force-pushed the fix-repo-google-docs-markdown-sync-cve-2026-14257-brace-expansion branch from 91eed09 to 248ac0b Compare September 4, 2026 17:41
@anupamme anupamme changed the title fix: upgrade brace-expansion to patched version (CVE-2026-14257) fix: update brace-expansion to 2.1.4 within minimatch's declared range (CVE-2026-14257, CVE-2026-69152) Sep 4, 2026
@anupamme

anupamme commented Sep 4, 2026

Copy link
Copy Markdown
Author

I've reworked the PR and force-pushed. The overrides block is gone entirely and package.json is now byte-identical to main — the change is a 3-line in-range lockfile update to brace-expansion@2.1.4, plus a regression test. Going through your points in order:

1. Authoritative advisory and ranges. Two advisories affect the installed 2.1.2:

Advisory CVE Affected (2.x) Patched
GHSA-mh99-v99m-4gvg CVE-2026-14257 >=2.0.0 <2.1.3 2.1.3
GHSA-rgw5-rvv9-x895 CVE-2026-69152 >=2.0.0 <2.1.4 2.1.4

Both are High (7.5, CWE-400/770). CVE-2026-69152 explicitly bypasses the CVE-2026-14257 mitigation, so 2.1.4 is the right target — 2.1.3 would be re-flagged immediately.

On your last question: the "5.0.8, 3.0.3, 2.1.3, 1.1.17" in the original description are CVE-2026-14257's four per-release-line patch versions. The automation picked the 5.x line and then over-shot to 5.0.9, instead of the 2.x line that actually matches this tree. That's the root of the bug you found.

2. Dependency path. brace-expansion has exactly one depender in the graph — your trace was right:

googleapis@160.0.0 → googleapis-common@8.0.3 → gaxios@7.1.3 (exact pin)
  → rimraf@5.0.10 → glob@10.5.0 → minimatch@9.0.9 → brace-expansion@2.1.2 (^2.0.2)

3. Reachability — not reachable, confirming your assessment. Beyond GDMS not importing glob/minimatch/rimraf/brace-expansion anywhere, there's a stronger reason: gaxios@7.1.3 never loads rimraf at all. Its published tarball ships only build/, and rimraf appears solely in its package.json — zero references in shipped code. And rimraf is declared in gaxios 7.1.3 alone: 7.1.2 didn't have it, and 7.1.4, 7.1.5, 7.1.6, 7.2.0 and 7.3.x all dropped it again. It appears to be a stray dependency, so the whole subtree is installed but never required.

4. Compatible parent upgrade — none available. googleapis-common@8.0.3 pins gaxios to the exact string "7.1.3", so npm update can't move off the one release carrying rimraf. The escape is googleapis-common@9.x (gaxios ^7.3.0, no rimraf), but even googleapis@178.0.0 — the current latest — still declares googleapis-common ^8.0.0, and 8.0.3 is the newest 8.x. So no parent upgrade removes the path today, including a googleapis major bump.

That leaves remediation at brace-expansion itself — and fortunately a fully in-range one exists, so no override is needed:

npm update brace-expansion --package-lock-only
 "node_modules/brace-expansion": {
-  "version": "2.1.2",
+  "version": "2.1.4",

2.1.4 satisfies minimatch's declared ^2.0.2, keeps balanced-match@^1.0.0 (so balanced-match stays at 1.0.2 — its bump to 4.0.4 was only a side effect of the 5.x override), and declares no engines constraint.

5. Regression test. Since the path isn't reachable, a behavioral exploit test wouldn't be meaningful. I added test/dependencies.test.js instead — two lockfile invariants, node:test, no new dependencies, no network:

  1. the resolved brace-expansion is >= 2.1.4;
  2. it satisfies every declared range in the lockfile.

I verified both failure modes are actually caught:

# 5.0.9 (this PR's previous revision):
✖ the resolved brace-expansion satisfies every declared dependency range
  AssertionError: brace-expansion 5.0.9 does not satisfy ^2.0.2 declared by node_modules/minimatch

# 2.1.2 (unpatched):
✖ brace-expansion is patched for CVE-2026-14257 and CVE-2026-69152
  AssertionError: brace-expansion 2.1.2 is older than the patched 2.1.4

Test 2 would have blocked the previous revision before it reached you.

Verification. npm run check passes (222 tests, syntax check, Raycast typecheck). npm audit no longer reports brace-expansion — the only high-severity finding is cleared. And your reproduction now returns a value instead of throwing:

node -e 'import("minimatch").then(m => console.log(m.minimatch("a.js", "*.{js,ts}")))'
→ true

Two smaller notes: I didn't bump the version or add a CHANGELOG.md entry, since AGENTS.md exempts internal-maintenance changes — happy to add one if you'd prefer. And separately from this PR, npm audit reports 3 unrelated moderate findings on main (qs array-limit bypass, uuid via gaxios); I can open a separate PR for those if that's useful.

Thanks for the careful review — the cross-major override was the wrong call, and pushing back on it is what surfaced the in-range fix.

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.

2 participants