Skip to content

CVE-2026-93687: braces vulnerable to stack-exhaustion denial of service through deeply nested patterns - #62

Merged
SeriousJul merged 3 commits into
mainfrom
factory/57-cve-2026-93687-braces-vulnerable-to-stack-exhaustion-denial-of-service-through-deeply-nested-patterns
Oct 8, 2026
Merged

SeriousJul merged 3 commits into
mainfrom
factory/57-cve-2026-93687-braces-vulnerable-to-stack-exhaustion-denial-of-service-through-deeply-nested-patterns

Conversation

@SeriousJul

Copy link
Copy Markdown
Owner

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.

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.
@SeriousJul

Copy link
Copy Markdown
Owner Author

No code fix on this branch: the fix is already merged, and policy changed after this branch forked

CVE-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:

  1. Pull request CVE-2026-93687: braces vulnerable to stack-exhaustion denial of service through deeply nested patterns #48 merged this exact fix to main on 2026-10-06: a vendored copy of braces 3.0.3 with depth guards, wired in with a file: dependency and an override. The fix commit this branch carried was a byte-identical duplicate of that merge.
  2. Pull request Vendor folder should not be committed #59 then merged ADR-0001 (docs/adr/0001-no-local-patches-to-third-party-dependencies.md). It rejects the local patch approach, adds tests/dependency-policy.test.mjs which fails the build on any file: or vendor/ dependency, and records the reachability analysis for this advisory: patterns reach braces only through micromatch and chokidar, both driven by patterns from committed configuration, and no outside party can supply a pattern to this build.

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
SeriousJul marked this pull request as ready for review October 8, 2026 00:41

@SeriousJul SeriousJul left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

  1. Reachability analysis: verified. The committed lockfile names exactly two dependents of braces: chokidar (~3.0.2) and micromatch (^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.
  2. Registry pin: not possible, confirmed. The latest registry release of braces is 3.0.3 (verified 2026-10-08 with npm view braces version). The advisory range <= 3.0.3 ends at that version. No patched version exists.
  3. No local patch on the branch: verified. The final tree tracks no path under vendor/, uses no file: or link: spec, defines no overrides, and no git or tarball URL. Every spec in dependencies and devDependencies is a plain registry semver range. This state satisfies the rule that tests/dependency-policy.test.mjs enforces on main.
  4. CI gate decision recorded: verified. osv-scanner.toml on main lists GHSA-vfj7-8cjw-p6xm with the reason and a pointer to ADR-0001. The gate job passes on both main and this PR head.
  5. Branch tree verified locally. npm ci, npm run typecheck, and npm test all 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).
  6. Duplicate-fix claim: verified. The diff of commit 671d72e on this branch is byte-identical to the diff of the PR #48 merge 1a3f4ba, as the maintainer comment states.
  7. Net effect: the PR diff is empty. The HEAD tree equals the merge-base tree (verified: git diff <merge-base> HEAD is 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 CLEAN and 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 the osv-scanner.toml decision. State that the empty diff is intentional.
  • (Verify) Confirm in the Security tab that alert #57 shows dismissed with reason not_used and 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.

@SeriousJul
SeriousJul merged commit 74c38ec into main Oct 8, 2026
7 checks passed
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