Repository navigation
CVE-2026-93687: braces vulnerable to stack-exhaustion denial of service through deeply nested patterns - #62
Conversation
The recursive AST walkers in braces lack depth guards. A pattern nested deep enough while staying under the 10000-character limit exhausts the call stack and kills the Node.js process with an uncaught RangeError. Measured on Node 20, a 4999-deep parenthesised pattern (9999 characters) crashes braces.compile and braces.stringify. No patched version exists: 3.0.3 is the latest release and the last version in the vulnerable range <= 3.0.3 (GHSA-vfj7-8cjw-p6xm, upstream issue micromatch/braces#70 still open). The registry copy is therefore replaced with a vendored copy of 3.0.3 plus depth guards (cap of 100 nesting levels, throw a catchable RangeError beyond it), wired in with a direct file: dependency and a braces override so the transitive copies used by chokidar and micromatch resolve to it. tests/braces-security.test.mjs pins the lockfile replacement and the bounded-depth behaviour; vendor/braces/PATCH-NOTES.md records what changed relative to 3.0.3 and how to drop the copy once upstream publishes a fix.
This branch carried the vendored braces 3.0.4 from pull request #48, which merged the same fix to main. Pull request #59 then merged ADR-0001, which forbids local patches (vendor/ folder, file: dependencies, patch packages) and fails the build on them via tests/dependency-policy.test.mjs. ADR-0001 records the reachability analysis for this advisory: patterns reach braces only through committed configuration in micromatch and chokidar, and no outside party can supply a pattern to this build, so the project does not qualify. The approved response for this alert is a Dependabot dismissal with reason not_used, a maintainer decision made after the fact is documented. No patched version of braces exists: 3.0.3 is the latest registry release and the last version in the vulnerable range <= 3.0.3, so there is no registry upgrade to make. This revert restores the registry lockfile so this branch matches the policy on main and carries no competing fix.
No code fix on this branch: the fix is already merged, and policy changed after this branch forkedCVE-2026-93687 (GHSA-vfj7-8cjw-p6xm) covers braces <= 3.0.3 and has no patched version: 3.0.3 is the latest release in the registry (verified 2026-10-08 via npm and the advisory). Sequence of events:
Per ADR-0001 the sanctioned response for this alert is a Dependabot dismissal with reason not_used, which is a maintainer decision. The registry copy 3.0.3 stays in the lockfile; there is no registry version to bump to. Commit e14b534 reverts the duplicated vendored fix so this branch matches the merged policy and carries no competing change. The branch is verified: npm ci, typecheck, 63/63 tests, and the production build all pass. This pull request is now empty against main and stands only as the record for the alert. |
SeriousJul
left a comment
There was a problem hiding this comment.
Review: PR #62 (CVE-2026-93687, braces stack-exhaustion DoS)
- Score: 92 / 100
- Specification Check: Pass
- Quality Check: Pass
- Required Changes: None required for the merge. Two follow-up items below.
Specification Check (ticket: Dependabot alert #57, GHSA-vfj7-8cjw-p6xm)
The sanctioned response for this alert is defined by ADR-0001 on main: a reachability analysis, a registry version pin where one exists, and a Dependabot dismissal. I verified each part.
- Reachability analysis: verified. The committed lockfile names exactly two dependents of
braces:chokidar(~3.0.2) andmicromatch(^3.0.3). Both take patterns from this repository's committed configuration. File paths are matched, never parsed as patterns. No outside party can supply a pattern to this build. The analysis in ADR-0001 is correct. - Registry pin: not possible, confirmed. The latest registry release of
bracesis 3.0.3 (verified 2026-10-08 withnpm view braces version). The advisory range<= 3.0.3ends at that version. No patched version exists. - No local patch on the branch: verified. The final tree tracks no path under
vendor/, uses nofile:orlink:spec, defines nooverrides, and no git or tarball URL. Every spec independenciesanddevDependenciesis a plain registry semver range. This state satisfies the rule thattests/dependency-policy.test.mjsenforces on main. - CI gate decision recorded: verified.
osv-scanner.tomlon main listsGHSA-vfj7-8cjw-p6xmwith the reason and a pointer to ADR-0001. The gate job passes on both main and this PR head. - Branch tree verified locally.
npm ci,npm run typecheck, andnpm testall pass on this branch: 63/63 tests, 0 failures. GitHub CI on the head is green (osv-scan gate, build-and-test, both CodeQL analyses). - Duplicate-fix claim: verified. The diff of commit
671d72eon this branch is byte-identical to the diff of the PR #48 merge1a3f4ba, as the maintainer comment states. - Net effect: the PR diff is empty. The HEAD tree equals the merge-base tree (verified:
git diff <merge-base> HEADis empty). The merge changes no file on main. The resolution lives on main (ADR-0001,osv-scanner.toml, policy test) plus the Dependabot dismissal. The PR stands as the record for the alert. This matches the maintainer comment.
One caveat: the Dependabot dismissal itself (reason not_used, citing ADR-0001) is asserted in the ADR. I could not verify the alert state from the repository: the alert page is not reachable through the API from my position. See the follow-up item.
Quality Check
- Commit messages document the decision well. The revert message explains why the branch changed course (ADR-0001 landed after the branch forked) and what it restores.
- The merge state is
CLEANand mergeable. The branch is 10 commits behind main. This has no effect because the diff is empty. - The PR title and body do not show the final state. The body is the raw alert text, which reads as a promise of a code fix. The final state is a no-change record plus an out-of-band dismissal. The maintainer comment carries the explanation, but the description itself should say so.
Required Changes
- None required for the merge.
- (Recommended) Update the PR description to state the resolution path: ADR-0001, the Dependabot dismissal with reason
not_used, and theosv-scanner.tomldecision. State that the empty diff is intentional. - (Verify) Confirm in the Security tab that alert #57 shows dismissed with reason
not_usedand a comment that cites ADR-0001. - (Optional) Merge the PR as the record for the alert, or close it without merging. Both outcomes are consistent with ADR-0001.
https://github.com/SeriousJul/seriousjul.github.io/security/dependabot/57
Package: braces
Ecosystem: npm
Vulnerable range: <= 3.0.3
Severity: high
CVSS score: 7.5
braces through 3.0.3 contains a stack overflow vulnerability in the recursive AST walkers that lack depth guards. Attackers can supply deeply nested brace patterns under the character limit to exhaust the call stack and terminate the Node.js process with an uncaught RangeError.