Skip to content

fix: patch braces stack-exhaustion DoS (CVE-2026-93687) - #451

Merged
droxer merged 1 commit into
mainfrom
fix/braces-cve-2026-93687
Oct 16, 2026
Merged

droxer merged 1 commit into
mainfrom
fix/braces-cve-2026-93687

Conversation

@droxer

@droxer droxer commented Oct 16, 2026

Copy link
Copy Markdown
Owner

Summary

npm audit reported 11 high-severity findings. All 11 are one advisory: braces <= 3.0.3 overflows the stack on deeply nested patterns (GHSA-vfj7-8cjw-p6xm / CVE-2026-93687). npm counted it again for each package that pulls it in (micromatch, fast-glob, globby, stylelint, eslint-config-next, @next/eslint-plugin-next, shadcn, @shadcn/registry, ts-morph, @ts-morph/common).

No patched release exists yet: 3.0.3 is the latest version and upstream issue #70 is still open. npm audit fix --force would downgrade stylelint to 7.7.0 and still leave the other packages flagged.

Fix

  • Patched copy: tools/vendor/braces/ is the npm 3.0.3 package plus the depth guard from micromatch/braces#88, written by hand:
    • lib/assert-depth.js: an iterative pre-walk that rejects ASTs deeper than 128 levels or containing a cycle. It runs at the top of compile, expand and stringify.
    • lib/parse.js: stops nesting { and ( at the same depth.
    • Version is 3.0.3-relay.1.
  • Root package.json: braces added as a devDependency (file:tools/vendor/braces) with "braces": "$braces" in overrides. A plain relative file: override doesn't work because npm resolves it from the dependent's directory (node_modules/micromatch/tools/...).
  • Docker builds: web/Dockerfile and backend/Dockerfile copy the vendored package before npm ci, since both install root devDependencies. deploy/services/web.json now watches tools/vendor/**.

Exposure

Dev-only: npm audit --omit=dev was already clean before this change. In this repo braces only expands glob patterns from our own lint configs, so the risk was low. This change clears the audit with a reviewed patch instead of an unreviewed git dependency.

Follow-up

Remove the override, the devDependency and tools/vendor/braces/ once upstream publishes a fixed braces.

Test plan

  • npm audit --registry=https://registry.npmjs.org: 0 vulnerabilities (the npmmirror registry in the lockfile doesn't support audit)
  • npm ls braces: every consumer resolves to 3.0.3-relay.1 -> ./tools/vendor/braces
  • node --test tools/vendor/braces/test/: 5/5 pass (deep and cyclic ASTs rejected; normal ranges, globs and 64-level nesting unchanged)
  • eslint src and stylelint "src/**/*.css" in web/ pass with the patched copy
  • npm run test:ts: 2286/2286 node tests and 531/531 React tests pass
  • Railway web and backend Docker builds succeed with the new COPY step

🤖 Generated with Claude Code

braces <= 3.0.3 recurses once per nesting level in its AST walkers, so a
deeply nested pattern overflows the stack (GHSA-vfj7-8cjw-p6xm). No patched
release exists, and npm audit reported it 11 times through micromatch,
fast-glob, globby, stylelint, eslint-config-next, shadcn and ts-morph.

Vendor braces 3.0.3 under tools/vendor/braces with the depth guard from
micromatch/braces#88: an iterative pre-walk rejects ASTs deeper than 128
levels or containing a cycle, and the parser stops nesting at the same
depth. The root override points every consumer at it ($braces, since a
relative file: override resolves from the dependent's directory). Both
Dockerfiles copy the package before npm ci.

Drop the override and the vendored copy once upstream ships a fix.
@droxer
droxer merged commit 516eb1d into main Oct 16, 2026
@droxer
droxer deleted the fix/braces-cve-2026-93687 branch October 16, 2026 11:43
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.

1 participant