fix(git-gate): scan $new --not --all for every push, not $old..$new #422

Merged
didericis merged 1 commits from fix-git-gate-force-push-scan-range into main 2026-07-18 16:51:34 -04:00
Collaborator

Problem

git-gate's pre-receive hook scanned existing-branch updates with $old..$new. On a rebase / non-fast-forward force-push onto a branch whose base main has advanced, $old (the old tip) is no longer an ancestor of $new, so $old..$new expands to all of main's new history — including the deliberate sandbox-escape gitleaks fixtures (15f0e0b). gitleaks trips on those and rejects a push that introduced no new secret.

Fix

Unify the scan range on $new --not --all for every non-delete push — the deferred open question from PRD 0028, which already applied it to new refs for #106. It scans only the commits the push actually introduces (not reachable from any ref the gate already holds) and is security-equivalent per PRD 0028's analysis: the bare repo's refs come only from trusted upstream mirror-fetch and gitleaks-gated pushes, so an excluded commit is already-upstream or already-scanned. It's also more correct for non-fast-forward pushes, where $old..$new can skip commits off the direct path.

Tests

Renamed/rewrote the scan-scope unit test in tests/unit/test_git_gate.py to assert the unified range and that neither log_opts="$new" (full ancestry) nor log_opts="$old..$new" (ancestry-blind delta) survives. Full git-gate suite green (37 tests).

Deployment note

The running gateway in the infra firecracker VM was live-patched with the rendered hook (canonical /etc/git-gate/pre-receive + all bare repos' hooks/pre-receive) so in-flight bottles are unblocked immediately; git receive-pack re-reads the on-disk hook per push, so no gateway restart was needed. Future bottle launches render this fixed hook from source.

Fixes #421

🤖 Generated with Claude Code

https://claude.ai/code/session_01S1qRZTJC6qgBsUSjNrBdkX

## Problem git-gate's pre-receive hook scanned existing-branch updates with `$old..$new`. On a rebase / non-fast-forward force-push onto a branch whose base `main` has advanced, `$old` (the old tip) is no longer an ancestor of `$new`, so `$old..$new` expands to **all of main's new history** — including the deliberate sandbox-escape gitleaks fixtures (`15f0e0b`). gitleaks trips on those and rejects a push that introduced no new secret. ## Fix Unify the scan range on `$new --not --all` for every non-delete push — the deferred open question from **PRD 0028**, which already applied it to *new* refs for #106. It scans only the commits the push actually introduces (not reachable from any ref the gate already holds) and is security-equivalent per PRD 0028's analysis: the bare repo's refs come only from trusted upstream mirror-fetch and gitleaks-gated pushes, so an excluded commit is already-upstream or already-scanned. It's also more correct for non-fast-forward pushes, where `$old..$new` can skip commits off the direct path. ## Tests Renamed/rewrote the scan-scope unit test in `tests/unit/test_git_gate.py` to assert the unified range and that neither `log_opts="$new"` (full ancestry) nor `log_opts="$old..$new"` (ancestry-blind delta) survives. Full git-gate suite green (37 tests). ## Deployment note The running gateway in the infra firecracker VM was live-patched with the rendered hook (canonical `/etc/git-gate/pre-receive` + all bare repos' `hooks/pre-receive`) so in-flight bottles are unblocked immediately; `git receive-pack` re-reads the on-disk hook per push, so no gateway restart was needed. Future bottle launches render this fixed hook from source. Fixes #421 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01S1qRZTJC6qgBsUSjNrBdkX
didericis-claude added 1 commit 2026-07-18 16:45:38 -04:00
fix(git-gate): scan $new --not --all for every push, not $old..$new
test / integration (pull_request) Successful in 10s
tracker-policy-pr / check-pr (pull_request) Successful in 7s
lint / lint (push) Successful in 42s
test / unit (pull_request) Successful in 33s
test / coverage (pull_request) Successful in 39s
bcc0bb3c41
The pre-receive hook scanned existing-branch updates with the delta range
$old..$new. On a rebase / non-fast-forward force-push onto an advanced main,
$old is no longer an ancestor of $new, so $old..$new expands to all of main's
new history — including the deliberate sandbox-escape gitleaks fixtures — and
the push is rejected on commits that belong to main, not the branch.

Unify the range on `$new --not --all` for every non-delete push (this is the
deferred open question from PRD 0028, which already applied it to new refs
for #106). It scans only the commits the push introduces and is
security-equivalent: the bare repo's refs come only from trusted upstream
mirror-fetch and gitleaks-gated pushes, so an excluded commit is
already-upstream or already-scanned. It is also more correct for
non-fast-forward pushes, where $old..$new can skip commits off the direct path.

Fixes #421

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1qRZTJC6qgBsUSjNrBdkX
didericis merged commit 5b359fe8d2 into main 2026-07-18 16:51:34 -04:00
didericis deleted branch fix-git-gate-force-push-scan-range 2026-07-18 16:51:36 -04:00
Sign in to join this conversation.