Use raw Wiki links for non-renderable Wiki files (#30273)

In Wiki pages, short-links created to local Wiki files were always
expanded as regular Wiki Links. In particular, if a link wanted to point
to a file that Gitea doesn't know how to render (e.g, a .zip file), a
user following the link would be silently redirected to the Wiki's home
page.

This change makes short-links* in Wiki pages be expanded to raw wiki
links, so these local wiki files may be accessed without manually
accessing their URL.

* only short-links ending in a file extension that isn't renderable are
affected.

Closes #27121.

Signed-off-by: Rafael Girão <rafael.s.girao@tecnico.ulisboa.pt>
Co-authored-by: silverwind <me@silverwind.io>
pull/30397/head
Rafael 8 months ago committed by GitHub
parent 50099d7af4
commit c1f76aea45
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 22
      modules/markup/html.go
  2. 12
      modules/markup/html_test.go
  3. 24
      modules/markup/markdown/markdown_test.go
  4. 13
      modules/markup/markdown/transform_link.go
  5. 13
      routers/web/repo/wiki_test.go
  6. BIN
      tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/81/a1c039774e337621609336c0e44ed9f92278f7
  7. BIN
      tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/91/dc55f9de16a558e859123f2b99668469b1a1dc
  8. 1
      tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/a5/bbc0fd39a696feabed2d4cccaf05abbcaf3b02
  9. BIN
      tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/cf/19952a40b92eb2f86689146a65ac2d87c0818a
  10. BIN
      tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/e1/6da91326b845f1ba86a7df0a67db352f96dcb0
  11. 2
      tests/gitea-repositories-meta/user2/repo1.wiki.git/refs/heads/master
  12. 1
      tests/integration/git_clone_wiki_test.go

@ -709,7 +709,8 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
name += tail
image := false
switch ext := filepath.Ext(link); ext {
ext := filepath.Ext(link)
switch ext {
// fast path: empty string, ignore
case "":
// leave image as false
@ -767,11 +768,26 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
}
} else {
if !absoluteLink {
var base string
if ctx.IsWiki {
link = util.URLJoin(ctx.Links.WikiLink(), link)
switch ext {
case "":
// no file extension, create a regular wiki link
base = ctx.Links.WikiLink()
default:
// we have a file extension:
// return a regular wiki link if it's a renderable file (extension),
// raw link otherwise
if Type(link) != "" {
base = ctx.Links.WikiLink()
} else {
base = ctx.Links.WikiRawLink()
}
}
} else {
link = util.URLJoin(ctx.Links.SrcLink(), link)
base = ctx.Links.SrcLink()
}
link = util.URLJoin(base, link)
}
childNode.Type = html.TextNode
childNode.Data = name

@ -427,6 +427,10 @@ func TestRender_ShortLinks(t *testing.T) {
otherImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+Other.jpg")
encodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+%23.jpg")
notencodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "some", "path", "Link+#.jpg")
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")
favicon := "http://google.com/favicon.ico"
test(
@ -481,6 +485,14 @@ func TestRender_ShortLinks(t *testing.T) {
"[[Link]] [[Other Link]] [[Link?]]",
`<p><a href="`+url+`" rel="nofollow">Link</a> <a href="`+otherURL+`" rel="nofollow">Other Link</a> <a href="`+encodedURL+`" rel="nofollow">Link?</a></p>`,
`<p><a href="`+urlWiki+`" rel="nofollow">Link</a> <a href="`+otherURLWiki+`" rel="nofollow">Other Link</a> <a href="`+encodedURLWiki+`" rel="nofollow">Link?</a></p>`)
test(
"[[markdown_file.md]]",
`<p><a href="`+renderableFileURL+`" rel="nofollow">markdown_file.md</a></p>`,
`<p><a href="`+renderableFileURLWiki+`" rel="nofollow">markdown_file.md</a></p>`)
test(
"[[file.zip]]",
`<p><a href="`+unrenderableFileURL+`" rel="nofollow">file.zip</a></p>`,
`<p><a href="`+unrenderableFileURLWiki+`" rel="nofollow">file.zip</a></p>`)
test(
"[[Link #.jpg]]",
`<p><a href="`+encodedImgurl+`" rel="nofollow"><img src="`+encodedImgurl+`" title="Link #.jpg" alt="Link #.jpg"/></a></p>`,

@ -653,9 +653,9 @@ space</p>
Expected: `<p>space @mention-user<br/>
/just/a/path.bin<br/>
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
<a href="/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/image.jpg" alt="local image"/></a><br/>
<a href="/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/path/file" alt="local image"/></a><br/>
@ -711,9 +711,9 @@ space</p>
Expected: `<p>space @mention-user<br/>
/just/a/path.bin<br/>
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
<a href="https://gitea.io/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="https://gitea.io/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="https://gitea.io/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="https://gitea.io/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<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/>
<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/>
@ -769,9 +769,9 @@ space</p>
Expected: `<p>space @mention-user<br/>
/just/a/path.bin<br/>
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<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/>
<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/>
@ -829,9 +829,9 @@ space</p>
Expected: `<p>space @mention-user<br/>
/just/a/path.bin<br/>
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<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/>
<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/>
@ -889,9 +889,9 @@ space</p>
Expected: `<p>space @mention-user<br/>
/just/a/path.bin<br/>
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<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/>
<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/>
@ -951,9 +951,9 @@ space</p>
Expected: `<p>space @mention-user<br/>
/just/a/path.bin<br/>
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<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/>
<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/>

@ -4,6 +4,8 @@
package markdown
import (
"path/filepath"
"code.gitea.io/gitea/modules/markup"
giteautil "code.gitea.io/gitea/modules/util"
@ -18,7 +20,16 @@ func (g *ASTTransformer) transformLink(ctx *markup.RenderContext, v *ast.Link, r
if !isAnchorFragment && !markup.IsFullURLBytes(link) {
base := ctx.Links.Base
if ctx.IsWiki {
base = ctx.Links.WikiLink()
if filepath.Ext(string(link)) == "" {
// This link doesn't have a file extension - assume a regular wiki link
base = ctx.Links.WikiLink()
} else if markup.Type(string(link)) != "" {
// If it's a file type we can render, use a regular wiki link
base = ctx.Links.WikiLink()
} else {
// Otherwise, use a raw link instead
base = ctx.Links.WikiRawLink()
}
} else if ctx.Links.HasBranchInfo() {
base = ctx.Links.SrcLink()
}

@ -200,12 +200,13 @@ func TestDeleteWikiPagePost(t *testing.T) {
func TestWikiRaw(t *testing.T) {
for filepath, filetype := range map[string]string{
"jpeg.jpg": "image/jpeg",
"images/jpeg.jpg": "image/jpeg",
"Page With Spaced Name": "text/plain; charset=utf-8",
"Page-With-Spaced-Name": "text/plain; charset=utf-8",
"Page With Spaced Name.md": "", // there is no "Page With Spaced Name.md" in repo
"Page-With-Spaced-Name.md": "text/plain; charset=utf-8",
"jpeg.jpg": "image/jpeg",
"images/jpeg.jpg": "image/jpeg",
"files/Non-Renderable-File.zip": "application/octet-stream",
"Page With Spaced Name": "text/plain; charset=utf-8",
"Page-With-Spaced-Name": "text/plain; charset=utf-8",
"Page With Spaced Name.md": "", // there is no "Page With Spaced Name.md" in repo
"Page-With-Spaced-Name.md": "text/plain; charset=utf-8",
} {
unittest.PrepareTestEnv(t)

@ -0,0 +1 @@
xťŹKnÄ Dłć죱ÁvóEQ˛Ę~nĐ@3Fň đ‰r”\,dŽ<EFBFBD>^”TĄŇSµĎŹGj|žĹK+DÜĘࢠ$2`ĺ4ÇÉY«”Y”u{‡Xho\Źŕ‚u4E;k- P4<EFBFBD>Q^Hł84Ŕđlk.ü†iă_©ü|gţVžv¨Ă=ĚŤüž|Î-U—q8Ú;—ZJµč,ý<EFBFBD>Nnô_<EFBFBD>Ő”ę0Ůaě3ľçýŇż TĐmÄ·ěqă1mÄŘŤb˝ňµµŁ^ÇńžÚzşˇ/ďůŇá_Ö5ŐzR'-'É~étl˝

@ -1 +1 @@
0dca5bd9b5d7ef937710e056f575e86c0184ba85
a5bbc0fd39a696feabed2d4cccaf05abbcaf3b02

@ -45,6 +45,7 @@ func TestRepoCloneWiki(t *testing.T) {
assertFileExist(t, filepath.Join(dstPath, "Page-With-Image.md"))
assertFileExist(t, filepath.Join(dstPath, "Page-With-Spaced-Name.md"))
assertFileExist(t, filepath.Join(dstPath, "images"))
assertFileExist(t, filepath.Join(dstPath, "files/Non-Renderable-File.zip"))
assertFileExist(t, filepath.Join(dstPath, "jpeg.jpg"))
})
})

Loading…
Cancel
Save