fix: refuse a push refspec that begins with '+', so no push can force an update #839

Merged
hexajon merged 1 commit from issue-637-push-leading-plus into main 2026-10-20 16:40:52 +00:00
Owner

Summary

In a git push refspec, a leading + forces the update, so the remote ref can lose commits. git also accepts + at the start of a branch or tag name, so a valid name is no proof of a safe refspec. Before this change, a branch value such as +main passed ValidateRef and reached git push as a forcing refspec.

The change:

  • A new ValidatePushRef in internal/git/validate.go refuses a leading - (as ValidateRef does) and a leading +. The refusal says why and names the fix: use a name that does not begin with +.
  • Writer.Push, Writer.PushForce, and Writer.PushTag run the check before the guard and before any backend runs.
  • The pushes that run outside the Writer run it too: GitPushDir, the AGit push, the service push, issue develop, the initial finish push, and tag --push.

Tests refuse a +-prefixed branch or tag on the Writer (before the backend runs), GitPushDir, the AGit push, the service push, the orchestrate push, issue develop, and tag --push. The initial finish push runs the check but has no test of its own. #833 moves that push into the Writer, where the Writer tests cover it.

./scripts/gate.sh passes on this commit.

Closes #637

## Summary In a `git push` refspec, a leading `+` forces the update, so the remote ref can lose commits. git also accepts `+` at the start of a branch or tag name, so a valid name is no proof of a safe refspec. Before this change, a branch value such as `+main` passed `ValidateRef` and reached `git push` as a forcing refspec. The change: - A new `ValidatePushRef` in `internal/git/validate.go` refuses a leading `-` (as `ValidateRef` does) and a leading `+`. The refusal says why and names the fix: use a name that does not begin with `+`. - `Writer.Push`, `Writer.PushForce`, and `Writer.PushTag` run the check before the guard and before any backend runs. - The pushes that run outside the Writer run it too: `GitPushDir`, the AGit push, the service push, `issue develop`, the initial finish push, and `tag --push`. Tests refuse a `+`-prefixed branch or tag on the Writer (before the backend runs), `GitPushDir`, the AGit push, the service push, the orchestrate push, `issue develop`, and `tag --push`. The initial finish push runs the check but has no test of its own. #833 moves that push into the Writer, where the Writer tests cover it. `./scripts/gate.sh` passes on this commit. Closes #637
fix: refuse a push refspec that begins with '+', so no push can force an update
Some checks failed
drift / govulncheck (pull_request) Has been skipped
drift / surface-diff (pull_request) Successful in 38s
drift / forgejo-smoke (pull_request) Has been skipped
ci / mcp-conformance (pull_request) Successful in 1m32s
ci / mcp-conformance (push) Successful in -28s
drift / govulncheck (push) Has been skipped
drift / surface-diff (push) Successful in -37s
drift / forgejo-smoke (push) Has been skipped
ci / gate (push) Failing after 3m10s
ci / gate (pull_request) Failing after 9m34s
c8598c0506
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sixfold-space/madtea!839
No description provided.