diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 5a9e546d56..615b1a6ae1 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -71,7 +71,8 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error } func pruneBrokenReferences(ctx context.Context, m *repo_model.Mirror, gitRepo gitrepo.Repository, timeout time.Duration) error { - cmd := gitcmd.NewCommand("remote", "prune").AddDynamicArguments(m.GetRemoteName()).WithTimeout(timeout) + // Never follow HTTP redirects, see cmdFetch in runSync. + cmd := gitcmd.NewCommand("remote", "prune").AddConfig("http.followRedirects", "false").AddDynamicArguments(m.GetRemoteName()).WithTimeout(timeout) stdout, _, pruneErr := gitrepo.RunCmdString(ctx, gitRepo, cmd) if pruneErr != nil { // sanitize the output, since it may contain the remote address, which may contain a password @@ -119,7 +120,9 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*repo_module.SyncResu // use fetch but not remote update because git fetch support --tags but remote update doesn't cmdFetch := func() *gitcmd.Command { - cmd := gitcmd.NewCommand("fetch", "--tags") + // Never follow HTTP redirects: a mirror remote that later starts redirecting to an + // otherwise-blocked address would be an SSRF/exfiltration vector on scheduled syncs. + cmd := gitcmd.NewCommand("fetch", "--tags").AddConfig("http.followRedirects", "false") if m.EnablePrune { cmd.AddArguments("--prune") } @@ -200,7 +203,8 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*repo_module.SyncResu } cmdRemoteUpdatePrune := func() *gitcmd.Command { - return gitcmd.NewCommand("remote", "update", "--prune"). + // Never follow HTTP redirects, see cmdFetch above. + return gitcmd.NewCommand("remote", "update", "--prune").AddConfig("http.followRedirects", "false"). AddDynamicArguments(m.GetRemoteName()).WithTimeout(timeout).WithEnv(envs) }