Skip to content

Read a Bare Push's Destination From Branch Config, a Persisted Mirror, and Its Own Checkout in the Write Guard - #3049

Merged
ptr727 merged 6 commits into
developfrom
feature/guard-bare-push-target-3019
Oct 15, 2026
Merged

ptr727 merged 6 commits into
developfrom
feature/guard-bare-push-target-3019

Conversation

@ptr727

@ptr727 ptr727 commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

Summary

Closes the write guard's bare-push and @{push} allow gaps, plus one #2943 item. Every changed verdict goes from allow to deny.

The READMEs describe each new reading.

Verification

  • Gaps confirmed at develop's tip. Each was probed against throwaway repositories under mktemp -d with git push --dry-run --porcelain (git 2.47.3), and develop's classify allowed every one. The probes were the mirror-namespace fetch refspec and the single-branch clone (git wrote develop both times), a persisted mirror (git deleted a remote-only branch, and with --tags it deleted develop), a timed-out @{push} read, --x:refs/heads/develop after -- and after --end-of-options, and -C, cd, sudo -D, and wrapped pushes into a checkout whose origin requires a pull request.
  • Develop-versus-branch differential. Both guards' classify ran over the same inputs: every develop _GIT_CASES row (301), 99,603 generated push shapes over seven constructed repositories and three rule maps, the 133 _PUSH_DIR_CASES, and 189 runs of extra shapes. 0 deny -> allow, 13,007 allow -> deny. By mechanism, those are 5,138 pushes into a missing directory, 2,569 each under sudo -D, env -C, and a wrapper shell, 146 in a checkout with a persisted mirror, and 16 dash-named refspecs after the boundary. A plain push, a timeout prefix, a run-time word elsewhere, a later cd, git tag/git commit before a push, a glob refspec, and a checkout tracking origin/develop under push.default unset or current each kept develop's verdict.
  • Revert proof. With develop's _check_push_bypass, _push_targets, _current_push_branch, _read_branch, and _git_stdout swapped into the branch module, all 19 new deny cases in the selftest fail and every control passes.
  • gh-write-guard.py --selftest passes, and passes too with an empty PATH, as the installer's no-git test runs it. test_install.py passes (161 tests), and so does the root tests/ suite (2,813 tests). Ruff format and check, mypy, prose_lint.py, the eol and eol-coverage gates, spec/validate.py, and markdownlint, editorconfig-checker, and cspell in Docker pass too.

Local Review

Frontier local-strict-review passes ran before every push. These findings were fixed:

  • A run-time word anywhere, a timeout prefix, or a later cd had marked a push's checkout unnamed. That over-denied, so _repo_may_move replaced it.
  • The checkouts of all pushes in a command had been applied to every target. Each push now carries its own.
  • A README claim, a control row that proved nothing, a live git read in the offline selftest rows, and sentence length were also fixed.
  • One deny -> allow was found and removed: a decoding change on the origin URL read was reverted to develop's strict form.

The opening push listed four introduced findings from its final pass. Three are fixed on later pushes, and the fourth is filed:

  • A later cd now moves a push's checkout only where a loop or a function definition may run it first, so a pipe, a background job, or a subshell after the push no longer counts.
  • The launcher test skips options, numbers, and redirections, and setsid, gtimeout, time, flock, and winpty keep their directory.
  • The _repo_may_move docstring names uv as its moving example.
  • Read Every Launcher in Front of a Push in the Write Guard #3050: only the first launcher word is read, so a staying launcher in front of a moving one hides it. Develop allows those shapes too.

Filed rather than widened here:

Partially addressed: #2943, its item on the directory a push runs in only.

Closes on promotion: #3019, #3020, #3029, #3036

🤖 Generated with Claude Code

ptr727 and others added 3 commits October 14, 2026 19:31
…ing in the Write Guard

A bare push now also reads its destination from push.default and
branch.<name>.merge, reads a persisted remote.<name>.mirror, and is
unresolved when a branch or push-target read times out or cannot run.
A dash-named word after the first -- or --end-of-options is also read as
a positional. A push that -C or a leading cd runs in another checkout is
also checked against that checkout's origin, and one whose checkout the
guard cannot name counts as having undetermined rules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…out Reading

A push's checkout is now read only from a directory-moving word before it,
so a timeout prefix, a run-time word elsewhere, or a later cd no longer
marks its repository unnamed. Each push's targets are checked against its
own checkout's origin rather than every checkout in the command.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The moved-checkout reading now covers an eval, source, or . after a shell
keyword, a cd a loop or function may run before the push, a git directory
variable anywhere before it, and git run by a command not known to keep
its directory. The origin URL read keeps develop's strict decoding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings October 15, 2026 03:21
@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: Repository: ptr727/ProjectTemplate/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 0da264f4-4ba8-4807-b1e1-d74cb1ae0490
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ptr727

ptr727 commented Oct 15, 2026

Copy link
Copy Markdown
Owner Author

A recorded local strict-review pass covers head 13729a2837cffd49ff1ce3a9e2aead4a3149764d, the content this pull request carries at that commit against develop, and it recorded 6 findings.

@codecov

codecov Bot commented Oct 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (develop@4720440). Learn more about missing BASE report.

Additional details and impacted files
@@            Coverage Diff             @@
##             develop    #3049   +/-   ##
==========================================
  Coverage           ?   61.21%           
==========================================
  Files              ?       17           
  Lines              ?     9062           
  Branches           ?        0           
==========================================
  Hits               ?     5547           
  Misses             ?     3515           
  Partials           ?        0           
Flag Coverage Δ
python-3.13 61.21% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Fix repository-move detection and quoting handling, and correct the contradictory timeout documentation.

2 open findings
What changed in this PR

This PR hardens the Git push write guard by detecting configured destinations, persisted mirrors, failed reads, option-boundary refspecs, and alternate checkouts.

Changes:

  • Expands push destination and mirror detection.
  • Fails closed on unresolved metadata and checkout paths.
  • Updates documentation and self-tests.
File Summary
host-setup/​agent-safety/​README.md Documents expanded push detection behavior.
host-setup/​agent-safety/​claude/​README.md Updates Claude hook behavior documentation.
host-setup/​agent-safety/​claude/​gh-write-guard.py Implements push analysis, checkout resolution, and tests.

🧠 Review effort: Lite


💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread host-setup/agent-safety/claude/gh-write-guard.py Outdated
Comment thread host-setup/agent-safety/claude/gh-write-guard.py Outdated
…ncher Options

A cd written after a push now moves its checkout only where a loop or a
function may run it first, so a pipe or a background job no longer does.
The launcher test skips options, numbers, and redirections, and keeps
setsid, gtimeout, time, flock, and winpty as launchers that stay put.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ptr727 and others added 2 commits October 14, 2026 20:46
A later cd now moves a push's checkout only where a loop or a function
definition may run it first, so a subshell or a command substitution
after the push no longer counts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A definition written with its brace on the next line, or with a subshell
body, lexes its () fused with the next operator, so that token now counts
as a definition too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ptr727

ptr727 commented Oct 15, 2026

Copy link
Copy Markdown
Owner Author

A recorded local strict-review pass covers head 1eb4d5c9b4c72d04b2c0783c3d0797f07485f96a, the content this pull request carries at that commit against develop, and it recorded 2 findings.

@ptr727
ptr727 merged commit bcb8394 into develop Oct 15, 2026
13 checks passed
@ptr727
ptr727 deleted the feature/guard-bare-push-target-3019 branch October 15, 2026 04:14
ptr727 added a commit that referenced this pull request Oct 15, 2026
… Narrative Findings, and Bootstrap Handoff Tests to Main (#3051)

- #3042 brings the agent-safety READMEs up to the write guard's #3023
and #3037 readings: `--prune` beside a pattern, `:`, `--all`, or a bare
push, a `--mirror` push with no refspec, a `heads/` destination outside
`refs/`, the full `@{push}` read, and a bare push denied when `git
remote` cannot list the remotes
- #3041 has `backlog-burndown` keep an abandoned group's claim comment
until cleanup decides its release, say stopped on each path that sends a
group to the maintainer, and name a branch no pull request has used when
a claim comment first names it
- #3044 tests `bootstrap.sh`'s `--report` handoff to the agent tools
step, tests the `--agents` handoff on Linux and through
`Invoke-AgentsInstall` under `pwsh`, and moves the unprivileged-root
probe into `tests/host_capability.py`
- #3045 makes `pr_review.py` read an `Open findings remain:` list in a
Copilot review and count each entry no thread of the round accounts for,
pairing by severity tier and file, so the count can only rise
- #3049 makes `gh-write-guard` read a bare push's destination from
`push.default` and `branch.<name>.merge`, read a persisted
`remote.<name>.mirror`, deny when the `@{push}` read times out or cannot
run, give a dash-named word after `--` or `--end-of-options` a reading
as a remote or refspec, and judge a push moved into another checkout
against that checkout's origin as well. A differential against the
previous guard found no deny-to-allow flip. It addresses one item of
#2943, which stays open
- #3054 makes `gh-write-guard` answer unresolved, and so deny, when its
`push.default` read times out or cannot run, a promotion fix for this
pull request's first review
- #3062 brings both agent-safety READMEs up to #3054: an unset
`push.default` is read as `simple`, both destinations are read for a
value git does not define or a read exiting with any other error, and a
read that times out or cannot run is denied
- #3059 bumps `actions/create-github-app-token` from 3.2.0 to 3.3.0

After this merges, the host-safety installer payload changes, so the
maintainer runs the installer after promotion.

Fixes #3021
Fixes #3026
Fixes #3027
Fixes #3039
Fixes #3033
Fixes #3038
Fixes #3034
Fixes #3031
Fixes #3019
Fixes #3020
Fixes #3029
Fixes #3036
Fixes #3052

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.

2 participants