Skip to content

Commit 2010fbe

Browse files
authored
Fix raw wiki links (#31825)
Fix #31395 This regression is introduced by #30273. To find out how GitHub handles this case, I did [some tests](#31395 (comment)). 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.
1 parent e4f850b commit 2010fbe

File tree

5 files changed

+84
-32
lines changed

5 files changed

+84
-32
lines changed

modules/markup/html_link.go

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
package markup
55

66
import (
7-
"path"
8-
97
"code.gitea.io/gitea/modules/util"
108
)
119

@@ -14,13 +12,9 @@ func ResolveLink(ctx *RenderContext, link, userContentAnchorPrefix string) (resu
1412
if !isAnchorFragment && !IsFullURLString(link) {
1513
linkBase := ctx.Links.Base
1614
if ctx.IsWiki {
17-
if ext := path.Ext(link); ext == "" || ext == ".-" {
18-
linkBase = ctx.Links.WikiLink() // the link is for a wiki page
19-
} else if DetectMarkupTypeByFileName(link) != "" {
20-
linkBase = ctx.Links.WikiLink() // the link is renderable as a wiki page
21-
} else {
22-
linkBase = ctx.Links.WikiRawLink() // otherwise, use a raw link instead to view&download medias
23-
}
15+
// no need to check if the link should be resolved as a wiki link or a wiki raw link
16+
// just use wiki link here and it will be redirected to a wiki raw link if necessary
17+
linkBase = ctx.Links.WikiLink()
2418
} else if ctx.Links.BranchPath != "" || ctx.Links.TreePath != "" {
2519
// if there is no BranchPath, then the link will be something like "/owner/repo/src/{the-file-path}"
2620
// 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}"

modules/markup/html_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ func TestRender_ShortLinks(t *testing.T) {
437437
renderableFileURL := util.URLJoin(tree, "markdown_file.md")
438438
renderableFileURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "markdown_file.md")
439439
unrenderableFileURL := util.URLJoin(tree, "file.zip")
440-
unrenderableFileURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "file.zip")
440+
unrenderableFileURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "file.zip")
441441
favicon := "http://google.com/favicon.ico"
442442

443443
test(

modules/markup/markdown/markdown_test.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -672,9 +672,9 @@ space</p>
672672
Expected: `<p>space @mention-user<br/>
673673
/just/a/path.bin<br/>
674674
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
675-
<a href="/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
675+
<a href="/wiki/file.bin" rel="nofollow">local link</a><br/>
676676
<a href="https://example.com" rel="nofollow">remote link</a><br/>
677-
<a href="/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
677+
<a href="/wiki/file.bin" rel="nofollow">local link</a><br/>
678678
<a href="https://example.com" rel="nofollow">remote link</a><br/>
679679
<a href="/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/image.jpg" alt="local image"/></a><br/>
680680
<a href="/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/path/file" alt="local image"/></a><br/>
@@ -730,9 +730,9 @@ space</p>
730730
Expected: `<p>space @mention-user<br/>
731731
/just/a/path.bin<br/>
732732
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
733-
<a href="https://gitea.io/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
733+
<a href="https://gitea.io/wiki/file.bin" rel="nofollow">local link</a><br/>
734734
<a href="https://example.com" rel="nofollow">remote link</a><br/>
735-
<a href="https://gitea.io/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
735+
<a href="https://gitea.io/wiki/file.bin" rel="nofollow">local link</a><br/>
736736
<a href="https://example.com" rel="nofollow">remote link</a><br/>
737737
<a href="https://gitea.io/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/wiki/raw/image.jpg" alt="local image"/></a><br/>
738738
<a href="https://gitea.io/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/wiki/raw/path/file" alt="local image"/></a><br/>
@@ -788,9 +788,9 @@ space</p>
788788
Expected: `<p>space @mention-user<br/>
789789
/just/a/path.bin<br/>
790790
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
791-
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
791+
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
792792
<a href="https://example.com" rel="nofollow">remote link</a><br/>
793-
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
793+
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
794794
<a href="https://example.com" rel="nofollow">remote link</a><br/>
795795
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/>
796796
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/>
@@ -848,9 +848,9 @@ space</p>
848848
Expected: `<p>space @mention-user<br/>
849849
/just/a/path.bin<br/>
850850
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
851-
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
851+
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
852852
<a href="https://example.com" rel="nofollow">remote link</a><br/>
853-
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
853+
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
854854
<a href="https://example.com" rel="nofollow">remote link</a><br/>
855855
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/>
856856
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/>
@@ -908,9 +908,9 @@ space</p>
908908
Expected: `<p>space @mention-user<br/>
909909
/just/a/path.bin<br/>
910910
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
911-
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
911+
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
912912
<a href="https://example.com" rel="nofollow">remote link</a><br/>
913-
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
913+
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
914914
<a href="https://example.com" rel="nofollow">remote link</a><br/>
915915
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/>
916916
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/>
@@ -970,9 +970,9 @@ space</p>
970970
Expected: `<p>space @mention-user<br/>
971971
/just/a/path.bin<br/>
972972
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
973-
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
973+
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
974974
<a href="https://example.com" rel="nofollow">remote link</a><br/>
975-
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
975+
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
976976
<a href="https://example.com" rel="nofollow">remote link</a><br/>
977977
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/>
978978
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/>

routers/web/repo/wiki.go

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -138,18 +138,41 @@ func wikiContentsByEntry(ctx *context.Context, entry *git.TreeEntry) []byte {
138138
return content
139139
}
140140

141-
// wikiContentsByName returns the contents of a wiki page, along with a boolean
142-
// indicating whether the page exists. Writes to ctx if an error occurs.
143-
func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName wiki_service.WebPath) ([]byte, *git.TreeEntry, string, bool) {
141+
// wikiEntryByName returns the entry of a wiki page, along with a boolean
142+
// indicating whether the entry exists. Writes to ctx if an error occurs.
143+
// The last return value indicates whether the file should be returned as a raw file
144+
func wikiEntryByName(ctx *context.Context, commit *git.Commit, wikiName wiki_service.WebPath) (*git.TreeEntry, string, bool, bool) {
145+
isRaw := false
144146
gitFilename := wiki_service.WebPathToGitPath(wikiName)
145147
entry, err := findEntryForFile(commit, gitFilename)
146148
if err != nil && !git.IsErrNotExist(err) {
147149
ctx.ServerError("findEntryForFile", err)
148-
return nil, nil, "", false
149-
} else if entry == nil {
150+
return nil, "", false, false
151+
}
152+
if entry == nil {
153+
// check if the file without ".md" suffix exists
154+
gitFilename := strings.TrimSuffix(gitFilename, ".md")
155+
entry, err = findEntryForFile(commit, gitFilename)
156+
if err != nil && !git.IsErrNotExist(err) {
157+
ctx.ServerError("findEntryForFile", err)
158+
return nil, "", false, false
159+
}
160+
isRaw = true
161+
}
162+
if entry == nil {
163+
return nil, "", true, false
164+
}
165+
return entry, gitFilename, false, isRaw
166+
}
167+
168+
// wikiContentsByName returns the contents of a wiki page, along with a boolean
169+
// indicating whether the page exists. Writes to ctx if an error occurs.
170+
func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName wiki_service.WebPath) ([]byte, *git.TreeEntry, string, bool) {
171+
entry, gitFilename, noEntry, _ := wikiEntryByName(ctx, commit, wikiName)
172+
if entry == nil {
150173
return nil, nil, "", true
151174
}
152-
return wikiContentsByEntry(ctx, entry), entry, gitFilename, false
175+
return wikiContentsByEntry(ctx, entry), entry, gitFilename, noEntry
153176
}
154177

155178
func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
@@ -215,18 +238,30 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
215238
isSideBar := pageName == "_Sidebar"
216239
isFooter := pageName == "_Footer"
217240

218-
// lookup filename in wiki - get filecontent, gitTree entry , real filename
219-
data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
241+
// lookup filename in wiki - get gitTree entry , real filename
242+
entry, pageFilename, noEntry, isRaw := wikiEntryByName(ctx, commit, pageName)
220243
if noEntry {
221244
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
222245
}
246+
if isRaw {
247+
ctx.Redirect(util.URLJoin(ctx.Repo.RepoLink, "wiki/raw", string(pageName)))
248+
}
223249
if entry == nil || ctx.Written() {
224250
if wikiRepo != nil {
225251
wikiRepo.Close()
226252
}
227253
return nil, nil
228254
}
229255

256+
// get filecontent
257+
data := wikiContentsByEntry(ctx, entry)
258+
if ctx.Written() {
259+
if wikiRepo != nil {
260+
wikiRepo.Close()
261+
}
262+
return nil, nil
263+
}
264+
230265
var sidebarContent []byte
231266
if !isSideBar {
232267
sidebarContent, _, _, _ = wikiContentsByName(ctx, commit, "_Sidebar")
@@ -442,15 +477,24 @@ func renderEditPage(ctx *context.Context) {
442477
ctx.Data["Title"] = displayName
443478
ctx.Data["title"] = displayName
444479

445-
// lookup filename in wiki - get filecontent, gitTree entry , real filename
446-
data, entry, _, noEntry := wikiContentsByName(ctx, commit, pageName)
480+
// lookup filename in wiki - gitTree entry , real filename
481+
entry, _, noEntry, isRaw := wikiEntryByName(ctx, commit, pageName)
447482
if noEntry {
448483
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
449484
}
485+
if isRaw {
486+
ctx.Error(http.StatusForbidden, "Editing of raw wiki files is not allowed")
487+
}
450488
if entry == nil || ctx.Written() {
451489
return
452490
}
453491

492+
// get filecontent
493+
data := wikiContentsByEntry(ctx, entry)
494+
if ctx.Written() {
495+
return
496+
}
497+
454498
ctx.Data["content"] = string(data)
455499
ctx.Data["sidebarPresent"] = false
456500
ctx.Data["sidebarContent"] = ""

routers/web/repo/wiki_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ func TestWiki(t *testing.T) {
8787
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
8888
assert.EqualValues(t, "Home", ctx.Data["Title"])
8989
assertPagesMetas(t, []string{"Home", "Page With Image", "Page With Spaced Name", "Unescaped File"}, ctx.Data["Pages"])
90+
91+
ctx, _ = contexttest.MockContext(t, "user2/repo1/jpeg.jpg")
92+
ctx.SetPathParam("*", "jpeg.jpg")
93+
contexttest.LoadRepo(t, ctx, 1)
94+
Wiki(ctx)
95+
assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
96+
assert.Equal(t, "/user2/repo1/wiki/raw/jpeg.jpg", ctx.Resp.Header().Get("Location"))
9097
}
9198

9299
func TestWikiPages(t *testing.T) {
@@ -160,6 +167,13 @@ func TestEditWiki(t *testing.T) {
160167
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
161168
assert.EqualValues(t, "Home", ctx.Data["Title"])
162169
assert.Equal(t, wikiContent(t, ctx.Repo.Repository, "Home"), ctx.Data["content"])
170+
171+
ctx, _ = contexttest.MockContext(t, "user2/repo1/wiki/jpeg.jpg?action=_edit")
172+
ctx.SetPathParam("*", "jpeg.jpg")
173+
contexttest.LoadUser(t, ctx, 2)
174+
contexttest.LoadRepo(t, ctx, 1)
175+
EditWiki(ctx)
176+
assert.EqualValues(t, http.StatusForbidden, ctx.Resp.Status())
163177
}
164178

165179
func TestEditWikiPost(t *testing.T) {

0 commit comments

Comments
 (0)