Skip to content

source-control:babysit-prs: --apply helpers need mechanically-validated wrappers like the merge gate and thread resolver #1281

Description

@kyle-sexton

The babysit-prs helpers that take --apply perform their mutations inside their own process. A host permission classifier decides on the outer shell command only, so nothing downstream can reconsider the individual operation:

  • refresh_pr_branch.py --apply issues PUT repos/{repo}/pulls/{n}/update-branch. No host rule governing gh sees that call.
  • prune_babysit_worktrees.py --apply runs git worktree remove. No host rule governing git sees that either.
  • manage_babysit_lease.py and manage_feedback_ledger.py write state files.
  • request_review.py --apply posts a comment — the one exception, because the comment body arrives in argv and is therefore visible to whatever reads the command line.

The consequence is that a host grant covering these scripts is necessarily a grant of whatever the script does, not of the operation the operator had in mind. Two of this skill's own components already solve exactly this and are the model:

  • babysit_merge.py is check-only unless --merge is passed, so the mutating mode is selected by a flag readable from the command text.
  • source-control-babysit-resolve-thread enforces bot-only participation and the outdated check in code, and refuses a bulk resolve with no thread pin.

Both make the permitted operation a property the invocation can be checked against. The --apply helpers do not.

Ask

Give each mutating mode a wrapper (or an in-script guard) that mechanically validates the single operation it is permitted, matching the merge gate's and the resolver's shape — so a host grant can name a wrapper whose behavior is bounded rather than a script whose --apply branch is unbounded.

Worth noting that two of the four are already structurally safe by accident of implementation, and locking that in is part of the ask rather than new work:

  • refresh_pr_branch.py pins expected_head_sha and validate_current_candidate refuses when headRefOid != expected_head_sha. Server-side and head-pinned, so it cannot force-push or rewrite history.
  • prune_babysit_worktrees.py calls git worktree remove without --force and classifies a dirty worktree as keep_dirty, so it cannot discard uncommitted work.

Those are properties of the current implementation with no test asserting them, which is the gap: a future refactor could add --force and nothing would notice.

Context

Found by the Codex reviewer on melodic-software/dotfiles#315, which hardens the host-side autoMode prose that grants these scripts. That PR removed the sentence claiming the nested actions stayed governed by its gh and git entries, because the claim is not checkable. It now records the structural properties above as properties of today's installed code rather than as guarantees, and points at this issue as the real fix.

Related: #843 (plugin bin/ not on the Bash tool PATH, which is why host grants still name raw script paths).

Activity

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

    area: securitySecurity-relevant: vulnerability, hardening, or disclosure follow-up.needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.priority: mediumReal value, no hard deadline; normal backlog flow.work-class: structuralRefactors, migrations, contract changes; cross-cutting and hard to reverse.

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions