0
0
mirror of https://github.com/go-gitea/gitea.git synced 2026-04-03 21:12:09 +02:00

Merge d909617bfdd046522f28d90c3938f2e89ce71cbd into 7b17234945ff3f7c6f09c54d9c4ffc93dc137212

This commit is contained in:
Lunny Xiao 2026-04-03 02:26:40 +00:00 committed by GitHub
commit edf6dfb717
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 83 additions and 15 deletions

View File

@ -24,9 +24,9 @@ type Client interface {
}
// 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" {
return newFilesystemClient(endpoint)
}
return newHTTPClient(endpoint, httpTransport)
return newHTTPClient(endpoint, httpTransport, checkRedirect)
}

View File

@ -12,10 +12,10 @@ import (
func TestNewClient(t *testing.T) {
u, _ := url.Parse("file:///test")
c := NewClient(u, nil)
c := NewClient(u, nil, nil)
assert.IsType(t, &FilesystemClient{}, c)
u, _ = url.Parse("https://test.com/lfs")
c = NewClient(u, nil)
c = NewClient(u, nil, nil)
assert.IsType(t, &HTTPClient{}, c)
}

View File

@ -34,7 +34,7 @@ func (c *HTTPClient) BatchSize() int {
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 {
httpTransport = &http.Transport{
Proxy: proxy.Proxy(),
@ -42,7 +42,8 @@ func newHTTPClient(endpoint *url.URL, httpTransport *http.Transport) *HTTPClient
}
hc := &http.Client{
Transport: httpTransport,
Transport: httpTransport,
CheckRedirect: checkRedirect,
}
basic := &BasicTransferAdapter{hc}

View File

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

View File

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

View File

@ -5,8 +5,10 @@ package migrations
import (
"crypto/tls"
"net"
"net/http"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/hostmatcher"
"code.gitea.io/gitea/modules/proxy"
"code.gitea.io/gitea/modules/setting"
@ -15,10 +17,27 @@ import (
// NewMigrationHTTPClient returns a HTTP client for migration
func NewMigrationHTTPClient() *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
func NewMigrationHTTPTransport() *http.Transport {
return &http.Transport{

View File

@ -5,12 +5,16 @@ package migrations
import (
"net"
"net/http"
"net/url"
"path/filepath"
"testing"
"code.gitea.io/gitea/models/unittest"
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/test"
"github.com/stretchr/testify/assert"
)
@ -113,3 +117,43 @@ func TestAllowBlockList(t *testing.T) {
// reset
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))
}),
CheckRedirect: CheckMigrateRedirect,
},
userMap: make(map[int64]*onedevUser),
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 {
log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo)
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 {
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()
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 {
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
func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
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) {
if u.IsOrganization() {
t, err := organization.OrgFromUser(u).GetOwnerTeam(ctx)
@ -160,7 +160,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
if opts.LFS {
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 {
log.Error("Failed to store missing LFS objects for repository: %v", err)
return repo, fmt.Errorf("StoreMissingLfsObjectsInRepository: %w", err)

View File

@ -162,7 +162,7 @@ func testScheduleUpdateMirrorSync(t *testing.T) {
}, false)
assert.NoError(t, err)
assert.True(t, mirrorRepo.IsMirror)
mirrorRepo, err = repo_service.MigrateRepositoryGitData(t.Context(), user, mirrorRepo, opts, nil)
mirrorRepo, err = repo_service.MigrateRepositoryGitData(t.Context(), user, mirrorRepo, opts, nil, nil)
assert.NoError(t, err)
mirrorContext := NewAPITestContext(t, user.Name, mirrorRepo.Name, auth_model.AccessTokenScopeWriteRepository)

View File

@ -51,7 +51,7 @@ func TestMirrorPull(t *testing.T) {
assert.NoError(t, err)
assert.True(t, mirrorRepo.IsMirror, "expected pull-mirror repo to be marked as a mirror immediately after its creation")
mirrorRepo, err = repo_service.MigrateRepositoryGitData(ctx, user, mirrorRepo, opts, nil)
mirrorRepo, err = repo_service.MigrateRepositoryGitData(ctx, user, mirrorRepo, opts, nil, nil)
assert.NoError(t, err)
// these units should have been enabled