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

Merge bee3e1c09b1cedad9b379cc112b25f6b9fc1c186 into 6eed75af248ae597d854a3c5e6b8831a5ff76290

This commit is contained in:
Lunny Xiao 2026-04-03 04:38:04 +00:00 committed by GitHub
commit 3657d33223
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 105 additions and 5 deletions

View File

@ -287,6 +287,7 @@ func (g *RepositoryDumper) CreateLabels(_ context.Context, labels ...*base.Label
// CreateReleases creates releases
func (g *RepositoryDumper) CreateReleases(_ context.Context, releases ...*base.Release) error {
if g.opts.ReleaseAssets {
httpClient := NewMigrationHTTPClient()
for _, release := range releases {
attachDir := filepath.Join("release_assets", uuid.New().String())
if err := os.MkdirAll(filepath.Join(g.baseDir, attachDir), os.ModePerm); err != nil {
@ -296,25 +297,26 @@ func (g *RepositoryDumper) CreateReleases(_ context.Context, releases ...*base.R
// we cannot use asset.Name because it might contains special characters.
attachLocalPath := filepath.Join(attachDir, uuid.New().String())
// SECURITY: We cannot check the DownloadURL and DownloadFunc are safe here
// ... we must assume that they are safe and simply download the attachment
// SECURITY: Prefer DownloadFunc and fall back to a migration-filtered HTTP client.
// download attachment
err := func(attachPath string) error {
var rc io.ReadCloser
var err error
if asset.DownloadURL == nil {
if asset.DownloadFunc != nil {
rc, err = asset.DownloadFunc()
if err != nil {
return err
}
defer rc.Close()
} else {
resp, err := http.Get(*asset.DownloadURL)
} else if asset.DownloadURL != nil {
resp, err := httpClient.Get(*asset.DownloadURL)
if err != nil {
return err
}
defer resp.Body.Close()
rc = resp.Body
} else {
return errors.New("missing download URL and download function")
}
fw, err := os.Create(attachPath)

View File

@ -0,0 +1,98 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package migrations
import (
"context"
"io"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"sync/atomic"
"testing"
"code.gitea.io/gitea/models/unittest"
base "code.gitea.io/gitea/modules/migration"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRepositoryDumperReleaseAssetPrefersDownloadFunc(t *testing.T) {
var downloadURLHits int32
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
atomic.AddInt32(&downloadURLHits, 1)
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("remote"))
}))
t.Cleanup(server.Close)
downloadURL := server.URL + "/asset"
var downloadFuncCalls int32
asset := &base.ReleaseAsset{
Name: "asset.txt",
DownloadURL: &downloadURL,
DownloadFunc: func() (io.ReadCloser, error) {
atomic.AddInt32(&downloadFuncCalls, 1)
return io.NopCloser(strings.NewReader("local")), nil
},
}
release := &base.Release{
TagName: "v1.0.0",
Assets: []*base.ReleaseAsset{asset},
}
baseDir := t.TempDir()
dumper, err := NewRepositoryDumper(context.Background(), baseDir, "owner", "repo", base.MigrateOptions{ReleaseAssets: true})
require.NoError(t, err)
require.NoError(t, dumper.CreateReleases(context.Background(), release))
assert.Equal(t, int32(1), atomic.LoadInt32(&downloadFuncCalls))
assert.Equal(t, int32(0), atomic.LoadInt32(&downloadURLHits))
attachRelative := filepath.Join("release_assets", release.TagName, asset.Name)
attachPath := filepath.Join(baseDir, "owner", "repo", attachRelative)
data, err := os.ReadFile(attachPath)
require.NoError(t, err)
assert.Equal(t, "local", string(data))
require.NotNil(t, asset.DownloadURL)
assert.Equal(t, attachRelative, *asset.DownloadURL)
}
func TestRepositoryDumperReleaseAssetUsesMigrationClient(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
test.MockVariableValue(&setting.Migrations.AllowedDomains, "github.com")()
test.MockVariableValue(&setting.Migrations.AllowLocalNetworks, false)()
assert.NoError(t, Init())
var downloadURLHits int32
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
atomic.AddInt32(&downloadURLHits, 1)
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("remote"))
}))
t.Cleanup(server.Close)
downloadURL := server.URL + "/asset"
asset := &base.ReleaseAsset{
Name: "asset.txt",
DownloadURL: &downloadURL,
}
release := &base.Release{
TagName: "v1.0.0",
Assets: []*base.ReleaseAsset{asset},
}
baseDir := t.TempDir()
dumper, err := NewRepositoryDumper(context.Background(), baseDir, "owner", "repo", base.MigrateOptions{ReleaseAssets: true})
require.NoError(t, err)
assert.Error(t, dumper.CreateReleases(context.Background(), release))
assert.Equal(t, int32(0), atomic.LoadInt32(&downloadURLHits))
}