Summary
There are two distinct gaps that cause blocked fetch events to be missing from the Repos page metrics:
Gap 1 — Store-and-forward mode (/push/...) has no URL allowlist enforcement for fetches
registerGitServlet adds no UrlRuleAggregateFilter (or equivalent) to the push-path filter chain.
The only allowlist hook is RepositoryUrlRuleHook, which is wired inside StoreAndForwardReceivePackFactory and runs exclusively in the ReceivePack (push) lifecycle.
Fetches via /push/<provider>/... go through:
StoreAndForwardRepositoryResolver — clones/fetches from upstream unconditionally
StoreAndForwardUploadPackFactory — creates a bare UploadPack with no rule checks
Neither component consults the UrlRuleRegistry or calls FetchStore.record(), so:
- A repo that is not in the allow list can still be fetched via the store-and-forward path
- No blocked-fetch metric is ever written
Gap 2 — Transparent proxy mode (/proxy/...): /info/refs phase does not call recordFetch
UrlRuleAggregateFilter.applyInfoRefsRules() (the INFO branch in doHttpFilter) calls setResult() when a rule blocks the request, but never calls recordFetch().
git clone / git fetch always hit /info/refs?service=git-upload-pack first. If the repo is not in the allow list, the request is blocked at the discovery handshake and the upload-pack request never arrives — so the recordFetch(request, false) in the upload-pack branch is never reached. The net result is zero rows written to FetchStore for blocked fetches in the common clone/fetch flow.
Affected code
| Location |
Issue |
GitProxyServletRegistrar.registerGitServlet |
No UrlRuleAggregateFilter registered on the /push/ path for fetch operations; no FetchStore passed |
UrlRuleAggregateFilter.applyInfoRefsRules |
recordFetch(request, false) missing for Denied and NotAllowed cases |
RepositoryUrlRuleHook |
Only handles HttpOperation.PUSH; no fetch-path analogue exists |
Expected behaviour
- Fetches via
/push/... to a repo not in the allow list should be blocked with an appropriate git error (same as push behaviour).
- Every blocked fetch — regardless of whether it was rejected at the
/info/refs phase or the upload-pack phase, and regardless of proxy mode — should be recorded in FetchStore with Result.BLOCKED so the Repos page blocked-fetch count is accurate.
Suggested fix direction
Gap 1: Add a fetch-gate filter on the /push/ path (or extend UrlRuleAggregateFilter to cover it) that checks URL rules for FETCH/INFO operations and calls FetchStore.record() on block. Pass FetchStore through registerGitServlet to support this.
Gap 2: Add recordFetch(request, false) into both the Denied and NotAllowed cases inside applyInfoRefsRules in UrlRuleAggregateFilter.
Summary
There are two distinct gaps that cause blocked fetch events to be missing from the Repos page metrics:
Gap 1 — Store-and-forward mode (
/push/...) has no URL allowlist enforcement for fetchesregisterGitServletadds noUrlRuleAggregateFilter(or equivalent) to the push-path filter chain.The only allowlist hook is
RepositoryUrlRuleHook, which is wired insideStoreAndForwardReceivePackFactoryand runs exclusively in the ReceivePack (push) lifecycle.Fetches via
/push/<provider>/...go through:StoreAndForwardRepositoryResolver— clones/fetches from upstream unconditionallyStoreAndForwardUploadPackFactory— creates a bareUploadPackwith no rule checksNeither component consults the
UrlRuleRegistryor callsFetchStore.record(), so:Gap 2 — Transparent proxy mode (
/proxy/...):/info/refsphase does not callrecordFetchUrlRuleAggregateFilter.applyInfoRefsRules()(theINFObranch indoHttpFilter) callssetResult()when a rule blocks the request, but never callsrecordFetch().git clone/git fetchalways hit/info/refs?service=git-upload-packfirst. If the repo is not in the allow list, the request is blocked at the discovery handshake and the upload-pack request never arrives — so therecordFetch(request, false)in the upload-pack branch is never reached. The net result is zero rows written toFetchStorefor blocked fetches in the common clone/fetch flow.Affected code
GitProxyServletRegistrar.registerGitServletUrlRuleAggregateFilterregistered on the/push/path for fetch operations; noFetchStorepassedUrlRuleAggregateFilter.applyInfoRefsRulesrecordFetch(request, false)missing forDeniedandNotAllowedcasesRepositoryUrlRuleHookHttpOperation.PUSH; no fetch-path analogue existsExpected behaviour
/push/...to a repo not in the allow list should be blocked with an appropriate git error (same as push behaviour)./info/refsphase or the upload-pack phase, and regardless of proxy mode — should be recorded inFetchStorewithResult.BLOCKEDso the Repos page blocked-fetch count is accurate.Suggested fix direction
Gap 1: Add a fetch-gate filter on the
/push/path (or extendUrlRuleAggregateFilterto cover it) that checks URL rules forFETCH/INFOoperations and callsFetchStore.record()on block. PassFetchStorethroughregisterGitServletto support this.Gap 2: Add
recordFetch(request, false)into both theDeniedandNotAllowedcases insideapplyInfoRefsRulesinUrlRuleAggregateFilter.