Skip to content

Bind PR head, merge-preview tree, input diff, and check attribution in evidence #222

Open

Description

@fraware

Why this is a release-relevant issue

Discovered by first-user audit, 15 October 2026. On pull_request, GitHub documents github.sha/GITHUB_SHA as the synthetic PR merge commit (refs/pull/N/merge); github.event.pull_request.head.sha is the contributor's actual PR head. See official GitHub Actions pull_request semantics.

The current action.yml (checked at main) correctly notices the two identifiers in its change-input step: OVK_EVENT_HEAD_SHA: ${{ github.event.pull_request.head.sha }} and OVK_GITHUB_SHA: ${{ github.sha }}. It constructs the default diff via git diff BASE_SHA HEAD_SHA. Later, however, five steps (Run OVK multi-lane verification, Run OVK check path, Emit OVK GitHub check (check/verify) and further downstream emission) pass OVK_HEAD_SHA: ${{ github.sha }}, giving the synthetic merge SHA as the evidence subject/check target.

This is a potential semantic/attestation binding ambiguity, not yet a demonstrated exploit. The default actions/checkout also checks out the merge commit, so github.sha can faithfully identify the checked-out merge tree; however, the extracted input diff is generated from the PR head and base. A transferable report cannot silently equate the merge tree, PR-head tree, diff target and reviewer-visible check identity. Base drift and conflicts can make the discrepancy material. Fixing one string without fixing the model can worsen the trust boundary.

Proposed contract and acceptance

  • Define an explicit SubjectIdentity carrying PR base SHA, PR head SHA, evaluated checkout/tree SHA, comparison mode (HEAD/base vs MERGE/base), checker input-diff digest, source-profile digests and toolchain identity.
  • Choose/document a single coherent evaluation mode for the first supported product lane: either (A) check PR head and base with checkout matching head, or (B) check a merge-preview tree with an explicit merge-preview subject and explicitly related head. Do not conflate them in evidence.
  • Record/validate both the actual checked-out tree and the diff source revision. Fail closed if event metadata, checkout and evidence subject conflict or if fetch/merge history does not support the intended comparison. Preserve bounded handling for fork PRs and untrusted PR code.
  • Keep PR-check presentation anchored to a suitable GitHub head SHA for reviewers, while the evidence truthfully states exactly what tree was checked. If GitHub API requires separate check attribution, represent check presentation target separately from verified source subject and do not compromise freshness checks.
  • Test a PR where head and merge differ; simulate base branch moving after the PR head, missing/nonfetchable SHA, fork PR, and replaying evidence for another base/head pair. Regression must reject false reuse and stale subject binding.
  • Do not switch to pull_request_target plus untrusted PR checkout. Minimize token write permissions and follow GitHub's documented fork-PR security boundaries. Do not execute PR-supplied scripts with elevated privileges.
  • No public shareable 'verified PR' receipt until the above source-identity semantics and provenance are qualified. Advisory reports can continue with explicit scope/unknown and no production reliance.

Priority: before expanding PR evidence distribution or GitHub App. This is more important for the North Star than another abstract dependency theorem: shareable evidence must be about the source revision actually evaluated. Related: #216, #221, #25, release-hardening draft #220, guarantee report draft #218.

Not an authorization to merge or release: this issue establishes a precise engineering investigation/acceptance gate only.

Activity

  1. fraware commented on Oct 15, 2026

    @fraware
    OwnerAuthor

    P0 implementation checkpoint — 15 Oct 2026: Draft PR #223 now proposes a concrete, fail-closed identity contract for the Action's default PR input: require checkout commit == GitHub event github.sha, require its raw two merge-parent SHAs == event base.sha and pull_request.head.sha, require the base commit object, then compute git diff BASE MERGE to match the evidence subject. Produce a JSON sidecar with checkout/tree, diff SHA-256, exact comparison, caller-bound/unbound marker and explicit no signed-envelope binding / no relying authority. Refuse pull_request_target, SHA mismatch and strict caller-selected diffs. Automated GitHub Check / PR-comment publication is additionally suppressed for advisory caller-supplied unbound diffs.

    Real CI dogfood run 38818371087 successfully executed a genuine synthetic GitHub merge-preview comparison, printing event_base_to_verified_merge_preview. The same first run's full unit job failed against an existing release-governance test that indiscriminately demanded PR-head checkout for all jobs. PR #223 now narrowly keeps the recorded PR head for source-validation/release jobs while allowing exactly the two Action-dogfood jobs to check out the merge preview with depth 2; a regression test preserves the distinction. This is a policy correction, not a waiver of release-head integrity.

    Exact head: 9d0cfd95ba1f8d002e20d564765a67efe632e6ca. CI 38819081731 and six-cell repro 38819081712 are queued, not accepted.

    Issue #222 remains open until the typed source identities and diff digest are bound into independently authenticated machine evidence; reviewer-visible GitHub check target is explicitly modeled separately; relevant fork/private shallow-checkout cases and real-source/profile semantics are qualified. This PR is a bounded correctness foundation, not a complete attested source-verification release. Keep draft and reconcile its action.yml overlap with separate release PR #220 under review, not by an untested merge.

  2. fraware commented on Oct 16, 2026

    @fraware
    OwnerAuthor

    Live dogfood-confirmed source/evidence mismatch — 2026-10-15; draft correction #227

    Real GitHub Actions run 38830701134, action_default_diff_dogfood, failed with OVK refusing unsupported job summary: evidence subject disagrees with the observed checkout. I downloaded artifact ovk-evidence-artifacts-action_default_diff_dogfood (artifact id 12017765340) and inspected both JSON files.

    • Source receipt ovk-source-binding.json: evaluated_checkout_commit_sha=6c706febb13c32b75ae9cd6ab13a2023b3daa961 (synthetic merge preview), event_pr_head_sha=90f0a4492056149fd57c84502d2ebea80d2d8231; evidence_subject_sha=6c706febb13c32b75ae9cd6ab13a2023b3daa961; source diff hash 6d4e37f5cb607cd5fbb8c13330c8176b9f7d8cf0068ba8f61096fa5ea71bb04b.
    • Actual ovk-evidence.json bundle subject.head_sha: 90f0a4492056149fd57c84502d2ebea80d2d8231 (contributor PR head), not the receipt's checked-out merge SHA.
    • Source cause: ovk.core.context.build_repository_context overwrites a caller-supplied head_sha with GitHub event pull_request.head.sha. The Action passes github.sha on ovk check, but that argument is overridden after source binding. Earlier CI and source-binding tests checked the receipt and source diff, not the resulting evidence bundle's subject.

    This invalidates the earlier narrative that the default Action always produces matching merge-bound evidence. There is no signed/relying guarantee; do not publicly display this evidence as merge-preview verified. The read-only renderer failed closed and a direct attempted Check emission from mismatched source may also be refused, depending on its emission target.

    Draft #227 now includes:

    1. ovk check --source-binding structural validation of current HEAD/tree, PR event base/head/repository, exact diff bytes, and requested merge subject.
    2. An opt-in kernel/context path to preserve the validated merge-preview evidence subject while retaining the distinct contributor head in event metadata; unbound callers retain historical event behavior.
    3. Real two-parent Git fixture + adversarial tests, and exact-head Action dogfood qualification that must verify a generated report against the actual receipt.
    4. No signed certificate, production authorization, generic source-to-model proof, strict reliance, or historical portability claims.

    Status: draft candidate; CI not yet certified on final head; no main merge, release or external adoption.

  3. fraware commented on Oct 16, 2026

    @fraware
    OwnerAuthor

    First independently inspected real-PR correction observation (development only)

    At intermediate draft #227 commit b7020afa50db8d6b25d52d7f1c697d2424f01cbd, the live action_default_diff_dogfood job in CI run 38839898034 completed SUCCESS after the CLI subject-selection correction. The job used the ordinary GitHub pull-request synthetic merge-preview checkout.

    I downloaded the actual GitHub artifact ovk-evidence-artifacts-action_default_diff_dogfood (artifact id 12021550038) and separately compared:

    • receipt evaluated_checkout_commit_sha=9af58527fa069420392cbb99aaa34b63199acfe8;
    • receipt evidence_subject_sha=9af58527fa069420392cbb99aaa34b63199acfe8;
    • ovk-evidence.json subject.head_sha=9af58527fa069420392cbb99aaa34b63199acfe8;
    • original event_pr_head_sha=b7020afa50db8d6b25d52d7f1c697d2424f01cbd (correctly distinct);
    • event base 79370539ca9b26a0a6b01f6ac6ab6923243f40f4.

    Result: one real workflow has established that the corrected local observed receipt and final evidence subject match. The earlier failed artifact and the corrected passing artifact are both retained as a before/after causal regression record, not a proof of GitHub event authenticity or source-to-IR completeness.

    Important: this run is at an intermediate SHA; final draft #227 head has additional adversarial tests and documentation, and final-head CI, six-cell reproducibility, native-backend workflows, and independent review remain open. No main merge/release/strict authority.

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