Skip to content

registry: phase 6 — six distribution controls, and the PHASES constant that scores them - #56

Merged
p4gs merged 1 commit into
mainfrom
feat/register-phase-6
Sep 11, 2026
Merged

p4gs merged 1 commit into
mainfrom
feat/register-phase-6

Conversation

@p4gs

@p4gs p4gs commented Sep 11, 2026

Copy link
Copy Markdown
Owner

Registers the six ids p4gs/sscs-bootstrapper#71 adds, and must merge and deploy first (reclassify() throws on an unclassified id, and directory-scan.yml clones the tool's main at depth 1).

The load-bearing change is scoring.ts's PHASES, and getting it wrong fails silently

computeScore reduces totalPass/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 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. That asymmetry is now documented in the constant itself.

Worth noting: the tool side found the same class independently (machine.rs's score_of, fixed there by deriving from the registry). One bug, two codebases, silent in both.

Classes, where the reasoning 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 that, which is the definition of class C.
  • publish-provenance is B — the only one that asks a third-party registry 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 beats 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 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 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 --noEmit clean, bun run build clean, bun test 450 pass / 0 fail, including the cross-repo parity case run against the tool branch.

…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.
@p4gs
p4gs merged commit c7fa356 into main Sep 11, 2026
16 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