Repository navigation
registry: phase 6 — six distribution controls, and the PHASES constant that scores them - #56
Merged
Merged
Conversation
…t that scores them Registers the six ids p4gs/sscs-bootstrapper#71 adds and, more importantly, teaches the scorer that phase 6 exists. **The load-bearing change is `scoring.ts`'s PHASES, and getting it wrong would have failed silently.** `computeScore` reduces `totalPass` and `totalCountable` over PHASES, but derives `coverage` from `scoped.length`, which has no phase filter. Registering six phase-6 controls while PHASES still read 1..5 would therefore not merely have omitted a row: every phase-6 pass would have counted for nothing, a phase-6 FAIL would have been invisible in the overall percentage, and the six controls would still have inflated the coverage denominator — so every listing's published coverage would have dropped, for a reason nothing reported. `reclassify()` fail-closes on an unknown control id; there is no equivalent guard on phase, which is exactly why this is worth spelling out in the constant's own doc comment. The tool found the same class independently on its side (`machine.rs`'s `score_of`, which that PR fixes by deriving from the registry). One bug, two codebases, both silent. Classes, and the reasoning that is not obvious: - `maintainer-mfa` is **C**, not B. It reads the MAINTAINER's own accounts (`gh api user`, `npm profile get`) with the maintainer's own credentials; no repository scan can see it, which is the definition of class C. - `publish-provenance` is **B** — the one control here that asks a third-party registry a question about published artifacts. - `publish-targets` maps to the posture-disclosure sentinel `[]`. It is an inventory of where a project publishes; it blocks no technique, and saying so is more honest than inventing an attack class for it. - The rest are A or A′ by whether they read committed manifests or committed workflows. A2 (stolen publisher identity) is the class this whole phase exists for, so four of the six carry it. Also replaced a literal `phase <= 5` assertion in `local.test.ts` with membership in PHASES, so the next phase cannot pass tests while being silently unscored — the failure mode this commit exists to prevent. Counts move 48 → 54 across both assertions and nine prose sites; the fixture is regenerated, never hand-edited. Verified: tsc clean, build clean, `bun test` 450 pass / 0 fail including the cross-repo parity case run against the tool branch.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Registers the six ids p4gs/sscs-bootstrapper#71 adds, and must merge and deploy first (
reclassify()throws on an unclassified id, anddirectory-scan.ymlclones the tool'smainat depth 1).The load-bearing change is
scoring.ts'sPHASES, and getting it wrong fails silentlycomputeScorereducestotalPass/totalCountableoverPHASES, but derivescoveragefromscoped.length, which has no phase filter. Registering six phase-6 controls whilePHASESstill read1..5would not merely have omitted a row:reclassify()fail-closes on an unknown control id. There is no equivalent guard on phase. That asymmetry is now documented in the constant itself.Worth noting: the tool side found the same class independently (
machine.rs'sscore_of, fixed there by deriving from the registry). One bug, two codebases, silent in both.Classes, where the reasoning is not obvious
maintainer-mfais C, not B. It reads the maintainer's own accounts (gh api user,npm profile get) with the maintainer's own credentials. No repository scan can see that, which is the definition of class C.publish-provenanceis B — the only one that asks a third-party registry about published artifacts.publish-targetsmaps to the posture-disclosure sentinel[]. It is an inventory of where a project publishes; it blocks no technique, and saying so beats inventing an attack class for it.A2 (stolen publisher identity) is the class this phase exists for, so four of the six carry it.
Also
Replaced a literal
phase <= 5assertion inlocal.test.tswith membership inPHASES, so the next phase cannot pass tests while being silently unscored — the exact failure mode this PR exists to prevent.Counts move 48 → 54 across both assertions and nine prose sites; the fixture is regenerated, not hand-edited.
Verified:
bunx tsc --noEmitclean,bun run buildclean,bun test450 pass / 0 fail, including the cross-repo parity case run against the tool branch.