push: a branch value with a leading + force-pushes past ValidateRef #637
Labels
No labels
breaking
bug
documentation
enhancement
epic
good first issue
help wanted
refactoring
resolution/duplicate
resolution/invalid
resolution/wontfix
security
severity/critical
severity/high
severity/low
severity/medium
status/abandoned
status/blocked
status/needs-decision
status/needs-info
status/needs-verification
testing
upstream
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
sixfold-space/madtea#637
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
This is a second force path that ADR 0029 does not allow, next to #635. It needs no
forceflag and no lease.madt_push branch=andmadtea push <branch>pass the branch value togit push -u origin <branch>. In a git refspec, a leading+forces the update. The git-push manual says a refspec is "an optional plus +, followed by the source object", and that a non-fast-forward "update can be overridden by adding ... the optional leading + to a refspec". git also accepts+fooas a branch name:git check-ref-format --branch '+foo'exits 0.The comment at
internal/git/push.go:14-18gives "a definite --force-class abuse" as one reason for the leading-dash check. A leading+is the same abuse in refspec form, and nothing checks for it.The path, read from the code
internal/mcp/tools_localgit.go:650passesinput.Branchunchanged tosvcgit.Push.internal/service/git/push.go:125callsValidateRef.internal/git/validate.go:20rejects only a leading-, so+mainpasses.internal/service/git/push.go:134and:140) read the commits on+main. That ref does not resolve, so the read fails. Both guards pass on a read error (internal/service/git/identity_guard.go:76-86,internal/service/git/signing_guard.go:115-117). The identity guard comment assumes that "the push that follows fails on its own". With a leading+, the push does not fail.internal/git/push.go:22runsgit push -u origin +main. git reads that as a forced push of the localmain.So
madt_push branch="+main"force-pushesmain, withforceunset. Forge branch protection can refuse a force push to a protected branch. An unprotected branch has no such guard.Reconcile also reaches
GitPushDir:realGitOps.Push(internal/service/orchestrate/types.go:205) pushes the PR head ref that comes from the forge. I have not traced that path further.I have not reproduced this end to end yet. Each step above comes from the code and the git manual.
Acceptance
git pushrefuses a branch value with a leading+before git runs.