🔒 fix(release): pin cosign signer identity and issuer in install.sh (SEC-03) - #80
Merged
Conversation
…SEC-03)
`hack/install.sh` ran `cosign verify-blob --bundle "$bundle" "$ARCHIVE"` with no
signer pin. Keyless verification without `--certificate-identity-regexp` either
errors out (cosign v2 requires the flags) or accepts ANY Fulcio certificate — so
a mirror-swapped archive shipped with its own validly signed bundle verified
clean and `--require-signature` promised a guarantee it did not deliver, while
SECURITY.md's published copy-paste instructions pinned both flags.
install.sh now carries the pair byte-identically to SECURITY.md:
--certificate-oidc-issuer https://token.actions.githubusercontent.com
--certificate-identity-regexp '^https://github.com/PlatformRelay/assent/'
New gate `hack/release/install_cosign_pin_test.sh`, offline (stubbed `cosign`
first on PATH, fake .sigstore.json bundles, no network, no real Fulcio):
* REQ-AUD2-S03-01 — both flags are inside the folded cosign invocation
(comment lines excluded, so prose cannot satisfy the assertion).
* REQ-AUD2-S03-02 — a bundle whose certificate identity is
github.com/evil-mirror/assent fails `install.sh --require-signature` and
writes no binary to --dest; likewise a foreign OIDC issuer. A matching
identity still installs (positive control), and the stub log proves both
flags reach cosign's argv rather than merely existing in the file.
* REQ-AUD2-S03-03 — non-vacuity in both halves: deleting either flag from a
temp copy reddens the static assertion, AND the un-pinned copy installs the
foreign-signed archive the fixed script rejects, so the pin is load-bearing.
* REQ-AUD2-S03-04 — drift gate: issuer and identity regexp are extracted from
both hack/install.sh and SECURITY.md (each must yield exactly one distinct
value) and compared; rewriting either file alone reddens.
* REQ-AUD2-S03-05 — wired as its own `task check` stage
`release-install-cosign-pin-test`, with a self-pin plus the AUD-S18
CHECK_STAGES entry so the release exit gate grades it.
Out of scope, noted for follow-up: hack/release/verify-artifacts.sh:124 runs the
same unpinned `cosign verify-blob --bundle` on the maintainer/CI verification
path.
…ng (SEC-03) The pin landed in b616d00 copied SECURITY.md's published value faithfully and was still wrong. The repository was renamed PlatformRelay/assent -> PlatformRelay/Assent between v0.1.0 and v0.2.0; the Fulcio SAN carries GitHub's canonical casing, and cosign matches --certificate-identity-regexp case-SENSITIVELY. Decoded from the published bundles (openssl x509 on the bundle's certificate, X509v3 Subject Alternative Name): v0.3.0 https://github.com/PlatformRelay/Assent/.github/workflows/release.yaml@refs/tags/v0.3.0 v0.2.0 https://github.com/PlatformRelay/Assent/.github/workflows/release.yaml@refs/tags/v0.2.0 v0.1.0 https://github.com/PlatformRelay/assent/.github/workflows/release.yaml@refs/heads/main So the lowercase-only pin verified v0.1.0 and REJECTED the current releases: `install.sh --require-signature` would have failed closed against this project's own artifacts, and SECURITY.md's copy-paste instructions were already broken the same way. Both files now publish, byte-identically (one truth, REQ-AUD2-S03-04): --certificate-identity-regexp '^https://github\.com/PlatformRelay/[Aa]ssent/' The dots are escaped in the same edit — they were regexp metacharacters matching any character, so `github.com` also matched e.g. `githubXcom`. The pin is not widened beyond the org/repo: the `^` anchor stays, there is no `.*`, and another owner, an `assent-mirror` typosquat or another forge still fail. Gate changes (hack/release/install_cosign_pin_test.sh): * REQ-AUD2-S03-01/02 — new section 4c: the SANs above are committed as FIXTURES (public certificate contents) and every one is checked twice — as a regexp match and end-to-end through install.sh with the stub cosign. Six negative fixtures cover another owner, a typosquat, another org, another forge, a lost anchor and an unescaped-dot host. Section 4d re-runs the accept table against the pre-fix lowercase pin and requires it to reject at least two, so the fixture check is proved capable of catching this exact bug. * REQ-AUD2-S03-04 — the drift mutations no longer interpolate the pinned value into a sed pattern (it is itself a regexp now, which would have matched nothing and silently stopped mutating); the rewrites are flag-relative. * The stub self-test (section 4) now uses synthetic values instead of the project's pin, so a wrong pin reddens in 4b/4c naming the casing rather than in 4 blaming the stub. It also asserts the stub is permissive when unpinned, which is what makes 5c's "the pin is load-bearing" demonstration valid. Reported, deliberately NOT fixed here (not an owned path): hack/release/verify-artifacts.sh:124 runs the same unpinned `cosign verify-blob --bundle` on the maintainer/CI path.
…C-03) Doc round for AUD2-S03. No code change — hack/install.sh, SECURITY.md and hack/release/install_cosign_pin_test.sh are untouched here. openspec/specs/p5-aud2-audit-remediation/spec.md — the spec as written forbade what shipped, so it is amended in place (precedent: 1ac5692, b08be34 — spec-first still means the spec tracks what was measured): * "Reuse, explicitly" said SECURITY.md's flag pair was frozen and "the script must not invent a different issuer or identity pattern". The published pair was itself wrong, so mirroring it could not stay literal. The intent is kept, restated: the pin is verified against committed real-SAN fixtures, never chosen freely. * The AUD2-S03 Goal still quoted '^https://github.com/PlatformRelay/assent/', the value real cosign rejects. It now quotes the shipped '^https://github\.com/PlatformRelay/[Aa]ssent/' and carries an Amendment paragraph naming the cause: the Fulcio SAN carries the repo's post-rename casing (PlatformRelay/Assent from v0.2.0 on), cosign matches the regexp case-sensitively via Go RE2, so the lowercase value rejected every release from v0.2.0 on. * DoD gains the fixture clause; the stale `SECURITY.md:82–93` reference is refreshed to `SECURITY.md:80–98` at both sites (finding list and Reuse). docs/decisions/decisions.md — D-153 records the substantive fact that until now lived only in a commit body: SECURITY.md's published verification instructions were BROKEN for every release since v0.2.0. An adopter following them on v0.2.0 or v0.3.0 got a verification failure on a genuine artifact, whose natural reading is "this release was tampered with". Cause, the three decoded SANs, the rejected alternatives ((?i), ship-broken-and-follow-up), the one-truth enforcement, and a Revert line that states the revert re-breaks v0.2.0+. openspec/specs/backlog.md — AUD2-F01 tracks SEC-03's twin, hack/release/verify-artifacts.sh:124, with sizing and three traps. Recorded as a found-in-flight follow-up OUTSIDE the AUD2 story table on purpose: D-152 records AUD2 as five stories and AUD2-S05's exit gate dispositions S01–S04, so a sixth row there would move that claim. Not done, deliberately: the reviewer's optional suggestion to extend the pin with the workflow path (`.../[Aa]ssent/\.github/workflows/release\.yaml@`). It is hardening rather than a hole (exploiting the looseness needs repo write access), and it is a code + fixture change in a round the review scoped to documentation. Deferred to AUD2-F01, which touches the same pin.
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.
Integration branch for AUD2-S03 (SEC-03) — rebased
lane/aud2-s03(6a61a17) ontoorigin/main(39333fd) with the integrator-ownedCHANGELOG.mdregeneration amended in.What this closes
hack/install.shinvokedcosign verify-blobwithout--certificate-identity-regexp/--certificate-oidc-issuer. On cosign v2+ that either errors out (making--require-signaturea broken promise) or accepts any Fulcio identity — a mirror-swapped archive+bundle pair verified clean.The pin
Byte-identical in
hack/install.shand bothSECURITY.mdcosign blocks, held by a drift gate.The
[Aa]is deliberate (D-153): the repo was renamedassent→Assentbetween v0.1.0 and v0.2.0, the Fulcio SAN carries GitHub's canonical casing, and cosign matches the identity regexp case-sensitively (Go RE2). The previously published lowercase value rejected the project's own v0.2.0 and v0.3.0 artifacts. Dots are also escaped (they were metacharacters). The^anchor and owner/repo scope are unchanged, soevil-mirror,assent-mirror,PlatformRelayEvil, GitLab andgithubXcomall still fail.Verification
task check— exit 0, all 18 stages.bash hack/release/install_cosign_pin_test.sh— exit 0. Proves both pins load-bearing by mutation (deleting either re-opens SEC-03), and validates against committed real-SAN fixtures decoded from the published v0.1.0/v0.2.0/v0.3.0 bundles.task ci-audit-test— exit 0.release-install-cosign-pin-testis present in bothTaskfile.yml'scheck:list andCHECK_STAGES; both count 18 (AUD-S18 / RELSE-08 lockstep verified).Review record
REQUEST CHANGES at
ba9242c(documentation only) → doc round → APPROVE at6a61a17on fresh re-review. No P0/P1 outstanding. Originallane/aud2-s03stays on origin at6a61a17as the review record.