From 3fe1f732686989095abd25599c1dc75bda46246d Mon Sep 17 00:00:00 2001 From: Giteabot Date: Sat, 17 Aug 2024 11:19:26 +0800 Subject: [PATCH] Fix raw wiki links (#31825) (#31845) Backport #31825 by @Zettat123 Fix #31395 This regression is introduced by #30273. To find out how GitHub handles this case, I did [some tests](https://github.com/go-gitea/gitea/issues/31395#issuecomment-2278929115). I use redirect in this PR instead of checking if the corresponding `.md` file exists when rendering the link because GitHub also uses redirect. With this PR, there is no need to resolve the raw wiki link when rendering a wiki page. If a wiki link points to a raw file, access will be redirected to the raw link. --------- Co-authored-by: Zettat123 Co-authored-by: yp05327 <576951401@qq.com> Co-authored-by: Lunny Xiao --- modules/markup/html_link.go | 12 ++--- modules/markup/html_test.go | 2 +- modules/markup/markdown/markdown_test.go | 24 ++++----- routers/web/repo/wiki.go | 64 ++++++++++++++++++++---- routers/web/repo/wiki_test.go | 14 ++++++ 5 files changed, 84 insertions(+), 32 deletions(-) diff --git a/modules/markup/html_link.go b/modules/markup/html_link.go index a41b87e9fa..b086135348 100644 --- a/modules/markup/html_link.go +++ b/modules/markup/html_link.go @@ -4,8 +4,6 @@ package markup import ( - "path" - "code.gitea.io/gitea/modules/util" ) @@ -14,13 +12,9 @@ func ResolveLink(ctx *RenderContext, link, userContentAnchorPrefix string) (resu if !isAnchorFragment && !IsFullURLString(link) { linkBase := ctx.Links.Base if ctx.IsWiki { - if ext := path.Ext(link); ext == "" || ext == ".-" { - linkBase = ctx.Links.WikiLink() // the link is for a wiki page - } else if DetectMarkupTypeByFileName(link) != "" { - linkBase = ctx.Links.WikiLink() // the link is renderable as a wiki page - } else { - linkBase = ctx.Links.WikiRawLink() // otherwise, use a raw link instead to view&download medias - } + // no need to check if the link should be resolved as a wiki link or a wiki raw link + // just use wiki link here and it will be redirected to a wiki raw link if necessary + linkBase = ctx.Links.WikiLink() } else if ctx.Links.BranchPath != "" || ctx.Links.TreePath != "" { // if there is no BranchPath, then the link will be something like "/owner/repo/src/{the-file-path}" // and then this link will be handled by the "legacy-ref" code and be redirected to the default branch like "/owner/repo/src/branch/main/{the-file-path}" diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 8911bf3f2e..85ed78a922 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -435,7 +435,7 @@ func TestRender_ShortLinks(t *testing.T) { renderableFileURL := util.URLJoin(tree, "markdown_file.md") renderableFileURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "markdown_file.md") unrenderableFileURL := util.URLJoin(tree, "file.zip") - unrenderableFileURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "file.zip") + unrenderableFileURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "file.zip") favicon := "http://google.com/favicon.ico" test( diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go index d71ca36386..14344d9155 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -655,9 +655,9 @@ space

Expected: `

space @mention-user
/just/a/path.bin
https://example.com/file.bin
-local link
+local link
remote link
-local link
+local link
remote link
local image
local image
@@ -713,9 +713,9 @@ space

Expected: `

space @mention-user
/just/a/path.bin
https://example.com/file.bin
-local link
+local link
remote link
-local link
+local link
remote link
local image
local image
@@ -771,9 +771,9 @@ space

Expected: `

space @mention-user
/just/a/path.bin
https://example.com/file.bin
-local link
+local link
remote link
-local link
+local link
remote link
local image
local image
@@ -831,9 +831,9 @@ space

Expected: `

space @mention-user
/just/a/path.bin
https://example.com/file.bin
-local link
+local link
remote link
-local link
+local link
remote link
local image
local image
@@ -891,9 +891,9 @@ space

Expected: `

space @mention-user
/just/a/path.bin
https://example.com/file.bin
-local link
+local link
remote link
-local link
+local link
remote link
local image
local image
@@ -953,9 +953,9 @@ space

Expected: `

space @mention-user
/just/a/path.bin
https://example.com/file.bin
-local link
+local link
remote link
-local link
+local link
remote link
local image
local image
diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go index 13b6a7b8e3..d2056353d8 100644 --- a/routers/web/repo/wiki.go +++ b/routers/web/repo/wiki.go @@ -138,18 +138,41 @@ func wikiContentsByEntry(ctx *context.Context, entry *git.TreeEntry) []byte { return content } -// wikiContentsByName returns the contents of a wiki page, along with a boolean -// indicating whether the page exists. Writes to ctx if an error occurs. -func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName wiki_service.WebPath) ([]byte, *git.TreeEntry, string, bool) { +// wikiEntryByName returns the entry of a wiki page, along with a boolean +// indicating whether the entry exists. Writes to ctx if an error occurs. +// The last return value indicates whether the file should be returned as a raw file +func wikiEntryByName(ctx *context.Context, commit *git.Commit, wikiName wiki_service.WebPath) (*git.TreeEntry, string, bool, bool) { + isRaw := false gitFilename := wiki_service.WebPathToGitPath(wikiName) entry, err := findEntryForFile(commit, gitFilename) if err != nil && !git.IsErrNotExist(err) { ctx.ServerError("findEntryForFile", err) - return nil, nil, "", false - } else if entry == nil { + return nil, "", false, false + } + if entry == nil { + // check if the file without ".md" suffix exists + gitFilename := strings.TrimSuffix(gitFilename, ".md") + entry, err = findEntryForFile(commit, gitFilename) + if err != nil && !git.IsErrNotExist(err) { + ctx.ServerError("findEntryForFile", err) + return nil, "", false, false + } + isRaw = true + } + if entry == nil { + return nil, "", true, false + } + return entry, gitFilename, false, isRaw +} + +// wikiContentsByName returns the contents of a wiki page, along with a boolean +// indicating whether the page exists. Writes to ctx if an error occurs. +func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName wiki_service.WebPath) ([]byte, *git.TreeEntry, string, bool) { + entry, gitFilename, noEntry, _ := wikiEntryByName(ctx, commit, wikiName) + if entry == nil { return nil, nil, "", true } - return wikiContentsByEntry(ctx, entry), entry, gitFilename, false + return wikiContentsByEntry(ctx, entry), entry, gitFilename, noEntry } func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { @@ -215,11 +238,14 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { isSideBar := pageName == "_Sidebar" isFooter := pageName == "_Footer" - // lookup filename in wiki - get filecontent, gitTree entry , real filename - data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName) + // lookup filename in wiki - get gitTree entry , real filename + entry, pageFilename, noEntry, isRaw := wikiEntryByName(ctx, commit, pageName) if noEntry { ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages") } + if isRaw { + ctx.Redirect(util.URLJoin(ctx.Repo.RepoLink, "wiki/raw", string(pageName))) + } if entry == nil || ctx.Written() { if wikiRepo != nil { wikiRepo.Close() @@ -227,6 +253,15 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { return nil, nil } + // get filecontent + data := wikiContentsByEntry(ctx, entry) + if ctx.Written() { + if wikiRepo != nil { + wikiRepo.Close() + } + return nil, nil + } + var sidebarContent []byte if !isSideBar { sidebarContent, _, _, _ = wikiContentsByName(ctx, commit, "_Sidebar") @@ -442,15 +477,24 @@ func renderEditPage(ctx *context.Context) { ctx.Data["Title"] = displayName ctx.Data["title"] = displayName - // lookup filename in wiki - get filecontent, gitTree entry , real filename - data, entry, _, noEntry := wikiContentsByName(ctx, commit, pageName) + // lookup filename in wiki - gitTree entry , real filename + entry, _, noEntry, isRaw := wikiEntryByName(ctx, commit, pageName) if noEntry { ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages") } + if isRaw { + ctx.Error(http.StatusForbidden, "Editing of raw wiki files is not allowed") + } if entry == nil || ctx.Written() { return } + // get filecontent + data := wikiContentsByEntry(ctx, entry) + if ctx.Written() { + return + } + ctx.Data["content"] = string(data) ctx.Data["sidebarPresent"] = false ctx.Data["sidebarContent"] = "" diff --git a/routers/web/repo/wiki_test.go b/routers/web/repo/wiki_test.go index 4602dcfeb4..86971f15f1 100644 --- a/routers/web/repo/wiki_test.go +++ b/routers/web/repo/wiki_test.go @@ -87,6 +87,13 @@ func TestWiki(t *testing.T) { assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) assert.EqualValues(t, "Home", ctx.Data["Title"]) assertPagesMetas(t, []string{"Home", "Page With Image", "Page With Spaced Name", "Unescaped File"}, ctx.Data["Pages"]) + + ctx, _ = contexttest.MockContext(t, "user2/repo1/jpeg.jpg") + ctx.SetParams("*", "jpeg.jpg") + contexttest.LoadRepo(t, ctx, 1) + Wiki(ctx) + assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) + assert.Equal(t, "/user2/repo1/wiki/raw/jpeg.jpg", ctx.Resp.Header().Get("Location")) } func TestWikiPages(t *testing.T) { @@ -160,6 +167,13 @@ func TestEditWiki(t *testing.T) { assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) assert.EqualValues(t, "Home", ctx.Data["Title"]) assert.Equal(t, wikiContent(t, ctx.Repo.Repository, "Home"), ctx.Data["content"]) + + ctx, _ = contexttest.MockContext(t, "user2/repo1/wiki/jpeg.jpg?action=_edit") + ctx.SetParams("*", "jpeg.jpg") + contexttest.LoadUser(t, ctx, 2) + contexttest.LoadRepo(t, ctx, 1) + EditWiki(ctx) + assert.EqualValues(t, http.StatusForbidden, ctx.Resp.Status()) } func TestEditWikiPost(t *testing.T) {