Skip to content

pr-merge: require a verifying run's head to be in main's history #8320

Closed

Description

@kyle-sexton

lane_dispatched (plugins/source-control/skills/babysit-prs/scripts/babysit_postmerge.py:631-670, with _lane_run at :782-788) trusts a run whose head_branch is main, and the run filter at :465 checks only head_branch == branch. The ancestry check at :817-818 (contains(merge_commit, head_sha)) accepts any descendant of the merge, not only main's own history.

Risk: an actor who already has write access, if no ruleset blocks tag creation (none of this repo's rulesets target tags today), could push a commit on top of main with a gutted pr-require-checks.yml, tag it main, and dispatch on the tag. If GitHub reports that run's head_branch as main (UNVERIFIED, not probed), a lane merge could be marked verified without its real CI. The impact is a false "verified" that frees an in-flight slot and suppresses post-merge demotion. The attacker must already hold repo write access.

Fix: also require that the run's head_sha is reachable from main's current tip (an ancestor of it, or equal), or add a ruleset that blocks tags named like branches. Check the cheaper one first.

Done when: a test with a dispatched run whose head_sha is not in main's history is not treated as verified, or a tag ruleset refusing main is live and referenced here.

Found by the reviews of #8311 and #8318 on 2026-10-18.

Activity

  1. added
    priority: lowNice-to-have, cosmetic, or speculative; opportunistic.
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    on Oct 18, 2026
  2. github-actions commented on Oct 18, 2026

    @github-actions
    Contributor

    Thanks for the report. Automated triage handed this issue to a maintainer.

    Proposed work class: work-class: scoped
    Basis: automated intake triage; a maintainer confirms or changes it.

    Automated triage. A maintainer reviews it.

  3. added
    status: confirmedA maintainer confirmed the item is real and in scope; ready to be worked.
    and removed
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    on Oct 19, 2026
  4. kyle-sexton commented on Oct 29, 2026

    @kyle-sexton
    MemberAuthor

    Closing: done. The done-when asked for a test showing that a dispatched run whose head_sha is not in main's history is not treated as verified. That is met on main.

    on_branch in plugins/source-control/skills/babysit-prs/scripts/babysit_postmerge.py (lines 1433-1440) requires the run's head commit to be an ancestor of the refs/heads/ tip, not just to carry head_branch: main. The lane-dispatch record check (around line 1847) does the same, which also refuses a tag named like the branch.

    Two tests in tests/test_babysit_postmerge.py assert that such a run does not verify: test_a_green_dispatch_off_the_branch_does_not_verify (line 1238) and test_a_tag_named_like_the_branch_does_not_verify (line 1646).

    The work landed through #8403 and #9459.

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

    priority: lowNice-to-have, cosmetic, or speculative; opportunistic.status: confirmedA maintainer confirmed the item is real and in scope; ready to be worked.

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions