Repository navigation
fix: patch braces stack-exhaustion DoS (CVE-2026-93687) - #451
Merged
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
npm auditreported 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 --forcewould downgradestylelintto 7.7.0 and still leave the other packages flagged.Fix
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 ofcompile,expandandstringify.lib/parse.js: stops nesting{and(at the same depth.3.0.3-relay.1.package.json:bracesadded as a devDependency (file:tools/vendor/braces) with"braces": "$braces"inoverrides. A plain relativefile:override doesn't work because npm resolves it from the dependent's directory (node_modules/micromatch/tools/...).web/Dockerfileandbackend/Dockerfilecopy the vendored package beforenpm ci, since both install root devDependencies.deploy/services/web.jsonnow watchestools/vendor/**.Exposure
Dev-only:
npm audit --omit=devwas already clean before this change. In this repobracesonly 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 fixedbraces.Test plan
npm audit --registry=https://registry.npmjs.org: 0 vulnerabilities (thenpmmirrorregistry in the lockfile doesn't support audit)npm ls braces: every consumer resolves to3.0.3-relay.1 -> ./tools/vendor/bracesnode --test tools/vendor/braces/test/: 5/5 pass (deep and cyclic ASTs rejected; normal ranges, globs and 64-level nesting unchanged)eslint srcandstylelint "src/**/*.css"inweb/pass with the patched copynpm run test:ts: 2286/2286 node tests and 531/531 React tests passCOPYstep🤖 Generated with Claude Code