Skip to content

chore(ci): replace vuln-gate's four hand-dispatched ecosystem sites with a PARSERS registry #634

Closed

Description

@mforce

Split out of #529, which closed 2026-08-30 having ported repo-template's fail-open --level fix. That issue's body named this explicitly and it was never filed:

Adopting the PARSERS registry is a legitimate follow-up, but it is a refactor and belongs in its own PR — not bundled with a security fix.

Filing it now rather than leaving it to rot in a closed issue, per the repo's own rule. Nothing is broken — this is a maintainability change, not a defect.

The problem

.github/scripts/vuln-gate.mjs (378 lines) hand-dispatches on ecosystem in four places, each of which must be edited together to add a third ecosystem:

Site Line Shape
ECOSYSTEMS set 35 new Set(["npm", "nuget", "any"])
reportProblem 294 if (ecosystem === "npm") … else if (ecosystem === "nuget") …
main() arg check ~343 options.ecosystem !== "npm" && options.ecosystem !== "nuget"
parser selection ~353 options.ecosystem === "npm" ? parseNpm(parsed) : parseNuget(parsed)

The last one is the sharp edge: it is a ternary with no third branch, so a new ecosystem that clears the ECOSYSTEMS guard but is not wired into the ternary is silently parsed as nuget. Wrong findings, green gate.

This is the hand-maintained-list shape AGENTS.md already warns about under Writing a guard — "prefer walk everything, exclude deliberately over list what I thought of". A PARSERS registry keyed by ecosystem makes the four sites one, and makes the set of supported ecosystems derivable rather than restated.

Scope

  • Replace the four sites with a single registry mapping ecosystem → { parse, validateShape }.
  • Derive ECOSYSTEMS, the usage string, and the arg check from the registry's keys rather than restating them. "any" stays valid for exception scope while not being a parseable ecosystem — that asymmetry is real and must survive the refactor.
  • Keep every exported function exported. parseNpm, parseNuget, gate, severityRank, levelProblem, isValidException, emitAllowlist and the rest are the tested API surface; this is an internal dispatch change, not an API change.

Guard

Per AGENTS.md, the guard comes with a mutation that makes it red before any claim is written: add a registry entry with no parser and assert the gate refuses to run rather than falling through to a default parser. A test that only checks "npm and nuget still work" would pass with the bug present and is not sufficient.

Explicitly out of scope

The gate()-level --level validation residual from #529 — an unvalidated level reaching the exported gate() gives floor = -1, so every finding blocks. That is fail-closed, not fail-open, and #529 filed it against collectify#117 with "do not repeat it here". Leave it alone.

Not epic-tracked

#529 carried no epic label and neither does this — it is CI tooling, not product work, so there is no phase-epic checklist to add it to.

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions