mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	Use hostmatcher to replace matchlist, improve security (#17605)
				
					
				
			Use hostmacher to replace matchlist. And we introduce a better DialContext to do a full host/IP check, otherwise the attackers can still bypass the allow/block list by a 302 redirection.
This commit is contained in:
		@@ -6,7 +6,6 @@ package migrations
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
@@ -18,8 +17,6 @@ import (
 | 
			
		||||
	admin_model "code.gitea.io/gitea/models/admin"
 | 
			
		||||
	"code.gitea.io/gitea/modules/log"
 | 
			
		||||
	base "code.gitea.io/gitea/modules/migration"
 | 
			
		||||
	"code.gitea.io/gitea/modules/proxy"
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
	"code.gitea.io/gitea/modules/structs"
 | 
			
		||||
 | 
			
		||||
	gitea_sdk "code.gitea.io/sdk/gitea"
 | 
			
		||||
@@ -90,12 +87,7 @@ func NewGiteaDownloader(ctx context.Context, baseURL, repoPath, username, passwo
 | 
			
		||||
		gitea_sdk.SetToken(token),
 | 
			
		||||
		gitea_sdk.SetBasicAuth(username, password),
 | 
			
		||||
		gitea_sdk.SetContext(ctx),
 | 
			
		||||
		gitea_sdk.SetHTTPClient(&http.Client{
 | 
			
		||||
			Transport: &http.Transport{
 | 
			
		||||
				TLSClientConfig: &tls.Config{InsecureSkipVerify: setting.Migrations.SkipTLSVerify},
 | 
			
		||||
				Proxy:           proxy.Proxy(),
 | 
			
		||||
			},
 | 
			
		||||
		}),
 | 
			
		||||
		gitea_sdk.SetHTTPClient(NewMigrationHTTPClient()),
 | 
			
		||||
	)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error(fmt.Sprintf("Failed to create NewGiteaDownloader for: %s. Error: %v", baseURL, err))
 | 
			
		||||
@@ -275,12 +267,7 @@ func (g *GiteaDownloader) convertGiteaRelease(rel *gitea_sdk.Release) *base.Rele
 | 
			
		||||
		Created:         rel.CreatedAt,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	httpClient := &http.Client{
 | 
			
		||||
		Transport: &http.Transport{
 | 
			
		||||
			TLSClientConfig: &tls.Config{InsecureSkipVerify: setting.Migrations.SkipTLSVerify},
 | 
			
		||||
			Proxy:           proxy.Proxy(),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	httpClient := NewMigrationHTTPClient()
 | 
			
		||||
 | 
			
		||||
	for _, asset := range rel.Attachments {
 | 
			
		||||
		size := int(asset.Size)
 | 
			
		||||
 
 | 
			
		||||
@@ -125,7 +125,7 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate
 | 
			
		||||
		Wiki:           opts.Wiki,
 | 
			
		||||
		Releases:       opts.Releases, // if didn't get releases, then sync them from tags
 | 
			
		||||
		MirrorInterval: opts.MirrorInterval,
 | 
			
		||||
	})
 | 
			
		||||
	}, NewMigrationHTTPTransport())
 | 
			
		||||
 | 
			
		||||
	g.repo = r
 | 
			
		||||
	if err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,6 @@ package migrations
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
@@ -19,7 +18,6 @@ import (
 | 
			
		||||
	"code.gitea.io/gitea/modules/log"
 | 
			
		||||
	base "code.gitea.io/gitea/modules/migration"
 | 
			
		||||
	"code.gitea.io/gitea/modules/proxy"
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
	"code.gitea.io/gitea/modules/structs"
 | 
			
		||||
	"code.gitea.io/gitea/modules/util"
 | 
			
		||||
 | 
			
		||||
@@ -100,12 +98,7 @@ func NewGithubDownloaderV3(ctx context.Context, baseURL, userName, password, tok
 | 
			
		||||
			)
 | 
			
		||||
			var client = &http.Client{
 | 
			
		||||
				Transport: &oauth2.Transport{
 | 
			
		||||
					Base: &http.Transport{
 | 
			
		||||
						TLSClientConfig: &tls.Config{InsecureSkipVerify: setting.Migrations.SkipTLSVerify},
 | 
			
		||||
						Proxy: func(req *http.Request) (*url.URL, error) {
 | 
			
		||||
							return proxy.Proxy()(req)
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					Base:   NewMigrationHTTPTransport(),
 | 
			
		||||
					Source: oauth2.ReuseTokenSource(nil, ts),
 | 
			
		||||
				},
 | 
			
		||||
			}
 | 
			
		||||
@@ -113,14 +106,13 @@ func NewGithubDownloaderV3(ctx context.Context, baseURL, userName, password, tok
 | 
			
		||||
			downloader.addClient(client, baseURL)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		var transport = NewMigrationHTTPTransport()
 | 
			
		||||
		transport.Proxy = func(req *http.Request) (*url.URL, error) {
 | 
			
		||||
			req.SetBasicAuth(userName, password)
 | 
			
		||||
			return proxy.Proxy()(req)
 | 
			
		||||
		}
 | 
			
		||||
		var client = &http.Client{
 | 
			
		||||
			Transport: &http.Transport{
 | 
			
		||||
				TLSClientConfig: &tls.Config{InsecureSkipVerify: setting.Migrations.SkipTLSVerify},
 | 
			
		||||
				Proxy: func(req *http.Request) (*url.URL, error) {
 | 
			
		||||
					req.SetBasicAuth(userName, password)
 | 
			
		||||
					return proxy.Proxy()(req)
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			Transport: transport,
 | 
			
		||||
		}
 | 
			
		||||
		downloader.addClient(client, baseURL)
 | 
			
		||||
	}
 | 
			
		||||
@@ -316,12 +308,7 @@ func (g *GithubDownloaderV3) convertGithubRelease(rel *github.RepositoryRelease)
 | 
			
		||||
		r.Published = rel.PublishedAt.Time
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	httpClient := &http.Client{
 | 
			
		||||
		Transport: &http.Transport{
 | 
			
		||||
			TLSClientConfig: &tls.Config{InsecureSkipVerify: setting.Migrations.SkipTLSVerify},
 | 
			
		||||
			Proxy:           proxy.Proxy(),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	httpClient := NewMigrationHTTPClient()
 | 
			
		||||
 | 
			
		||||
	for _, asset := range rel.Assets {
 | 
			
		||||
		var assetID = *asset.ID // Don't optimize this, for closure we need a local variable
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@ package migrations
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
@@ -18,8 +17,6 @@ import (
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/modules/log"
 | 
			
		||||
	base "code.gitea.io/gitea/modules/migration"
 | 
			
		||||
	"code.gitea.io/gitea/modules/proxy"
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
	"code.gitea.io/gitea/modules/structs"
 | 
			
		||||
 | 
			
		||||
	"github.com/xanzy/go-gitlab"
 | 
			
		||||
@@ -77,16 +74,11 @@ type GitlabDownloader struct {
 | 
			
		||||
//   Use either a username/password, personal token entered into the username field, or anonymous/public access
 | 
			
		||||
//   Note: Public access only allows very basic access
 | 
			
		||||
func NewGitlabDownloader(ctx context.Context, baseURL, repoPath, username, password, token string) (*GitlabDownloader, error) {
 | 
			
		||||
	gitlabClient, err := gitlab.NewClient(token, gitlab.WithBaseURL(baseURL), gitlab.WithHTTPClient(&http.Client{
 | 
			
		||||
		Transport: &http.Transport{
 | 
			
		||||
			TLSClientConfig: &tls.Config{InsecureSkipVerify: setting.Migrations.SkipTLSVerify},
 | 
			
		||||
			Proxy:           proxy.Proxy(),
 | 
			
		||||
		},
 | 
			
		||||
	}))
 | 
			
		||||
	gitlabClient, err := gitlab.NewClient(token, gitlab.WithBaseURL(baseURL), gitlab.WithHTTPClient(NewMigrationHTTPClient()))
 | 
			
		||||
	// Only use basic auth if token is blank and password is NOT
 | 
			
		||||
	// Basic auth will fail with empty strings, but empty token will allow anonymous public API usage
 | 
			
		||||
	if token == "" && password != "" {
 | 
			
		||||
		gitlabClient, err = gitlab.NewBasicAuthClient(username, password, gitlab.WithBaseURL(baseURL))
 | 
			
		||||
		gitlabClient, err = gitlab.NewBasicAuthClient(username, password, gitlab.WithBaseURL(baseURL), gitlab.WithHTTPClient(NewMigrationHTTPClient()))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -300,12 +292,7 @@ func (g *GitlabDownloader) convertGitlabRelease(rel *gitlab.Release) *base.Relea
 | 
			
		||||
		PublisherName:   rel.Author.Username,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	httpClient := &http.Client{
 | 
			
		||||
		Transport: &http.Transport{
 | 
			
		||||
			TLSClientConfig: &tls.Config{InsecureSkipVerify: setting.Migrations.SkipTLSVerify},
 | 
			
		||||
			Proxy:           proxy.Proxy(),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	httpClient := NewMigrationHTTPClient()
 | 
			
		||||
 | 
			
		||||
	for k, asset := range rel.Assets.Links {
 | 
			
		||||
		r.Assets = append(r.Assets, &base.ReleaseAsset{
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@ package migrations
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
@@ -16,7 +15,6 @@ import (
 | 
			
		||||
	"code.gitea.io/gitea/modules/log"
 | 
			
		||||
	base "code.gitea.io/gitea/modules/migration"
 | 
			
		||||
	"code.gitea.io/gitea/modules/proxy"
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
	"code.gitea.io/gitea/modules/structs"
 | 
			
		||||
 | 
			
		||||
	"github.com/gogs/go-gogs-client"
 | 
			
		||||
@@ -97,13 +95,12 @@ func NewGogsDownloader(ctx context.Context, baseURL, userName, password, token,
 | 
			
		||||
		client = gogs.NewClient(baseURL, token)
 | 
			
		||||
		downloader.userName = token
 | 
			
		||||
	} else {
 | 
			
		||||
		downloader.transport = &http.Transport{
 | 
			
		||||
			TLSClientConfig: &tls.Config{InsecureSkipVerify: setting.Migrations.SkipTLSVerify},
 | 
			
		||||
			Proxy: func(req *http.Request) (*url.URL, error) {
 | 
			
		||||
				req.SetBasicAuth(userName, password)
 | 
			
		||||
				return proxy.Proxy()(req)
 | 
			
		||||
			},
 | 
			
		||||
		var transport = NewMigrationHTTPTransport()
 | 
			
		||||
		transport.Proxy = func(req *http.Request) (*url.URL, error) {
 | 
			
		||||
			req.SetBasicAuth(userName, password)
 | 
			
		||||
			return proxy.Proxy()(req)
 | 
			
		||||
		}
 | 
			
		||||
		downloader.transport = transport
 | 
			
		||||
 | 
			
		||||
		client = gogs.NewClient(baseURL, "")
 | 
			
		||||
		client.SetHTTPClient(&http.Client{
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										30
									
								
								services/migrations/http_client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								services/migrations/http_client.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
// Copyright 2021 The Gitea Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a MIT-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package migrations
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/modules/hostmatcher"
 | 
			
		||||
	"code.gitea.io/gitea/modules/proxy"
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NewMigrationHTTPClient returns a HTTP client for migration
 | 
			
		||||
func NewMigrationHTTPClient() *http.Client {
 | 
			
		||||
	return &http.Client{
 | 
			
		||||
		Transport: NewMigrationHTTPTransport(),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMigrationHTTPTransport returns a HTTP transport for migration
 | 
			
		||||
func NewMigrationHTTPTransport() *http.Transport {
 | 
			
		||||
	return &http.Transport{
 | 
			
		||||
		TLSClientConfig: &tls.Config{InsecureSkipVerify: setting.Migrations.SkipTLSVerify},
 | 
			
		||||
		Proxy:           proxy.Proxy(),
 | 
			
		||||
		DialContext:     hostmatcher.NewDialContext("migration", allowList, blockList),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -15,8 +15,8 @@ import (
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models"
 | 
			
		||||
	admin_model "code.gitea.io/gitea/models/admin"
 | 
			
		||||
	"code.gitea.io/gitea/modules/hostmatcher"
 | 
			
		||||
	"code.gitea.io/gitea/modules/log"
 | 
			
		||||
	"code.gitea.io/gitea/modules/matchlist"
 | 
			
		||||
	base "code.gitea.io/gitea/modules/migration"
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
	"code.gitea.io/gitea/modules/util"
 | 
			
		||||
@@ -28,8 +28,8 @@ type MigrateOptions = base.MigrateOptions
 | 
			
		||||
var (
 | 
			
		||||
	factories []base.DownloaderFactory
 | 
			
		||||
 | 
			
		||||
	allowList *matchlist.Matchlist
 | 
			
		||||
	blockList *matchlist.Matchlist
 | 
			
		||||
	allowList *hostmatcher.HostMatchList
 | 
			
		||||
	blockList *hostmatcher.HostMatchList
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RegisterDownloaderFactory registers a downloader factory
 | 
			
		||||
@@ -73,30 +73,35 @@ func IsMigrateURLAllowed(remoteURL string, doer *models.User) error {
 | 
			
		||||
		return &models.ErrInvalidCloneAddr{Host: u.Host, IsProtocolInvalid: true, IsPermissionDenied: true, IsURLError: true}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	host := strings.ToLower(u.Host)
 | 
			
		||||
	if len(setting.Migrations.AllowedDomains) > 0 {
 | 
			
		||||
		if !allowList.Match(host) {
 | 
			
		||||
			return &models.ErrInvalidCloneAddr{Host: u.Host, IsPermissionDenied: true}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if blockList.Match(host) {
 | 
			
		||||
	hostName, _, err := net.SplitHostPort(u.Host)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// u.Host can be "host" or "host:port"
 | 
			
		||||
		err = nil //nolint
 | 
			
		||||
		hostName = u.Host
 | 
			
		||||
	}
 | 
			
		||||
	addrList, err := net.LookupIP(hostName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return &models.ErrInvalidCloneAddr{Host: u.Host, NotResolvedIP: true}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var ipAllowed bool
 | 
			
		||||
	var ipBlocked bool
 | 
			
		||||
	for _, addr := range addrList {
 | 
			
		||||
		ipAllowed = ipAllowed || allowList.MatchIPAddr(addr)
 | 
			
		||||
		ipBlocked = ipBlocked || blockList.MatchIPAddr(addr)
 | 
			
		||||
	}
 | 
			
		||||
	var blockedError error
 | 
			
		||||
	if blockList.MatchHostName(hostName) || ipBlocked {
 | 
			
		||||
		blockedError = &models.ErrInvalidCloneAddr{Host: u.Host, IsPermissionDenied: true}
 | 
			
		||||
	}
 | 
			
		||||
	// if we have an allow-list, check the allow-list first
 | 
			
		||||
	if !allowList.IsEmpty() {
 | 
			
		||||
		if !allowList.MatchHostName(hostName) && !ipAllowed {
 | 
			
		||||
			return &models.ErrInvalidCloneAddr{Host: u.Host, IsPermissionDenied: true}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !setting.Migrations.AllowLocalNetworks {
 | 
			
		||||
		addrList, err := net.LookupIP(strings.Split(u.Host, ":")[0])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return &models.ErrInvalidCloneAddr{Host: u.Host, NotResolvedIP: true}
 | 
			
		||||
		}
 | 
			
		||||
		for _, addr := range addrList {
 | 
			
		||||
			if util.IsIPPrivate(addr) || !addr.IsGlobalUnicast() {
 | 
			
		||||
				return &models.ErrInvalidCloneAddr{Host: u.Host, PrivateNet: addr.String(), IsPermissionDenied: true}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
	// otherwise, we always follow the blocked list
 | 
			
		||||
	return blockedError
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MigrateRepository migrate repository according MigrateOptions
 | 
			
		||||
@@ -462,16 +467,18 @@ func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts
 | 
			
		||||
 | 
			
		||||
// Init migrations service
 | 
			
		||||
func Init() error {
 | 
			
		||||
	var err error
 | 
			
		||||
	allowList, err = matchlist.NewMatchlist(setting.Migrations.AllowedDomains...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("init migration allowList domains failed: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	// TODO: maybe we can deprecate these legacy ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS, use ALLOWED_HOST_LIST/BLOCKED_HOST_LIST instead
 | 
			
		||||
 | 
			
		||||
	blockList, err = matchlist.NewMatchlist(setting.Migrations.BlockedDomains...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("init migration blockList domains failed: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	blockList = hostmatcher.ParseSimpleMatchList("migrations.BLOCKED_DOMAINS", setting.Migrations.BlockedDomains)
 | 
			
		||||
 | 
			
		||||
	allowList = hostmatcher.ParseSimpleMatchList("migrations.ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS", setting.Migrations.AllowedDomains)
 | 
			
		||||
	if allowList.IsEmpty() {
 | 
			
		||||
		// the default policy is that migration module can access external hosts
 | 
			
		||||
		allowList.AppendBuiltin(hostmatcher.MatchBuiltinExternal)
 | 
			
		||||
	}
 | 
			
		||||
	if setting.Migrations.AllowLocalNetworks {
 | 
			
		||||
		allowList.AppendBuiltin(hostmatcher.MatchBuiltinPrivate)
 | 
			
		||||
		allowList.AppendBuiltin(hostmatcher.MatchBuiltinLoopback)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,8 @@ func TestMigrateWhiteBlocklist(t *testing.T) {
 | 
			
		||||
	adminUser := unittest.AssertExistsAndLoadBean(t, &models.User{Name: "user1"}).(*models.User)
 | 
			
		||||
	nonAdminUser := unittest.AssertExistsAndLoadBean(t, &models.User{Name: "user2"}).(*models.User)
 | 
			
		||||
 | 
			
		||||
	setting.Migrations.AllowedDomains = []string{"github.com"}
 | 
			
		||||
	setting.Migrations.AllowedDomains = "github.com"
 | 
			
		||||
	setting.Migrations.AllowLocalNetworks = false
 | 
			
		||||
	assert.NoError(t, Init())
 | 
			
		||||
 | 
			
		||||
	err := IsMigrateURLAllowed("https://gitlab.com/gitlab/gitlab.git", nonAdminUser)
 | 
			
		||||
@@ -33,8 +34,8 @@ func TestMigrateWhiteBlocklist(t *testing.T) {
 | 
			
		||||
	err = IsMigrateURLAllowed("https://gITHUb.com/go-gitea/gitea.git", nonAdminUser)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	setting.Migrations.AllowedDomains = []string{}
 | 
			
		||||
	setting.Migrations.BlockedDomains = []string{"github.com"}
 | 
			
		||||
	setting.Migrations.AllowedDomains = ""
 | 
			
		||||
	setting.Migrations.BlockedDomains = "github.com"
 | 
			
		||||
	assert.NoError(t, Init())
 | 
			
		||||
 | 
			
		||||
	err = IsMigrateURLAllowed("https://gitlab.com/gitlab/gitlab.git", nonAdminUser)
 | 
			
		||||
@@ -47,6 +48,7 @@ func TestMigrateWhiteBlocklist(t *testing.T) {
 | 
			
		||||
	assert.Error(t, err)
 | 
			
		||||
 | 
			
		||||
	setting.Migrations.AllowLocalNetworks = true
 | 
			
		||||
	assert.NoError(t, Init())
 | 
			
		||||
	err = IsMigrateURLAllowed("https://10.0.0.1/go-gitea/gitea.git", nonAdminUser)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user