fix: do not allow SSH url for migration (#7004)

- Add a new function `IsPushMirrorURLAllowed` that will allow `ssh://` url and make the existing `IsMigrateURLAllowed` not allow such URLs anymore.
- Resolves forgejo/forgejo#6960
- Existing integration tests make sure that SSH urls are still allowed for the push mirror feature and added unit test to ensure that `IsMigrateURLAllowed` no longer allows SSH urls.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7004
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
This commit is contained in:
Gusted 2025-02-21 07:57:06 +00:00 committed by Earl Warren
parent 8910580d0b
commit e8ebb5d6e3
4 changed files with 28 additions and 4 deletions

View file

@ -363,7 +363,7 @@ func CreatePushMirror(ctx *context.APIContext, mirrorOption *api.CreatePushMirro
address, err := forms.ParseRemoteAddr(mirrorOption.RemoteAddress, mirrorOption.RemoteUsername, mirrorOption.RemotePassword) address, err := forms.ParseRemoteAddr(mirrorOption.RemoteAddress, mirrorOption.RemoteUsername, mirrorOption.RemotePassword)
if err == nil { if err == nil {
err = migrations.IsMigrateURLAllowed(address, ctx.ContextUser) err = migrations.IsPushMirrorURLAllowed(address, ctx.ContextUser)
} }
if err != nil { if err != nil {
HandleRemoteAddressError(ctx, err) HandleRemoteAddressError(ctx, err)

View file

@ -651,7 +651,7 @@ func SettingsPost(ctx *context.Context) {
address, err := forms.ParseRemoteAddr(form.PushMirrorAddress, form.PushMirrorUsername, form.PushMirrorPassword) address, err := forms.ParseRemoteAddr(form.PushMirrorAddress, form.PushMirrorUsername, form.PushMirrorPassword)
if err == nil { if err == nil {
err = migrations.IsMigrateURLAllowed(address, ctx.Doer) err = migrations.IsPushMirrorURLAllowed(address, ctx.Doer)
} }
if err != nil { if err != nil {
ctx.Data["Err_PushMirrorAddress"] = true ctx.Data["Err_PushMirrorAddress"] = true

View file

@ -39,8 +39,17 @@ func RegisterDownloaderFactory(factory base.DownloaderFactory) {
factories = append(factories, factory) factories = append(factories, factory)
} }
// IsMigrateURLAllowed checks if an URL is allowed to be migrated from // IsPushMirrorURLAllowed checks if an URL is allowed to be pushed to.
func IsPushMirrorURLAllowed(remoteURL string, doer *user_model.User) error {
return isURLAllowed(remoteURL, doer, true)
}
// IsMigrateURLAllowed checks if an URL is allowed to be migrated from.
func IsMigrateURLAllowed(remoteURL string, doer *user_model.User) error { func IsMigrateURLAllowed(remoteURL string, doer *user_model.User) error {
return isURLAllowed(remoteURL, doer, false)
}
func isURLAllowed(remoteURL string, doer *user_model.User, isPushMirror bool) error {
// Remote address can be HTTP/HTTPS/Git URL or local path. // Remote address can be HTTP/HTTPS/Git URL or local path.
u, err := url.Parse(remoteURL) u, err := url.Parse(remoteURL)
if err != nil { if err != nil {
@ -71,7 +80,7 @@ func IsMigrateURLAllowed(remoteURL string, doer *user_model.User) error {
return &models.ErrInvalidCloneAddr{Host: u.Host, IsURLError: true} return &models.ErrInvalidCloneAddr{Host: u.Host, IsURLError: true}
} }
if u.Opaque != "" || u.Scheme != "" && u.Scheme != "http" && u.Scheme != "https" && u.Scheme != "git" && u.Scheme != "ssh" { if u.Opaque != "" || u.Scheme != "" && u.Scheme != "http" && u.Scheme != "https" && u.Scheme != "git" && u.Scheme != "ssh" || (!isPushMirror && u.Scheme == "ssh") {
return &models.ErrInvalidCloneAddr{Host: u.Host, IsProtocolInvalid: true, IsPermissionDenied: true, IsURLError: true} return &models.ErrInvalidCloneAddr{Host: u.Host, IsProtocolInvalid: true, IsPermissionDenied: true, IsURLError: true}
} }

View file

@ -113,3 +113,18 @@ func TestAllowBlockList(t *testing.T) {
// reset // reset
init("", "", false) init("", "", false)
} }
func TestURLAllowedSSH(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"})
sshURL := "ssh://git@git.gay/gitgay/forgejo"
t.Run("Migrate URL", func(t *testing.T) {
require.Error(t, IsMigrateURLAllowed(sshURL, user))
})
t.Run("Pushmirror URL", func(t *testing.T) {
require.NoError(t, IsPushMirrorURLAllowed(sshURL, user))
})
}