Skip to content

ci: Markdown Lint fails repo-wide on a braces advisory with no patched release (GHSA-vfj7-8cjw-p6xm) #540

Closed

Description

@basiliskus

Summary

Markdown Quality → Markdown Lint fails on main and on every PR as it re-runs. The failure is in the Install linter dependencies step, before markdownlint executes — so it is not a lint error in any file.

5 high severity vulnerabilities
braces vulnerable to stack-exhaustion denial of service through deeply nested patterns
  — GHSA-vfj7-8cjw-p6xm
  braces → micromatch → fast-glob → globby → markdownlint-cli2
##[error]Process completed with exit code 1

The step is npm ci && npm audit --audit-level=high (.github/workflows/markdown-quality.yml:43-47). npm ci succeeds; npm audit exits 1.

Not caused by any change

GHSA-vfj7-8cjw-p6xm was published between 2026-10-02 and 2026-10-03. Nothing in the repo moved.

main @ 3634046   failure   2026-10-03T14:17:50Z   ← current main
main @ 410f332   success   2026-10-02T23:47:08Z
main @ 13d9d3a   success   2026-10-02T22:18:19Z

Open PRs still showing pass are displaying cached runs from before the advisory. They will fail on their next run. Every PR is affected as soon as CI re-triggers.

npm audit fix does not fix it

npm reports fixAvailable: markdownlint-cli2@0.23.3 (isSemVerMajor: true). That is wrong — I installed it in a scratch directory and re-audited:

$ npm i --save-exact markdownlint-cli2@0.23.3
$ npm audit --audit-level=high
STILL FAILS (exit 1)

braces present: 3.0.3
braces                 sev=high  range=*         direct=False
fast-glob              sev=high  range=*         direct=False
globby                 sev=high  range=>=8.0.0   direct=False
micromatch             sev=high  range=>=0.2.0   direct=False
markdownlint-cli2      sev=high  range=>=0.0.5   direct=True

markdownlint-cli2@0.23.3 depends on globby@16.2.4 and micromatch@4.0.8, which still reach braces@3.0.3.

The advisory range for braces is * — every published version. Latest is 3.0.3 (published 2024-05-21); there is no patched release. So no dependency bump clears this gate, and taking the breaking major upgrade would be churn for no benefit.

Current pins (.github/linters/): markdownlint-cli2@0.22.1, braces@3.0.3, micromatch@4.0.8, globby@16.2.0, fast-glob@3.3.3.

Impact

Markdown Lint is not in the ruleset's required status checks (Conventional Commit Title, pre-commit, acq offline suite under bash 3.2), so it does not block merges. But:

  • Every PR shows a red check that is nobody's fault, and reviewers have to be told to ignore it.
  • A persistently-red non-required check trains people to ignore red checks, which is how a real failure gets waved through.
  • npm run lint:md still works locally — only the CI audit gate fails — so the signal loss is CI-only.

Options

  1. Scope the audit gate to what is actionable. npm audit --audit-level=high --omit=dev, or drop the audit from this workflow and let the existing Dependabot/lint:secrets path own dependency alerting. The markdown workflow's job is linting markdown; a transitive DoS advisory in a dev-only glob library is not what it should gate on.
  2. Allowlist this advisory explicitly (e.g. npm audit --audit-level=high || true with a documented exception, or an audit-ci-style allowlist naming GHSA-vfj7-8cjw-p6xm) so the gate keeps working for everything else and the exception is visible and reviewable.
  3. Accept the breaking bump to markdownlint-cli2@0.23.3 — does not help, per the test above. Listed only to rule it out.

Option 1 or 2. Worth noting this is the same class as e712685 ("pin markdownlint-cli2 once, clear GHSA-253c"), which cleared a different advisory the same way — a new advisory will re-break it again, so a durable policy beats another one-off pin.

Risk assessment for the allowlist path

braces is a dev-only transitive dependency of a markdown linter that runs on trusted repo content in CI. The advisory is a stack-exhaustion DoS triggered by deeply nested brace patterns. The inputs here are this repository's own .md file paths and the glob patterns in .markdownlint-cli2.yaml. There is no untrusted input path, and the blast radius of a successful exploit is a hung CI job.

Notes

Found while updating #519 from main — the merge was clean and all required checks pass, but Markdown Lint went red for this reason rather than anything in that branch.

Activity

  1. mogul commented on Oct 4, 2026

    @mogul
    Contributor

    Additional context from #541:

    • The advisory is GHSA-vfj7-8cjw-p6xm / CVE-2026-93687.
    • Advisory metadata: affected versions <= 3.0.3, patched versions None; latest braces is 3.0.3, so no dependency bump of braces, micromatch, globby, or markdownlint-cli2 can currently make npm audit --audit-level=high pass.
    • npm audit fix --force must not be used here: it attempts to install markdownlint-cli2@0.0.4, an ancient release that predates the current dependency chain and would gut linting rather than fix braces.
    • Risk context: the vulnerability is uncontrolled recursion / stack-exhaustion DoS from deeply nested brace patterns. In this repository, the markdownlint glob inputs are repo-controlled (**/*.md plus fixed ignores in .markdownlint-cli2.yaml), supplied by maintainers, not by an attacker. The practical impact is availability-only on a dev-time CI linter: a crashed linter is a failed check, not a confidentiality or integrity breach.
    • Real remediation options are CI-policy decisions requiring maintainer sign-off, not dependency changes in unrelated feature PRs:
      1. Raise the workflow gate to --audit-level=critical.
      2. Scope/drop this audit from Markdown Quality and let a dedicated dependency-audit path own dependency alerting.
      3. Accept GHSA-vfj7-8cjw-p6xm / CVE-2026-93687 explicitly with a documented, dated exception.

    This is why #541 leaves .github/linters/package.json, .github/linters/package-lock.json, and .github/workflows/markdown-quality.yml untouched.

  2. mogul commented on Oct 6, 2026

    @mogul
    Contributor

    Resolved in #548

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions