Skip to content

Block +<refspec> force pushes in the pre-bash guard#38

Merged
cgraf78 merged 1 commit into
mainfrom
fix-push-force-refspec
Jul 2, 2026
Merged

Block +<refspec> force pushes in the pre-bash guard#38
cgraf78 merged 1 commit into
mainfrom
fix-push-force-refspec

Conversation

@cgraf78

@cgraf78 cgraf78 commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Summary

Close a bypass in the git push --force guard: a refspec that leads
with + forces the remote update without --force/-f.

agent-hook-pre-bash blocked git push --force and -f, but its
per-argument scan only looked at flags. git push origin +main,
+HEAD:main, or +refs/heads/x:y all force-overwrite the remote ref and
slipped through unguarded.

  • add a +?*) arm to the post-push argument loop: a positional refspec
    starting with + (the force marker) sets the force-push guard. A remote
    name can't start with +, and a leading + on a refspec always means
    force, so there is no legitimate non-force arg this catches.
  • --force-with-lease (and =ref) stay allowed as before

Known, pre-existing gaps left as-is: git push origin -- +main (the scan
stops at --) and --mirror; neither is a common agent form.

Testing

  • new agent-hook-bash-guards-test cases: +main, +HEAD:main, and
    +refs/heads/x:y are blocked; plain origin main and a non-force
    HEAD:main refspec stay allowed
  • verified red-green: removing the +?*) arm lets +main push through
    (exit 0 instead of blocked)
  • full suite green (./test/agentguard-test: all suites 0 failed);
    checkrun lint clean

Summary

Close a bypass in the `git push --force` guard: a refspec that leads
with `+` forces the remote update without `--force`/`-f`.

`agent-hook-pre-bash` blocked `git push --force` and `-f`, but its
per-argument scan only looked at flags. `git push origin +main`,
`+HEAD:main`, or `+refs/heads/x:y` all force-overwrite the remote ref and
slipped through unguarded.

- add a `+?*)` arm to the post-`push` argument loop: a positional refspec
  starting with `+` (the force marker) sets the force-push guard. A remote
  name can't start with `+`, and a leading `+` on a refspec always means
  force, so there is no legitimate non-force arg this catches.
- `--force-with-lease` (and `=ref`) stay allowed as before

Known, pre-existing gaps left as-is: `git push origin -- +main` (the scan
stops at `--`) and `--mirror`; neither is a common agent form.

Testing

- new `agent-hook-bash-guards-test` cases: `+main`, `+HEAD:main`, and
  `+refs/heads/x:y` are blocked; plain `origin main` and a non-force
  `HEAD:main` refspec stay allowed
- verified red-green: removing the `+?*)` arm lets `+main` push through
  (exit 0 instead of blocked)
- full suite green (`./test/agentguard-test`: all suites 0 failed);
  `checkrun lint` clean
@cgraf78 cgraf78 merged commit 592d671 into main Jul 2, 2026
6 checks passed
@cgraf78 cgraf78 deleted the fix-push-force-refspec branch July 2, 2026 12:09
cgraf78 added a commit that referenced this pull request Jul 2, 2026
Summary

Extend the force-push guard to catch `git push --mirror`, which
force-overwrites and prunes every remote ref — remote-destructive even
without `--force`. Follow-up to the `+<refspec>` fix (#38).

- add a dedicated `_GUARD_GIT_PUSH_MIRROR` guard and a `--mirror)` arm
in
  the post-`push` argument scan
- give it its own message (the force guard's `--force-with-lease` advice
  does not fit a mirror push): run it manually if a full mirror is truly
  intended
- `--prune`/`--all` are left out of scope; `--mirror` is the clear
  remote-destructive gap

Testing

- new `agent-hook-bash-guards-test` cases: `git push --mirror`,
`--mirror origin`, and `git -C repo push --mirror` are blocked with the
mirror-specific message; normal pushes, `+refspec`,
`--force-with-lease`,
  and `-u` are unaffected
- verified red-green: removing the `--mirror)` arm lets `--mirror`
through
  (exit 0)
- full suite green (`./test/agentguard-test`: all suites 0 failed);
  `checkrun lint` clean
cgraf78 added a commit that referenced this pull request Jul 2, 2026
Summary

Extend the push guard to catch `git push --prune`, which deletes remote
refs that have no local counterpart — remote-destructive. Follow-up to
the `+<refspec>` (#38) and `--mirror` (#39) guards.

- add a dedicated `_GUARD_GIT_PUSH_PRUNE` guard and a `--prune)` arm,
with
  its own message
- bare `--all` is intentionally NOT guarded: it only pushes branches and
  is destructive only combined with `--force`, which is already caught

Testing

- new `agent-hook-bash-guards-test` cases: `git push --prune ...` is
  blocked with the prune-specific message; bare `git push --all origin`
  stays allowed
- verified red-green: removing the `--prune)` arm lets `--prune` through
  (exit 0)
- full suite green (`./test/agentguard-test`: all suites 0 failed);
  `checkrun lint` clean
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant