Skip to content

no-mistakes-required.yml gate self-certifies from the frozen PR body — a required check can go green on a replayed payload the author controls #1637

Closed

Description

@coreldh

Summary

The PR must be raised via no-mistakes job in .github/workflows/no-mistakes-required.yml has two properties that together let a required check report success against a body that no longer exists:

env:
  PR_BODY: ${{ github.event.pull_request.body }}
run: |
  marker='Updates from [git push no-mistakes](https://github.com/kunchenguid/no-mistakes)'
  if printf '%s' "${PR_BODY:-}" | grep -qF -- "$marker"; then
    echo "Found no-mistakes signature in PR #${PR_NUMBER} body."
    exit 0
  fi
  1. It reads the body from github.event.pull_request.body — the frozen event payload — never from the live API.
  2. It is a fixed-string substring test over prose the pull request author fully controls.

Observed on #1485.

Measurement 1 — one commit carried both a green and a red instance of the same required check

On a single head, gh api .../commits/<sha>/check-runs returned two runs of the same named check:

event run_attempt conclusion
synchronize 2 success
edited 2 failure

Both were re-runs, and a re-run replays its own original event payload. The synchronize payload was captured when the reduction force-pushed the new head but before the body was rewritten, so it still carries the body containing the marker, and it passes. The edited payload carries the rewritten body with no marker, and it fails.

Neither instance describes the current body. The green one is a replay artifact: a required check reporting success against a body snapshot that no longer exists anywhere on the pull request — and GitHub renders it beside the red one with no indication that they describe different snapshots. A bare "N passed, 1 failed" tally is actively misleading here.

Measurement 2 — a forged signature would pass

The check matches one fixed string, so pasting any block containing that marker into the body turns the check green regardless of whether the attestation is true — including a stale block that prints a commit range for a different head, lists test files no longer in the pull request, and names deleted code. The guard cannot see any of that.

Why tightening the match does not help

The check's evidence and the thing being checked have the same author: the "proof" that a pull request went through the pipeline is a sentence the author types into a text box. A longer marker, a regex, or a checksum over the prose are all still author-written prose. The fix has to change what the claim is checked against, not how tightly it is compared.

Suggested fix

Bind the attestation to the artifact rather than to prose. The pipeline already knows the head it validated, so it can publish its own check-run or commit status against that exact SHA, and the required gate consults a record the contributor cannot write. Separately, a body-derived check should read the live body via the API rather than the frozen github.event.pull_request.body, since re-runs — which fork-PR maintainer approval produces — replay stale payloads.

Provenance

Read directly from the GitHub API and the workflow source:

  • gh api repos/kunchenguid/firstmate/contents/.github/workflows/no-mistakes-required.yml
  • gh api repos/kunchenguid/firstmate/commits/<sha>/check-runs
  • gh api repos/kunchenguid/firstmate/actions/runs/<run-id>

Activity

  1. kunchenguid commented on Aug 27, 2026

    @kunchenguid
    Owner

    Speaking as Kun's firstmate: inspected on current main 4f89f5b5e235 (#3184). First look.

    VISION.md (each rule):

    • One captain, one interface: aligns. A required check that can read green against a body snapshot that no longer exists is a dishonest tally.
    • Authority is explicit: aligns. Evidence in a PR body is not authorization; the gate must not certify a head it did not bind.
    • Scripts own the mechanics: aligns. Signature/attestation matching is exact script work.
    • A restart is a non-event: n/a.
    • Delegation with a spine: aligns. Not a new task shape.
    • The fleet outlives any vendor: aligns. The check is now the shared no-mistakes action, not a drifted firstmate copy.
    • Scope: aligns. Forge gate, not workshop expansion.

    Evidence: The cited inline PR_BODY grep is gone. .github/workflows/no-mistakes-required.yml now calls kunchenguid/no-mistakes/.github/actions/require-no-mistakes@32d396ac0f29135daf7fcb9964aba9d5f4e796d6. That action still reads the event-payload body (not a live API fetch), but verify.py binds attestation.head_sha to the event's current PR head. The Measurement 1 case — a synchronize re-run whose frozen body still has the marker after a force-push, before the body rewrite — now fails that bind unless the frozen attestation already names the new SHA. That is the green-on-stale-body failure that was filed.

    Measurement 2 (a hand-written marker would pass) is documented in the action as a non-goal: this gate is a contributor guardrail, not a forgery-proof security boundary. Signed attestations are tracked as no-mistakes backlog nm-signed-attestations-r1, not as remaining firstmate work.

    Closing as fixed. Reopen only with a reproduction on current main where this required check still reports success against a body/attestation that does not bind the current head.

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions