Repository navigation
npm audit in CI blocks on an unpatched braces advisory (GHSA-vfj7-8cjw-p6xm), failing Markdown Lint on main and every rebuilt branch #470
Description
Summary
npm audit in the Markdown Lint job rejects GHSA-vfj7-8cjw-p6xm / CVE-2026-93687
(braces, high severity, published 2026-09-18). The advisory's affected range is
<= 3.0.3 with first_patched_version: null — no fixed release exists, so this
cannot be resolved by upgrading. The job fails before markdownlint runs at all.
This is not a branch-specific problem. It is live on main.
Evidence
Failing step, .github/workflows/ci.yml:109-113:
- name: Install linter dependencies
working-directory: .github/linters
run: |
npm ci
npm audit --audit-level=moderateThe dependency path, from the job log:
braces *
Severity: high
braces vulnerable to stack-exhaustion denial of service through deeply nested patterns
node_modules/braces
micromatch >=0.2.0
fast-glob *
globby >=8.0.0
markdownlint-cli2 >=0.0.5
5 high severity vulnerabilities
##[error]Process completed with exit code 1.
So the chain is braces <- micromatch <- fast-glob <- globby <- markdownlint-cli2, where
markdownlint-cli2 is our pinned 0.23.3 in .github/linters/package.json. 0.23.3 is
the current latest upstream, so there is no newer markdownlint-cli2 to move to either.
Confirmed live on main: main tip f2227413's own Markdown Lint check is
failure, and its job log shows the identical braces / GHSA-vfj7-8cjw-p6xm output under
npm audit --audit-level=moderate.
npm audit fix --force is not a usable remedy — npm's own suggestion is to install
markdownlint-cli2@0.0.4, which it labels a breaking change.
Why it looks intermittent (and why that is misleading)
Open PRs split cleanly by whether their Markdown Lint job has run since the advisory and
with the current lockfile:
| PR | Markdown Lint | job ran |
|---|---|---|
| #466 | failure | 2026-10-05 |
| #468 | failure | 2026-10-05 |
| #469 | failure | 2026-10-05 |
| #453 | success | 2026-09-30 |
| #438 | success | 2026-10-01 |
| #416 | success | 2026-10-02 |
| #415 | success | 2026-09-15 |
| #408 | success | 2026-10-02 |
| #284 | success | 2026-10-02 |
Every green result predates the current failing state; no job that has run recently is
green. The older PRs are not unaffected, only unrerun — any push or re-run should turn
them red too. Treating this as "a few bad PRs" will under-count it.
Also affects the playbook
GSA-TTS/agentic-coding-playbook has the same markdownlint-cli2 0.23.3 pin in
.github/linters/package.json and runs npm audit --audit-level=high
(.github/workflows/ci.yml:96). Its #300 failing job log shows the same braces
advisory. There, the install step dies before ruff ever runs, which is why the two open
ruff dependabot PRs (#300, #301) fail for a reason unrelated to ruff.
Impact
mainis red on Markdown Lint.- Markdown linting is not actually running anywhere — the job dies during install, so
markdown problems currently pass unchecked. This is the part worth prioritizing: the
visible symptom is a red check, but the real cost is a silently disabled lint gate. - No contributor can get a green Markdown Lint check, so the failure carries no signal and
trains reviewers to ignore a red check. - It interacts badly with review feedback. On
#466a reviewer (correctly) asked the author
to drop an unrelated local markdownlint shim. That shim had replacedmarkdownlint-cli2
and so incidentally removed theglobby -> bracespath, making the job green. Removing
the shim re-exposed this pre-existing failure on that branch. Authors will keep
rediscovering this as if it were their own regression.
What this issue is asking for
A decision, not a specific patch. The options each have real costs and this should be a
maintainer call:
- Wait for an upstream fix. Honest, but
first_patched_versionisnulland markdown
linting stays disabled indefinitely. - Scope the audit so a known-unpatchable transitive dev-only advisory does not gate
the job (for example auditing only production dependencies, or an explicitly documented
and time-boxed exception for this GHSA). Keeps the lint gate working; needs care not to
become a blanket suppression. - Replace or drop
markdownlint-cli2in favour of a tool without theglobby
dependency chain. Note#451deliberately made.markdownlint-cli2.yamlthe single
source for both CI and pre-commit, so this is not a small change.
Worth stating plainly: options 2 and 3 both weaken or churn a supply-chain control, and
option 1 leaves a lint gate dead. There is no cost-free choice here, which is why it needs
an owner rather than a drive-by fix.
Relevant context for whoever picks this up: this is a stack-exhaustion DoS in a
dev-only, CI-only lint dependency that processes our own repository's glob patterns, not
untrusted input. That materially lowers the real risk relative to the advisory's "high"
severity rating, and is the main argument for option 2 — but the severity rating is
upstream's call, not ours, and the exception should be documented and revisited rather
than silent.
Provenance: identified while investigating why four ruff dependabot PRs
(GSA-TTS/agentic-coding-patterns#468, #469,
GSA-TTS/agentic-coding-playbook#300, #301) were failing CI. Two distinct causes were
found; ruff 0.16.9 itself is clean. This issue covers the braces cause. The other
cause — each dependabot PR moving only one of two ruff pins, which the
test_ruff_pins_match lockstep guard rejects — is separate and not covered here.
Activity
basiliskus commentedon Oct 5, 2026
This is fixed on the patterns side. Recording what landed, and what is left, before this is closed.
Fixed by #471 (merged 2026-10-05T21:39:29Z), which took option 3 from the list above — replacing the tool rather than scoping the audit.
markdownlint-cli20.23.3→markdownlint-cli@0.49.1, plus ajs-yamloverride for that package's ownGHSA-r3ph-w7gj-g6xm.Verified on
main(dd22daff):bracesis absent from.github/linters/package-lock.json, along with the rest of theglobby → fast-glob → micromatchchain.Two things it fixed beyond clearing the advisory, both worth noting here because they were separate concerns raised during review of #466:
Linting: N file(s)out ofmarkdownlint-cli2's output, which a shim could satisfy with a constant. It now callsrun-markdownlint.mjs --count-only, which globs with the sametinyglobbyenginemarkdownlint-cliuses internally and prints the real matched count.The quickstart was fixed in parallel by GSA-TTS/agentic-coding-quickstart#548 (merged 21:36:54Z), same swap.
The playbook was not, and is still on the vulnerable path.
GSA-TTS/agentic-coding-playbookmainstill pinsmarkdownlint-cli20.23.3and still hasbracesin its linter lockfile. It currently looks green only because itsPython Lint, Test & Validatejob last ran on 2026-10-01, before the advisory became blocking;#300and#301already fail on it. Because the audit sits in the install step, that job aborts before markdown lint, ruff lint, ruff format, and the validators run — so several gates are silently inactive there, not just one.That port is now tracked as GSA-TTS/agentic-coding-playbook#303, so it does not get lost when this issue closes. This one can be closed as fixed for patterns.
One correction to the body above, for anyone reading back: it says the ruff dual-pin skew was "separate and not covered here," which was right, but it did not mention that the skew is already tracked as #404, open since 2026-09-02. It is not a new or untracked problem.