0
0
mirror of https://github.com/go-gitea/gitea.git synced 2026-05-11 15:44:52 +02:00

Add check redirect for migrations

This commit is contained in:
Lunny Xiao 2026-03-02 15:36:12 -08:00
parent 56f23f623a
commit 4ac42f2b2f
No known key found for this signature in database
GPG Key ID: C3B7C91B632F738A
11 changed files with 79 additions and 11 deletions

View File

@ -24,9 +24,9 @@ type Client interface {
} }
// NewClient creates a LFS client // NewClient creates a LFS client
func NewClient(endpoint *url.URL, httpTransport *http.Transport) Client { func NewClient(endpoint *url.URL, httpTransport *http.Transport, checkRedirect func(req *http.Request, via []*http.Request) error) Client {
if endpoint.Scheme == "file" { if endpoint.Scheme == "file" {
return newFilesystemClient(endpoint) return newFilesystemClient(endpoint)
} }
return newHTTPClient(endpoint, httpTransport) return newHTTPClient(endpoint, httpTransport, checkRedirect)
} }

View File

@ -34,7 +34,7 @@ func (c *HTTPClient) BatchSize() int {
return setting.LFSClient.BatchSize return setting.LFSClient.BatchSize
} }
func newHTTPClient(endpoint *url.URL, httpTransport *http.Transport) *HTTPClient { func newHTTPClient(endpoint *url.URL, httpTransport *http.Transport, checkRedirect func(req *http.Request, via []*http.Request) error) *HTTPClient {
if httpTransport == nil { if httpTransport == nil {
httpTransport = &http.Transport{ httpTransport = &http.Transport{
Proxy: proxy.Proxy(), Proxy: proxy.Proxy(),
@ -42,7 +42,8 @@ func newHTTPClient(endpoint *url.URL, httpTransport *http.Transport) *HTTPClient
} }
hc := &http.Client{ hc := &http.Client{
Transport: httpTransport, Transport: httpTransport,
CheckRedirect: checkRedirect,
} }
basic := &BasicTransferAdapter{hc} basic := &BasicTransferAdapter{hc}

View File

@ -129,7 +129,7 @@ func (g *GiteaLocalUploader) CreateRepo(ctx context.Context, repo *base.Reposito
Wiki: opts.Wiki, Wiki: opts.Wiki,
Releases: opts.Releases, // if didn't get releases, then sync them from tags Releases: opts.Releases, // if didn't get releases, then sync them from tags
MirrorInterval: opts.MirrorInterval, MirrorInterval: opts.MirrorInterval,
}, NewMigrationHTTPTransport()) }, NewMigrationHTTPTransport(), CheckMigrateRedirect)
g.sameApp = strings.HasPrefix(repo.OriginalURL, setting.AppURL) g.sameApp = strings.HasPrefix(repo.OriginalURL, setting.AppURL)
g.repo = r g.repo = r

View File

@ -100,6 +100,7 @@ func NewGithubDownloaderV3(_ context.Context, baseURL, userName, password, token
Base: NewMigrationHTTPTransport(), Base: NewMigrationHTTPTransport(),
Source: oauth2.ReuseTokenSource(nil, ts), Source: oauth2.ReuseTokenSource(nil, ts),
}, },
CheckRedirect: CheckMigrateRedirect,
} }
downloader.addClient(client, baseURL) downloader.addClient(client, baseURL)
@ -111,7 +112,8 @@ func NewGithubDownloaderV3(_ context.Context, baseURL, userName, password, token
return proxy.Proxy()(req) return proxy.Proxy()(req)
} }
client := &http.Client{ client := &http.Client{
Transport: transport, Transport: transport,
CheckRedirect: CheckMigrateRedirect,
} }
downloader.addClient(client, baseURL) downloader.addClient(client, baseURL)
} }

View File

@ -113,6 +113,7 @@ func (g *GogsDownloader) client(ctx context.Context) *gogs.Client {
} }
return httpTransport.RoundTrip(req.WithContext(ctx)) return httpTransport.RoundTrip(req.WithContext(ctx))
}), }),
CheckRedirect: CheckMigrateRedirect,
}) })
return gogsClient return gogsClient
} }

View File

@ -5,8 +5,10 @@ package migrations
import ( import (
"crypto/tls" "crypto/tls"
"net"
"net/http" "net/http"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/hostmatcher" "code.gitea.io/gitea/modules/hostmatcher"
"code.gitea.io/gitea/modules/proxy" "code.gitea.io/gitea/modules/proxy"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
@ -15,10 +17,27 @@ import (
// NewMigrationHTTPClient returns a HTTP client for migration // NewMigrationHTTPClient returns a HTTP client for migration
func NewMigrationHTTPClient() *http.Client { func NewMigrationHTTPClient() *http.Client {
return &http.Client{ return &http.Client{
Transport: NewMigrationHTTPTransport(), Transport: NewMigrationHTTPTransport(),
CheckRedirect: CheckMigrateRedirect,
} }
} }
func CheckMigrateRedirect(req *http.Request, via []*http.Request) error {
redirectURL := req.URL
if redirectURL == nil {
return &git.ErrInvalidCloneAddr{IsURLError: true, Host: "<EMPTY_REDIRECT_URL>"}
}
if redirectURL.Scheme != "http" && redirectURL.Scheme != "https" {
return &git.ErrInvalidCloneAddr{Host: redirectURL.Host, IsProtocolInvalid: true, IsPermissionDenied: true, IsURLError: true}
}
hostName := redirectURL.Hostname()
if hostName == "" {
return &git.ErrInvalidCloneAddr{IsURLError: true, Host: redirectURL.String()}
}
addrList, _ := net.LookupIP(hostName)
return checkByAllowBlockList(hostName, addrList)
}
// NewMigrationHTTPTransport returns a HTTP transport for migration // NewMigrationHTTPTransport returns a HTTP transport for migration
func NewMigrationHTTPTransport() *http.Transport { func NewMigrationHTTPTransport() *http.Transport {
return &http.Transport{ return &http.Transport{

View File

@ -5,12 +5,16 @@ package migrations
import ( import (
"net" "net"
"net/http"
"net/url"
"path/filepath" "path/filepath"
"testing" "testing"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -113,3 +117,43 @@ func TestAllowBlockList(t *testing.T) {
// reset // reset
init("", "", false) init("", "", false)
} }
func TestCheckMigrateRedirect(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
defer test.MockVariableValue(&setting.Migrations.AllowedDomains, "")()
defer test.MockVariableValue(&setting.Migrations.BlockedDomains, "")()
defer test.MockVariableValue(&setting.Migrations.AllowLocalNetworks, false)()
assert.NoError(t, Init())
err := CheckMigrateRedirect(&http.Request{URL: &url.URL{Scheme: "https", Host: "1.2.3.4"}}, nil)
assert.NoError(t, err)
err = CheckMigrateRedirect(&http.Request{URL: &url.URL{Scheme: "https", Host: "127.0.0.1"}}, nil)
assert.Error(t, err)
var addrErr *git.ErrInvalidCloneAddr
assert.ErrorAs(t, err, &addrErr)
assert.True(t, addrErr.IsPermissionDenied)
err = CheckMigrateRedirect(&http.Request{URL: nil}, nil)
assert.Error(t, err)
assert.ErrorAs(t, err, &addrErr)
assert.True(t, addrErr.IsURLError)
err = CheckMigrateRedirect(&http.Request{URL: &url.URL{Scheme: "file", Host: "example.com"}}, nil)
assert.Error(t, err)
assert.ErrorAs(t, err, &addrErr)
assert.True(t, addrErr.IsProtocolInvalid)
assert.True(t, addrErr.IsPermissionDenied)
err = CheckMigrateRedirect(&http.Request{URL: &url.URL{Scheme: "https"}}, nil)
assert.Error(t, err)
assert.ErrorAs(t, err, &addrErr)
assert.True(t, addrErr.IsURLError)
setting.Migrations.AllowLocalNetworks = true
assert.NoError(t, Init())
err = CheckMigrateRedirect(&http.Request{URL: &url.URL{Scheme: "https", Host: "127.0.0.1"}}, nil)
assert.NoError(t, err)
}

View File

@ -90,6 +90,7 @@ func NewOneDevDownloader(ctx context.Context, baseURL *url.URL, username, passwo
} }
return httpTransport.RoundTrip(req.WithContext(ctx)) return httpTransport.RoundTrip(req.WithContext(ctx))
}), }),
CheckRedirect: CheckMigrateRedirect,
}, },
userMap: make(map[int64]*onedevUser), userMap: make(map[int64]*onedevUser),
milestoneMap: make(map[int64]string), milestoneMap: make(map[int64]string),

View File

@ -173,7 +173,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*repo_module.SyncResu
if m.LFS && setting.LFS.StartServer { if m.LFS && setting.LFS.StartServer {
log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo) log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo)
endpoint := lfs.DetermineEndpoint(remoteURL.String(), m.LFSEndpoint) endpoint := lfs.DetermineEndpoint(remoteURL.String(), m.LFSEndpoint)
lfsClient := lfs.NewClient(endpoint, migrations.NewMigrationHTTPTransport()) lfsClient := lfs.NewClient(endpoint, migrations.NewMigrationHTTPTransport(), migrations.CheckMigrateRedirect)
if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, m.Repo, gitRepo, lfsClient); err != nil { if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, m.Repo, gitRepo, lfsClient); err != nil {
log.Error("SyncMirrors [repo: %-v]: failed to synchronize LFS objects for repository: %v", m.Repo.FullName(), err) log.Error("SyncMirrors [repo: %-v]: failed to synchronize LFS objects for repository: %v", m.Repo.FullName(), err)
} }

View File

@ -145,7 +145,7 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error {
defer gitRepo.Close() defer gitRepo.Close()
endpoint := lfs.DetermineEndpoint(remoteURL.String(), "") endpoint := lfs.DetermineEndpoint(remoteURL.String(), "")
lfsClient := lfs.NewClient(endpoint, migrations.NewMigrationHTTPTransport()) lfsClient := lfs.NewClient(endpoint, migrations.NewMigrationHTTPTransport(), migrations.CheckMigrateRedirect)
if err := pushAllLFSObjects(ctx, gitRepo, lfsClient); err != nil { if err := pushAllLFSObjects(ctx, gitRepo, lfsClient); err != nil {
return util.SanitizeErrorCredentialURLs(err) return util.SanitizeErrorCredentialURLs(err)
} }

View File

@ -72,7 +72,7 @@ func cloneWiki(ctx context.Context, repo *repo_model.Repository, opts migration.
// MigrateRepositoryGitData starts migrating git related data after created migrating repository // MigrateRepositoryGitData starts migrating git related data after created migrating repository
func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
repo *repo_model.Repository, opts migration.MigrateOptions, repo *repo_model.Repository, opts migration.MigrateOptions,
httpTransport *http.Transport, httpTransport *http.Transport, checkRedirect func(req *http.Request, via []*http.Request) error,
) (*repo_model.Repository, error) { ) (*repo_model.Repository, error) {
if u.IsOrganization() { if u.IsOrganization() {
t, err := organization.OrgFromUser(u).GetOwnerTeam(ctx) t, err := organization.OrgFromUser(u).GetOwnerTeam(ctx)
@ -160,7 +160,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
if opts.LFS { if opts.LFS {
endpoint := lfs.DetermineEndpoint(opts.CloneAddr, opts.LFSEndpoint) endpoint := lfs.DetermineEndpoint(opts.CloneAddr, opts.LFSEndpoint)
lfsClient := lfs.NewClient(endpoint, httpTransport) lfsClient := lfs.NewClient(endpoint, httpTransport, checkRedirect)
if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, repo, gitRepo, lfsClient); err != nil { if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, repo, gitRepo, lfsClient); err != nil {
log.Error("Failed to store missing LFS objects for repository: %v", err) log.Error("Failed to store missing LFS objects for repository: %v", err)
return repo, fmt.Errorf("StoreMissingLfsObjectsInRepository: %w", err) return repo, fmt.Errorf("StoreMissingLfsObjectsInRepository: %w", err)