summaryrefslogtreecommitdiffstats
path: root/modules/migrations
diff options
context:
space:
mode:
authorzeripath <art27@cantab.net>2021-03-15 21:52:11 +0000
committerGitHub <noreply@github.com>2021-03-15 17:52:11 -0400
commit6e423d5573c20b78d6e21cb044e8f4d5de5b288a (patch)
tree61d2e282bc652b8254271fdd9e19b87a386b5dc7 /modules/migrations
parentf268b4896b1030761b28f1f8923d77d87adb8f0b (diff)
downloadgitea-6e423d5573c20b78d6e21cb044e8f4d5de5b288a.tar.gz
gitea-6e423d5573c20b78d6e21cb044e8f4d5de5b288a.zip
Ensure validation occurs on clone addresses too (#14994)
* Ensure validation occurs on clone addresses too Fix #14984 Signed-off-by: Andrew Thornton <art27@cantab.net> * fix lint Signed-off-by: Andrew Thornton <art27@cantab.net> * fix test Signed-off-by: Andrew Thornton <art27@cantab.net> * Fix api tests Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Diffstat (limited to 'modules/migrations')
-rw-r--r--modules/migrations/migrate.go59
-rw-r--r--modules/migrations/migrate_test.go36
2 files changed, 71 insertions, 24 deletions
diff --git a/modules/migrations/migrate.go b/modules/migrations/migrate.go
index 656b78a584..619b572a3f 100644
--- a/modules/migrations/migrate.go
+++ b/modules/migrations/migrate.go
@@ -10,6 +10,7 @@ import (
"fmt"
"net"
"net/url"
+ "path/filepath"
"strings"
"code.gitea.io/gitea/models"
@@ -17,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/matchlist"
"code.gitea.io/gitea/modules/migrations/base"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
)
// MigrateOptions is equal to base.MigrateOptions
@@ -34,39 +36,60 @@ func RegisterDownloaderFactory(factory base.DownloaderFactory) {
factories = append(factories, factory)
}
-func isMigrateURLAllowed(remoteURL string) error {
+// IsMigrateURLAllowed checks if an URL is allowed to be migrated from
+func IsMigrateURLAllowed(remoteURL string, doer *models.User) error {
+ // Remote address can be HTTP/HTTPS/Git URL or local path.
u, err := url.Parse(strings.ToLower(remoteURL))
if err != nil {
- return err
+ return &models.ErrInvalidCloneAddr{IsURLError: true}
}
- if strings.EqualFold(u.Scheme, "http") || strings.EqualFold(u.Scheme, "https") {
- if len(setting.Migrations.AllowedDomains) > 0 {
- if !allowList.Match(u.Host) {
- return &models.ErrMigrationNotAllowed{Host: u.Host}
- }
- } else {
- if blockList.Match(u.Host) {
- return &models.ErrMigrationNotAllowed{Host: u.Host}
- }
+ if u.Scheme == "file" || u.Scheme == "" {
+ if !doer.CanImportLocal() {
+ return &models.ErrInvalidCloneAddr{Host: "<LOCAL_FILESYSTEM>", IsPermissionDenied: true, LocalPath: true}
+ }
+ isAbs := filepath.IsAbs(u.Host + u.Path)
+ if !isAbs {
+ return &models.ErrInvalidCloneAddr{Host: "<LOCAL_FILESYSTEM>", IsInvalidPath: true, LocalPath: true}
+ }
+ isDir, err := util.IsDir(u.Host + u.Path)
+ if err != nil {
+ log.Error("Unable to check if %s is a directory: %v", u.Host+u.Path, err)
+ return err
+ }
+ if !isDir {
+ return &models.ErrInvalidCloneAddr{Host: "<LOCAL_FILESYSTEM>", IsInvalidPath: true, LocalPath: true}
}
+
+ return nil
+ }
+
+ if u.Scheme == "git" && u.Port() != "" && (strings.Contains(remoteURL, "%0d") || strings.Contains(remoteURL, "%0a")) {
+ return &models.ErrInvalidCloneAddr{Host: u.Host, IsURLError: true}
}
- if u.Host == "" {
- if !setting.ImportLocalPaths {
- return &models.ErrMigrationNotAllowed{Host: "<LOCAL_FILESYSTEM>"}
+ if u.Opaque != "" || u.Scheme != "" && u.Scheme != "http" && u.Scheme != "https" && u.Scheme != "git" {
+ return &models.ErrInvalidCloneAddr{Host: u.Host, IsProtocolInvalid: true, IsPermissionDenied: true, IsURLError: true}
+ }
+
+ if len(setting.Migrations.AllowedDomains) > 0 {
+ if !allowList.Match(u.Host) {
+ return &models.ErrInvalidCloneAddr{Host: u.Host, IsPermissionDenied: true}
+ }
+ } else {
+ if blockList.Match(u.Host) {
+ return &models.ErrInvalidCloneAddr{Host: u.Host, IsPermissionDenied: true}
}
- return nil
}
if !setting.Migrations.AllowLocalNetworks {
addrList, err := net.LookupIP(strings.Split(u.Host, ":")[0])
if err != nil {
- return &models.ErrMigrationNotAllowed{Host: u.Host, NotResolvedIP: true}
+ return &models.ErrInvalidCloneAddr{Host: u.Host, NotResolvedIP: true}
}
for _, addr := range addrList {
if isIPPrivate(addr) || !addr.IsGlobalUnicast() {
- return &models.ErrMigrationNotAllowed{Host: u.Host, PrivateNet: addr.String()}
+ return &models.ErrInvalidCloneAddr{Host: u.Host, PrivateNet: addr.String(), IsPermissionDenied: true}
}
}
}
@@ -76,7 +99,7 @@ func isMigrateURLAllowed(remoteURL string) error {
// MigrateRepository migrate repository according MigrateOptions
func MigrateRepository(ctx context.Context, doer *models.User, ownerName string, opts base.MigrateOptions) (*models.Repository, error) {
- err := isMigrateURLAllowed(opts.CloneAddr)
+ err := IsMigrateURLAllowed(opts.CloneAddr, doer)
if err != nil {
return nil, err
}
diff --git a/modules/migrations/migrate_test.go b/modules/migrations/migrate_test.go
index e8b71bb325..be119d32d3 100644
--- a/modules/migrations/migrate_test.go
+++ b/modules/migrations/migrate_test.go
@@ -5,41 +5,65 @@
package migrations
import (
+ "path/filepath"
"testing"
+ "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
)
func TestMigrateWhiteBlocklist(t *testing.T) {
+ assert.NoError(t, models.PrepareTestDatabase())
+
+ adminUser := models.AssertExistsAndLoadBean(t, &models.User{Name: "user1"}).(*models.User)
+ nonAdminUser := models.AssertExistsAndLoadBean(t, &models.User{Name: "user2"}).(*models.User)
+
setting.Migrations.AllowedDomains = []string{"github.com"}
assert.NoError(t, Init())
- err := isMigrateURLAllowed("https://gitlab.com/gitlab/gitlab.git")
+ err := IsMigrateURLAllowed("https://gitlab.com/gitlab/gitlab.git", nonAdminUser)
assert.Error(t, err)
- err = isMigrateURLAllowed("https://github.com/go-gitea/gitea.git")
+ err = IsMigrateURLAllowed("https://github.com/go-gitea/gitea.git", nonAdminUser)
assert.NoError(t, err)
setting.Migrations.AllowedDomains = []string{}
setting.Migrations.BlockedDomains = []string{"github.com"}
assert.NoError(t, Init())
- err = isMigrateURLAllowed("https://gitlab.com/gitlab/gitlab.git")
+ err = IsMigrateURLAllowed("https://gitlab.com/gitlab/gitlab.git", nonAdminUser)
assert.NoError(t, err)
- err = isMigrateURLAllowed("https://github.com/go-gitea/gitea.git")
+ err = IsMigrateURLAllowed("https://github.com/go-gitea/gitea.git", nonAdminUser)
+ assert.Error(t, err)
+
+ err = IsMigrateURLAllowed("https://10.0.0.1/go-gitea/gitea.git", nonAdminUser)
assert.Error(t, err)
+ setting.Migrations.AllowLocalNetworks = true
+ err = IsMigrateURLAllowed("https://10.0.0.1/go-gitea/gitea.git", nonAdminUser)
+ assert.NoError(t, err)
+
old := setting.ImportLocalPaths
setting.ImportLocalPaths = false
- err = isMigrateURLAllowed("/home/foo/bar/goo")
+ err = IsMigrateURLAllowed("/home/foo/bar/goo", adminUser)
assert.Error(t, err)
setting.ImportLocalPaths = true
- err = isMigrateURLAllowed("/home/foo/bar/goo")
+ abs, err := filepath.Abs(".")
+ assert.NoError(t, err)
+
+ err = IsMigrateURLAllowed(abs, adminUser)
+ assert.NoError(t, err)
+
+ err = IsMigrateURLAllowed(abs, nonAdminUser)
+ assert.Error(t, err)
+
+ nonAdminUser.AllowImportLocal = true
+ err = IsMigrateURLAllowed(abs, nonAdminUser)
assert.NoError(t, err)
setting.ImportLocalPaths = old