Skip to content

Commit e94f8d5

Browse files
authored
Correctly handle submodule view and avoid throwing 500 error (#34121)
Auto-redirect for in-site links, and show 404 for external links (to avoid open redirect or phishing)
1 parent a62ed19 commit e94f8d5

File tree

2 files changed

+59
-0
lines changed

2 files changed

+59
-0
lines changed

Diff for: routers/web/repo/view_home.go

+27
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import (
2020
unit_model "code.gitea.io/gitea/models/unit"
2121
user_model "code.gitea.io/gitea/models/user"
2222
"code.gitea.io/gitea/modules/git"
23+
giturl "code.gitea.io/gitea/modules/git/url"
24+
"code.gitea.io/gitea/modules/httplib"
2325
"code.gitea.io/gitea/modules/log"
2426
repo_module "code.gitea.io/gitea/modules/repository"
2527
"code.gitea.io/gitea/modules/setting"
@@ -302,8 +304,33 @@ func handleRepoEmptyOrBroken(ctx *context.Context) {
302304
ctx.Redirect(link)
303305
}
304306

307+
func handleRepoViewSubmodule(ctx *context.Context, submodule *git.SubModule) {
308+
submoduleRepoURL, err := giturl.ParseRepositoryURL(ctx, submodule.URL)
309+
if err != nil {
310+
HandleGitError(ctx, "prepareToRenderDirOrFile: ParseRepositoryURL", err)
311+
return
312+
}
313+
submoduleURL := giturl.MakeRepositoryWebLink(submoduleRepoURL)
314+
if httplib.IsCurrentGiteaSiteURL(ctx, submoduleURL) {
315+
ctx.RedirectToCurrentSite(submoduleURL)
316+
} else {
317+
// don't auto-redirect to external URL, to avoid open redirect or phishing
318+
ctx.Data["NotFoundPrompt"] = submoduleURL
319+
ctx.NotFound(nil)
320+
}
321+
}
322+
305323
func prepareToRenderDirOrFile(entry *git.TreeEntry) func(ctx *context.Context) {
306324
return func(ctx *context.Context) {
325+
if entry.IsSubModule() {
326+
submodule, err := ctx.Repo.Commit.GetSubModule(entry.Name())
327+
if err != nil {
328+
HandleGitError(ctx, "prepareToRenderDirOrFile: GetSubModule", err)
329+
return
330+
}
331+
handleRepoViewSubmodule(ctx, submodule)
332+
return
333+
}
307334
if entry.IsDir() {
308335
prepareToRenderDirectory(ctx)
309336
} else {

Diff for: routers/web/repo/view_home_test.go

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package repo
5+
6+
import (
7+
"net/http"
8+
"testing"
9+
10+
"code.gitea.io/gitea/models/unittest"
11+
git_module "code.gitea.io/gitea/modules/git"
12+
"code.gitea.io/gitea/modules/setting"
13+
"code.gitea.io/gitea/services/contexttest"
14+
15+
"github.com/stretchr/testify/assert"
16+
)
17+
18+
func TestViewHomeSubmoduleRedirect(t *testing.T) {
19+
unittest.PrepareTestEnv(t)
20+
21+
ctx, _ := contexttest.MockContext(t, "/user2/repo1/src/branch/master/test-submodule")
22+
submodule := &git_module.SubModule{Path: "test-submodule", URL: setting.AppURL + "user2/repo-other.git"}
23+
handleRepoViewSubmodule(ctx, submodule)
24+
assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
25+
assert.Equal(t, "/user2/repo-other", ctx.Resp.Header().Get("Location"))
26+
27+
ctx, _ = contexttest.MockContext(t, "/user2/repo1/src/branch/master/test-submodule")
28+
submodule = &git_module.SubModule{Path: "test-submodule", URL: "https://other/user2/repo-other.git"}
29+
handleRepoViewSubmodule(ctx, submodule)
30+
// do not auto-redirect for external URLs, to avoid open redirect or phishing
31+
assert.Equal(t, http.StatusNotFound, ctx.Resp.WrittenStatus())
32+
}

0 commit comments

Comments
 (0)