{{ message }}
plumbing: transport/http, harden redirect handling to match canonical git - #1997
Merged
Conversation
aymanbagabas
force-pushed
the
http-hardening
branch
2 times, most recently
from
April 15, 2026 02:05
37bba5a to
86775cd
Compare
pjbgf
added a commit
that referenced
this pull request
Apr 16, 2026
Back-port from #1997. Signed-off-by: Paulo Gomes <paulo@entire.io>
This commit adds security checks to the HTTP transport's redirect handling to prevent SSRF and credential leakage. The applyRedirect function now validates the redirect target's scheme and path, and the checkRedirect policy allows redirects only on the initial /info/refs request. Tests are added to verify that non-initial redirects are blocked, credentials are stripped on cross-host redirects, and unsupported schemes are rejected. This hardening aligns with canonical git's behavior and mitigates risks from malicious servers during repository discovery and fetch operations.
Signed-off-by: Paulo Gomes <paulo@entire.io> Entire-Checkpoint: 2b7603909172
Contributor
There was a problem hiding this comment.
Pull request overview
Hardens the HTTP transport’s redirect behavior to align with canonical git’s http.followRedirects = initial default, reducing redirect-based credential/SSRF risks during smart/dumb HTTP discovery and subsequent RPC calls.
Changes:
- Introduces a redirect policy (
FollowRedirects) and enforces it viahttp.Client.CheckRedirect(default: only follow redirects on the initial/info/refsrequest). - Tightens
applyRedirectto validate redirect targets and return errors for inconsistent redirect URLs, and clears session credentials on cross-host redirects. - Adds unit + integration tests covering redirect policy enforcement, redirected POST blocking, and credential stripping.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
aymanbagabas
commented
Apr 16, 2026
pjbgf
added a commit
that referenced
this pull request
Apr 16, 2026
Back-port from #1997. Signed-off-by: Paulo Gomes <paulo@entire.io>
pjbgf
added a commit
that referenced
this pull request
Apr 16, 2026
Back-port from #1997. Signed-off-by: Paulo Gomes <paulo@entire.io>
…th upstream Expands the previous logic that had 'initial' as the hard-coded logic. This enables users to opt-in into the other two options that Git supports: false (blind block) and true (blind allow). Signed-off-by: Paulo Gomes <paulo@entire.io>
pjbgf
approved these changes
Apr 16, 2026
pjbgf
added a commit
that referenced
this pull request
Apr 16, 2026
Back-port from #1997. Signed-off-by: Paulo Gomes <paulo@entire.io>
Merged
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
The HTTP transport's redirect handling had several security gaps compared to canonical git's default
http.followRedirects = initialpolicy. This PR closes them.Problem
When go-git's HTTP transport follows server redirects during
Handshake(theGET /info/refsdiscovery request), several things could go wrong:No redirect policy — Go's default
http.Clientfollows redirects on every request (GET, POST, etc.). A malicious server could redirect a pack-data POST to an attacker-controlled host and the client would silently follow it. Canonical git only follows redirects on the initial/info/refsrequest and treats 3xx on subsequent requests as errors.No protocol/scheme restriction — A redirect to
file://,gopher://, or any other scheme was accepted. This is an SSRF vector in server-side environments. Canonical git restricts redirect targets tohttp/https/ftp/ftpsviaCURLOPT_REDIR_PROTOCOLS.Silent fallback on path mismatch — If the redirect target's path didn't end with
/info/refs,applyRedirectsilently returned the original base URL instead of erroring. Canonical git'supdate_url_from_redirect()callsdie()here because a tail mismatch could indicate a malicious redirect attempting to rewrite the base URL to an unrelated repository.Credential leakage to redirect target — Both the URL-embedded
Userinfocredentials and theAuthorizercallback were stored in the session and re-applied on every subsequent POST. After a cross-host redirect, the original host's credentials would be sent to the new host. Canonical git re-derives credentials from the new URL viacredential_from_url(), effectively wiping the old ones.Changes
common.go—applyRedirectnow returns(*url.URL, error). Validates the redirect target's scheme ishttporhttps. Returns a hard error when the redirected path doesn't end with/info/refs(matching canonical git'sdie()behavior).http.go— Adds aCheckRedirectpolicy (checkRedirect) implementing canonical git'sinitialmode: only the/info/refsGET (tagged via context key) is allowed to follow redirects. Non-initial requests, unsupported schemes, and >10 hops are rejected. Go's stdlib already stripsAuthorizationon cross-host redirects and preserves it for same-host redirects, so no manual header manipulation is needed.handshake.go— Tags the/info/refsrequest withwithInitialRequest(ctx). AfterapplyRedirect, clears bothURL.Userand theAuthorizercallback when the host changed — both are credential sources that would otherwise leak to the redirect target.Canonical git reference
http.c:130(HTTP_FOLLOW_INITIALdefault),http.c:1583-1591(CURLOPT_FOLLOWLOCATION per-request)http.c:991-1013(get_curl_allowed_protocols)http.c:2243-2293(update_url_from_redirect)http.c:2354-2360(credential_from_urlafter redirect)Test plan
TestCheckRedirectPolicy— unit tests for thecheckRedirectfunction: blocks non-initial requests, allows initial requests, blocks unsupported schemes, blocks >10 redirectsTestApplyRedirect— unit tests forapplyRedirect: no-op detection, host/scheme/path updates, scheme rejection, tail mismatch errorTestRedirectPostBlocked— integration test: server proxies GET but 307-redirects POST; verifies fetch fails with "non-initial request"TestRedirectStripsCredentials— integration test: cross-host redirect clearsURL.Userfrom the session's base URLTestRedirectPath,TestRedirectSchema,TestRedirectPathWithFetch— existing integration tests still pass (legitimate redirects during handshake continue to work)go test ./plumbing/transport/http/...passes