git-gate: rebase/force-push scans main history ($old..$new drags in fixture secrets), rejecting clean pushes #421

Closed
opened 2026-07-18 16:44:59 -04:00 by didericis-claude · 0 comments
Collaborator

Summary

git-gate's pre-receive hook scans an existing-branch update with the delta range $old..$new. For a rebase / non-fast-forward force-push onto a branch whose base (main) has advanced, $old (the old branch tip) is no longer an ancestor of $new, so $old..$new resolves to every commit reachable from the new tip but not from the old tip — which pulls in all of main's new history, including the deliberate sandbox-escape gitleaks fixtures (e.g. 15f0e0b). gitleaks then trips on those historical fixtures and the push is rejected, even though the push itself introduces no new secret.

This is the exact failure mode that PRD 0028 fixed for new branches (#106) via $new --not --all, but the existing-branch/force-push path was left on $old..$new — called out as a deferred open question in PRD 0028:

Unify both branches on --not --all? It's also more robust than $old..$new for non-fast-forward/force pushes (which can skip commits off the direct path)…

Repro

  1. Branch off an older main, push through the gate (succeeds).
  2. main advances upstream to include a commit containing a gitleaks-shaped fixture.
  3. Rebase the branch onto the new main and git push --force-with-lease.
  4. Gate rejects: gitleaks rejected push on fixture commits that belong to main, not to the branch.

Fix

Unify the scan range on $new --not --all for every non-delete push (new refs and updates alike). This scans only the commits the push actually introduces — those not reachable from any ref the gate already holds — and is security-equivalent per PRD 0028's analysis: the bare repo's refs are populated only by trusted upstream mirror-fetch and gitleaks-gated pushes, so an excluded commit is already-upstream or already-scanned and cannot smuggle a new secret. It is also strictly more correct for non-fast-forward pushes, where $old..$new can miss commits off the direct path.

Notes

Distinct from #346 (matching the bottle's injected known secrets vs. gitleaks patterns) — this is purely about the commit range scanned. Related: #106, PRD 0028.

## Summary git-gate's pre-receive hook scans an **existing-branch** update with the delta range `$old..$new`. For a **rebase / non-fast-forward force-push** onto a branch whose base (`main`) has advanced, `$old` (the old branch tip) is no longer an ancestor of `$new`, so `$old..$new` resolves to *every commit reachable from the new tip but not from the old tip* — which pulls in **all of main's new history**, including the deliberate sandbox-escape gitleaks fixtures (e.g. `15f0e0b`). gitleaks then trips on those historical fixtures and the push is rejected, even though the push itself introduces no new secret. This is the exact failure mode that PRD 0028 fixed for *new* branches (#106) via `$new --not --all`, but the existing-branch/force-push path was left on `$old..$new` — called out as a deferred open question in PRD 0028: > **Unify both branches on `--not --all`?** It's also more robust than `$old..$new` for non-fast-forward/force pushes (which can skip commits off the direct path)… ## Repro 1. Branch off an older `main`, push through the gate (succeeds). 2. `main` advances upstream to include a commit containing a gitleaks-shaped fixture. 3. Rebase the branch onto the new `main` and `git push --force-with-lease`. 4. Gate rejects: `gitleaks rejected push` on fixture commits that belong to `main`, not to the branch. ## Fix Unify the scan range on `$new --not --all` for every non-delete push (new refs and updates alike). This scans only the commits the push actually introduces — those not reachable from any ref the gate already holds — and is security-equivalent per PRD 0028's analysis: the bare repo's refs are populated only by trusted upstream mirror-fetch and gitleaks-gated pushes, so an excluded commit is already-upstream or already-scanned and cannot smuggle a new secret. It is also strictly more correct for non-fast-forward pushes, where `$old..$new` can miss commits off the direct path. ## Notes Distinct from #346 (matching the bottle's injected known secrets vs. gitleaks patterns) — this is purely about the commit *range* scanned. Related: #106, PRD 0028.
didericis-claude added the Kind/Bug label 2026-07-18 16:44:59 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: didericis/bot-bottle#421