mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 19:38:23 +00:00 
			
		
		
		
	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)
This commit is contained in:
		| @@ -20,6 +20,8 @@ import ( | |||||||
| 	unit_model "code.gitea.io/gitea/models/unit" | 	unit_model "code.gitea.io/gitea/models/unit" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/git" | 	"code.gitea.io/gitea/modules/git" | ||||||
|  | 	giturl "code.gitea.io/gitea/modules/git/url" | ||||||
|  | 	"code.gitea.io/gitea/modules/httplib" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	repo_module "code.gitea.io/gitea/modules/repository" | 	repo_module "code.gitea.io/gitea/modules/repository" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| @@ -302,8 +304,33 @@ func handleRepoEmptyOrBroken(ctx *context.Context) { | |||||||
| 	ctx.Redirect(link) | 	ctx.Redirect(link) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func handleRepoViewSubmodule(ctx *context.Context, submodule *git.SubModule) { | ||||||
|  | 	submoduleRepoURL, err := giturl.ParseRepositoryURL(ctx, submodule.URL) | ||||||
|  | 	if err != nil { | ||||||
|  | 		HandleGitError(ctx, "prepareToRenderDirOrFile: ParseRepositoryURL", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	submoduleURL := giturl.MakeRepositoryWebLink(submoduleRepoURL) | ||||||
|  | 	if httplib.IsCurrentGiteaSiteURL(ctx, submoduleURL) { | ||||||
|  | 		ctx.RedirectToCurrentSite(submoduleURL) | ||||||
|  | 	} else { | ||||||
|  | 		// don't auto-redirect to external URL, to avoid open redirect or phishing | ||||||
|  | 		ctx.Data["NotFoundPrompt"] = submoduleURL | ||||||
|  | 		ctx.NotFound(nil) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func prepareToRenderDirOrFile(entry *git.TreeEntry) func(ctx *context.Context) { | func prepareToRenderDirOrFile(entry *git.TreeEntry) func(ctx *context.Context) { | ||||||
| 	return func(ctx *context.Context) { | 	return func(ctx *context.Context) { | ||||||
|  | 		if entry.IsSubModule() { | ||||||
|  | 			submodule, err := ctx.Repo.Commit.GetSubModule(entry.Name()) | ||||||
|  | 			if err != nil { | ||||||
|  | 				HandleGitError(ctx, "prepareToRenderDirOrFile: GetSubModule", err) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			handleRepoViewSubmodule(ctx, submodule) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
| 		if entry.IsDir() { | 		if entry.IsDir() { | ||||||
| 			prepareToRenderDirectory(ctx) | 			prepareToRenderDirectory(ctx) | ||||||
| 		} else { | 		} else { | ||||||
|   | |||||||
							
								
								
									
										32
									
								
								routers/web/repo/view_home_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								routers/web/repo/view_home_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | // Copyright 2025 The Gitea Authors. All rights reserved. | ||||||
|  | // SPDX-License-Identifier: MIT | ||||||
|  |  | ||||||
|  | package repo | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/unittest" | ||||||
|  | 	git_module "code.gitea.io/gitea/modules/git" | ||||||
|  | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | 	"code.gitea.io/gitea/services/contexttest" | ||||||
|  |  | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestViewHomeSubmoduleRedirect(t *testing.T) { | ||||||
|  | 	unittest.PrepareTestEnv(t) | ||||||
|  |  | ||||||
|  | 	ctx, _ := contexttest.MockContext(t, "/user2/repo1/src/branch/master/test-submodule") | ||||||
|  | 	submodule := &git_module.SubModule{Path: "test-submodule", URL: setting.AppURL + "user2/repo-other.git"} | ||||||
|  | 	handleRepoViewSubmodule(ctx, submodule) | ||||||
|  | 	assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus()) | ||||||
|  | 	assert.Equal(t, "/user2/repo-other", ctx.Resp.Header().Get("Location")) | ||||||
|  |  | ||||||
|  | 	ctx, _ = contexttest.MockContext(t, "/user2/repo1/src/branch/master/test-submodule") | ||||||
|  | 	submodule = &git_module.SubModule{Path: "test-submodule", URL: "https://other/user2/repo-other.git"} | ||||||
|  | 	handleRepoViewSubmodule(ctx, submodule) | ||||||
|  | 	// do not auto-redirect for external URLs, to avoid open redirect or phishing | ||||||
|  | 	assert.Equal(t, http.StatusNotFound, ctx.Resp.WrittenStatus()) | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user