Closes #12.
What was wrong
cloneBase and lfsObjectBase built URLs from r.Host and believed
X-Forwarded-Proto from anyone. A clone URL is copied out of a page and pasted
into a terminal, and an LFS batch href is followed by a client, so the Host
header chose where somebody else’s download went. The CSP still allowed inline
styles, there was no object-src, no Permissions-Policy, COOP or CORP, and the
pages that shell out to git (search, compare, blame, history, archives) had no
concurrency bound or deadline. Plain tar skipped the archive slot, and site links
always used http://.
What this does
Canonical links. canonicalBase uses public_url when set. Without it the
request origin is the only information there is, and forwarded headers are honoured
only when git_http_client_ip_header is configured — that setting is the
instance’s statement that a proxy it trusts rewrites them. A forged Host or
X-Forwarded-Proto therefore cannot change a generated link on a configured
instance. Applies to clone URLs, LFS batch hrefs and site URLs; site links now
follow the forge’s scheme.
Headers. CSP: object-src 'none' and style-src 'self' (no template or
generated markup contains an inline style, so 'unsafe-inline' was unused).
Added Permissions-Policy, Cross-Origin-Opener-Policy: same-origin,
Cross-Origin-Resource-Policy: same-origin.
Second CSRF lock. Origin/Referer must be this instance when the browser
sends one, on form posts and on the JSON ceremony endpoints. A missing header is
not evidence against a request; one that names another origin is refused even with
a valid token.
Bounded git. A pool of 8 for search, compare, blame, file history, commit
history and archives, with a 30 s deadline attached to the git.Repo so the
process is killed (WithContext in core/git, via exec.CommandContext). A
page waits up to 5 s for a slot, then answers 503 with Retry-After. Both archive
formats take the slot now.
Notes
core/gitgainedWithContext; callers that do not set one behave exactly as before, so hooks, the lander and the runner are unaffected.public_urlbeing unset still falls back to the request origin, because a clone URL cannot be relative and there is no other information. That is documented as a loopback-preview arrangement; the acceptance criterion (host injection cannot change generated URLs) holds whenever the instance is configured, which is every deployment that is not a local preview.Cross-Origin-Embedder-Policyis deliberately not set: it requires CORP on every subresource and would break thedata:images and the confetti canvas for no gain here. COOP/CORP are what the issue asked for.- The CSP change was checked against the templates and the static JS: none of them
writes a
styleattribute or a<style>element; the celebration script uses CSSOM (canvas.style...), whichstyle-srcdoes not govern.
Verification
gofmt,go vet ./...,go test -race ./...clean../vole workflow checkand./vole workflow run verifypass.
Tests
TestCloneURLPrefersTheConfiguredOrigin(forged Host and forwarded headers lose),TestCloneURLFallsBackToTheRequestOrigin(forwarded headers only behind a declared proxy).TestLFSObjectBasePrefersTheConfiguredOrigin,TestLFSObjectBaseHonoursForwardedHeadersOnlyBehindAProxy.TestSiteURLFollowsTheForgeScheme.TestSecurityHeadersCloseTheSurface(nounsafe-inline,object-src, COOP/CORP, Permissions-Policy).TestGitSlotRefusesWhenSaturated(503 +Retry-After, slot reusable after release).TestCrossOriginPostIsRefused(end to end: 403 with a valid token, session kept) and the foreign-origin case inTestJSONEndpointsRequireTheCSRFHeader.TestSameOriginRequest(no header, same origin, foreign origin, referrer,Origin: null).
Docs: docs/configuration.md (public_url decides generated links; forwarded
headers need the proxy setting), docs/sites.md (same-site risk of sharing a
registrable domain with the forge).