block / block/buzz

Git smart-HTTP: unauthenticated 0000 probe is only admitted on receive-pack — fetch with large negotiation (in-limit repo + divergent local history) still 401-aborts

Open
#4,423 3 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
32.7k
Forks
4.3k
Avg merge
1d 13h
Merged PRs (30d)
253

Description

## Summary

PR #4401 admits Git's unauthenticated 4-byte `0000` compatibility probe on the **receive-pack** route, fixing large pushes (#2880). But the probe is not receive-pack-specific: in Git's `remote-curl.c` (read at tag v2.50.1), `probe_rpc` (:868) is called inside `post_rpc` (:943) for *any* request that exceeds `http.postBuffer`, and `post_rpc` serves **both** services — `fetch_git` → `rpc_service` → `post_rpc` (:1245) and `push` → `rpc_service` (:1406), plus `stateless_connect` (:1458) for protocol v2.

So the identical 401-abort exists on **clone/fetch** when the upload-pack negotiation body crosses `http.postBuffer`, and #4401's middleware — mounted on the receive-pack route only — does not cover it.

## Measured reproduction (not just a code-reading claim)

Sami measured this against real git 2.51.0 with a local `git-http-backend` and a 401 injected on exactly the unauthenticated 4-byte POST (simulating `GitAuth`), with a fake credential helper advertising `authtype=Nostr` (mirroring `git-credential-nostr`'s non-curl-native scheme):

```text
# 25,000-ref repo, DEFAULT config — no http.postBuffer override
REQ POST /big.git/git-upload-pack CT=...x-git-upload-pack-request CL=4 body=0000
REQ POST /big.git/git-upload-pack CT=...x-git-upload-pack-request TE=chunked bodylen=1250124

# with 401 injected on the probe:
REQ POST /big.git/git-upload-pack -> 401 CHALLENGE (probe, body=b'0000')
REQ POST /big.git/git-upload-pack -> 401 CHALLENGE (probe, body=b'0000')
error: RPC failed; HTTP 401 curl 22
```

Negative control: same 401-injecting server, 1,500-ref repo → `clone rc=0`, zero probes. 16,000 refs did **not** trigger it; 25,000 did. The boundary matches the arithmetic: negotiation pkt-lines run ~50 bytes/ref, so 1 MiB (default `http.postBuffer`) ÷ ~50 ≈ 20k refs.

Both probe retries arrived unauthenticated — behavioral confirmation that `probe_rpc` never calls `http_append_auth_header` and the REAUTH loop cannot help a custom authtype.

## Reachability — **corrected 2026-08-02, see second comment below**

~~Low-likelihood today: the read-path trigger is ref-count driven (O(20k) refs)~~ **This framing was incomplete.** Ref count is the right model for **clone** (uniform `want` lines, measured at exactly 50.00 bytes/ref across 1.5k–25k refs plus a constant 124-byte header, so 1 MiB ÷ 50 ≈ 21k refs) — and Buzz's `MAX_MANIFEST_REFS = 10_000` (`manifest.rs:47`, enforced at `cas_publish.rs:329`) keeps clone permanently under the threshold at ~0.48 MiB worst case (0.24 MiB measured on a real-OID fixture). **Fetch is the reachable case** because `have` lines come from the *client's local* commits, adding a term no server-side cap bounds — and `http.postBuffer` gates total *decoded* negotiation bytes. Measured at the 10,000-ref ceiling: a fetch with divergent local history crosses 1 MiB decoded (1.46 MiB measured) and hits the 401 probe abort; clone of the same repo stays at 0.24 MiB and never probes.

So the trigger is not "a repo too big for Buzz to host" — it is **an in-limit repo plus a developer with substantial unpushed local work.** Live today. Note the wire bytes are gzipped (~450 KB); only decoded lengths reveal the crossing. Still correctly not held against #4401, but this is a real user-facing failure mode, not a corner case.

## Fix shape — **now measured end-to-end, see first comment below**

Small: the #4401 middleware's predicate is keyed on the receive-pack MIME (`application/x-git-receive-pack-request`, `Content-Length: 4`, body `0000`). Covering upload-pack is a second mount on the upload-pack POST route with the matching content type (`application/x-git-upload-pack-request`) — no new logic. Same security shape: path-independent empty 200, no tenant resolution, no state.

This was originally filed as a *proposal*; it has since been verified against real git 2.51.0 with a full `GitAuth` simulation (every git route 401s without `Authorization`): admitting the upload-pack probe makes the 25,000-ref clone succeed with `fsck` clean and exact ref parity with origin. One implementation detail pinned by that measurement: `probe_rpc` only checks the response status code, so the response content-type is not load-bearing — the implementation should still emit the matching MIME per service, and a single parameterized middleware covering both mounts prevents anyone getting half-working behavior by picking wrong. Full receipts in the comment below.

## Credit

Found and measured by Sami during the #4401 review (buzz-outside-pr-reviews thread, 2026-08-02). Call graph independently re-verified against `remote-curl.c` v2.50.1 by Eva.

Refs: #4401, #2880.

Contributor guide

Open the contributing guide

Research direction

Start by reading the receive-pack middleware added for #4401 and locate the upload-pack POST route. Compare the existing probe predicate with the upload-pack content type, then reproduce the large-negotiation fetch case described here. Done means authenticated fetches no longer abort on the unauthenticated 0000 probe while the existing receive-pack behavior remains intact.

Written by the indexing model from the issue text.

Assessment

Tech stack
git, rust
Domain
authentication, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.