From 33eca3e1002125f83e8e0ec1e317c35307f1f89a Mon Sep 17 00:00:00 2001 From: kunchenguid Date: Thu, 20 Aug 2026 19:37:10 -0700 Subject: [PATCH] ci: bind the no-mistakes attestation to the current PR head --- .github/workflows/no-mistakes-required.yml | 27 ++++++++++- test/workflows/no-mistakes-gate.test.ts | 56 ++++++++++++++++++++-- 2 files changed, 79 insertions(+), 4 deletions(-) diff --git a/.github/workflows/no-mistakes-required.yml b/.github/workflows/no-mistakes-required.yml index 9837e54..4a11477 100644 --- a/.github/workflows/no-mistakes-required.yml +++ b/.github/workflows/no-mistakes-required.yml @@ -39,6 +39,7 @@ jobs: PR_BODY: ${{ github.event.pull_request.body }} PR_AUTHOR: ${{ github.event.pull_request.user.login }} PR_NUMBER: ${{ github.event.pull_request.number }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} run: | set -eu marker='Updates from [git push no-mistakes](https://github.com/kunchenguid/no-mistakes)' @@ -165,7 +166,31 @@ jobs: exit 1 fi - echo "Attestation head_sha: $(printf '%s' "$payload" | jq -r '.head_sha // "(absent)"')" + attested_head="$(printf '%s' "$payload" | jq -r '.head_sha // ""')" + echo "Attestation head_sha: ${attested_head:-(absent)}" + + # Head binding. The attestation describes the commit no-mistakes ran + # its steps on; a later push moves the PR head without rewriting the + # body, so an attestation that does not name the current head proves + # nothing about the code being merged. A `synchronize` whose body was + # NOT rewritten by no-mistakes going red is the intended contract, not + # a false positive. + if [ -z "$attested_head" ] || [ -z "${PR_HEAD_SHA:-}" ] || [ "$attested_head" != "$PR_HEAD_SHA" ]; then + echo "::error::The no-mistakes pipeline attestation is STALE for the current head of PR #${PR_NUMBER}." + { + echo + echo "Attestation head_sha: ${attested_head:-(absent)}" + echo "PR head sha: ${PR_HEAD_SHA:-(absent)}" + echo + echo "A commit was pushed after the no-mistakes run, so the attestation does not" + echo "describe the code this PR now proposes to merge." + echo + echo "Re-run 'git push no-mistakes' to refresh it." + echo + echo "PR author: ${PR_AUTHOR}" + } >&2 + exit 1 + fi tab="$(printf '\t')" gate_status=0 diff --git a/test/workflows/no-mistakes-gate.test.ts b/test/workflows/no-mistakes-gate.test.ts index bd6608e..0d29e86 100644 --- a/test/workflows/no-mistakes-gate.test.ts +++ b/test/workflows/no-mistakes-gate.test.ts @@ -54,13 +54,17 @@ if (process.env.CI && posix && !runnable) { ); } -function runGate(body: string): { code: number; output: string } { +function runGate( + body: string, + headSha: string = HEAD_SHA, +): { code: number; output: string } { const result = spawnSync("bash", [scriptPath], { env: { ...process.env, PR_BODY: body, PR_AUTHOR: "somedev", PR_NUMBER: "42", + PR_HEAD_SHA: headSha, }, encoding: "utf8", }); @@ -89,9 +93,12 @@ function prBody(attestationPayload?: string): string { ].join("\n"); } -function attestation(steps: Array<[string, string]>): string { +function attestation( + steps: Array<[string, string]>, + headSha: string = HEAD_SHA, +): string { return JSON.stringify({ - head_sha: HEAD_SHA, + head_sha: headSha, steps: steps.map(([step, status]) => ({ step, status })), }); } @@ -189,6 +196,49 @@ describe.runIf(runnable)("no-mistakes PR gate", () => { }); } + // Head binding: the attestation describes the commit no-mistakes ran on, so + // an attestation naming any other commit says nothing about what is being + // merged. A synchronize whose body was not rewritten by no-mistakes going red + // is the contract, not a false positive. + it("accepts an attestation whose head_sha is the PR's current head", () => { + const { code, output } = runGate( + prBody(attestation(HEALTHY_STEPS, HEAD_SHA)), + HEAD_SHA, + ); + expect(code).toBe(0); + expect(output).toContain(`Attestation head_sha: ${HEAD_SHA}`); + }); + + it("rejects an attestation whose head_sha is not the PR's current head", () => { + const staleSha = "0000000000000000000000000000000000000000"; + const { code, output } = runGate( + prBody(attestation(HEALTHY_STEPS, staleSha)), + HEAD_SHA, + ); + expect(code).toBe(1); + expect(output).toContain("attestation is STALE for the current head"); + expect(output).toContain("Re-run 'git push no-mistakes' to refresh it"); + expect(output).toContain(staleSha); + expect(output).toContain(HEAD_SHA); + }); + + it("fails closed when the attestation carries no head_sha at all", () => { + const payload = JSON.stringify({ + steps: HEALTHY_STEPS.map(([step, status]) => ({ step, status })), + }); + const { code, output } = runGate(prBody(payload), HEAD_SHA); + expect(code).toBe(1); + expect(output).toContain("attestation is STALE for the current head"); + expect(output).toContain("Attestation head_sha: (absent)"); + }); + + it("fails closed when the PR head sha is unavailable", () => { + const { code, output } = runGate(prBody(attestation(HEALTHY_STEPS)), ""); + expect(code).toBe(1); + expect(output).toContain("attestation is STALE for the current head"); + expect(output).toMatch(/PR head sha: +\(absent\)/); + }); + it("fails closed on an attestation payload that is not valid JSON", () => { const { code, output } = runGate( prBody('{"head_sha":"abc","steps":[{"step":"review",'),