mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 10:56:10 +01:00 
			
		
		
		
	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>
This commit is contained in:
		| @@ -709,7 +709,8 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) { | |||||||
|  |  | ||||||
| 		name += tail | 		name += tail | ||||||
| 		image := false | 		image := false | ||||||
| 		switch ext := filepath.Ext(link); ext { | 		ext := filepath.Ext(link) | ||||||
|  | 		switch ext { | ||||||
| 		// fast path: empty string, ignore | 		// fast path: empty string, ignore | ||||||
| 		case "": | 		case "": | ||||||
| 			// leave image as false | 			// leave image as false | ||||||
| @@ -767,12 +768,27 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) { | |||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			if !absoluteLink { | 			if !absoluteLink { | ||||||
|  | 				var base string | ||||||
| 				if ctx.IsWiki { | 				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 { | 						} else { | ||||||
| 					link = util.URLJoin(ctx.Links.SrcLink(), link) | 							base = ctx.Links.WikiRawLink() | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
|  | 				} else { | ||||||
|  | 					base = ctx.Links.SrcLink() | ||||||
|  | 				} | ||||||
|  | 				link = util.URLJoin(base, link) | ||||||
|  | 			} | ||||||
| 			childNode.Type = html.TextNode | 			childNode.Type = html.TextNode | ||||||
| 			childNode.Data = name | 			childNode.Data = name | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -427,6 +427,10 @@ func TestRender_ShortLinks(t *testing.T) { | |||||||
| 	otherImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+Other.jpg") | 	otherImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+Other.jpg") | ||||||
| 	encodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+%23.jpg") | 	encodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+%23.jpg") | ||||||
| 	notencodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "some", "path", "Link+#.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" | 	favicon := "http://google.com/favicon.ico" | ||||||
|  |  | ||||||
| 	test( | 	test( | ||||||
| @@ -481,6 +485,14 @@ func TestRender_ShortLinks(t *testing.T) { | |||||||
| 		"[[Link]] [[Other Link]] [[Link?]]", | 		"[[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="`+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>`) | 		`<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( | 	test( | ||||||
| 		"[[Link #.jpg]]", | 		"[[Link #.jpg]]", | ||||||
| 		`<p><a href="`+encodedImgurl+`" rel="nofollow"><img src="`+encodedImgurl+`" title="Link #.jpg" alt="Link #.jpg"/></a></p>`, | 		`<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/> | 			Expected: `<p>space @mention-user<br/> | ||||||
| /just/a/path.bin<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://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="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="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/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/> | <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/> | 			Expected: `<p>space @mention-user<br/> | ||||||
| /just/a/path.bin<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://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://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://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/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/> | <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/> | 			Expected: `<p>space @mention-user<br/> | ||||||
| /just/a/path.bin<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://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="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="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/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/> | <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/> | 			Expected: `<p>space @mention-user<br/> | ||||||
| /just/a/path.bin<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://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="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="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/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/> | <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/> | 			Expected: `<p>space @mention-user<br/> | ||||||
| /just/a/path.bin<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://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="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="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/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/> | <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/> | 			Expected: `<p>space @mention-user<br/> | ||||||
| /just/a/path.bin<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://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="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="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/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/> | <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 | package markdown | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"path/filepath" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/modules/markup" | 	"code.gitea.io/gitea/modules/markup" | ||||||
| 	giteautil "code.gitea.io/gitea/modules/util" | 	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) { | 	if !isAnchorFragment && !markup.IsFullURLBytes(link) { | ||||||
| 		base := ctx.Links.Base | 		base := ctx.Links.Base | ||||||
| 		if ctx.IsWiki { | 		if ctx.IsWiki { | ||||||
|  | 			if filepath.Ext(string(link)) == "" { | ||||||
|  | 				// This link doesn't have a file extension - assume a regular wiki link | ||||||
| 				base = ctx.Links.WikiLink() | 				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() { | 		} else if ctx.Links.HasBranchInfo() { | ||||||
| 			base = ctx.Links.SrcLink() | 			base = ctx.Links.SrcLink() | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -202,6 +202,7 @@ func TestWikiRaw(t *testing.T) { | |||||||
| 	for filepath, filetype := range map[string]string{ | 	for filepath, filetype := range map[string]string{ | ||||||
| 		"jpeg.jpg":                      "image/jpeg", | 		"jpeg.jpg":                      "image/jpeg", | ||||||
| 		"images/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":         "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":      "", // there is no "Page With Spaced Name.md" in repo | ||||||
|   | |||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | x<01><>Kn<4B> D<><44>죱<>v<EFBFBD>EQ<45><51>~n<>@3F<33><0B><>r<EFBFBD>\,d<><64>^<5E>T<EFBFBD><54>S<EFBFBD>ϏGj|<7C><>K+D<><44><EFBFBD><01> | ||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -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-Image.md")) | ||||||
| 			assertFileExist(t, filepath.Join(dstPath, "Page-With-Spaced-Name.md")) | 			assertFileExist(t, filepath.Join(dstPath, "Page-With-Spaced-Name.md")) | ||||||
| 			assertFileExist(t, filepath.Join(dstPath, "images")) | 			assertFileExist(t, filepath.Join(dstPath, "images")) | ||||||
|  | 			assertFileExist(t, filepath.Join(dstPath, "files/Non-Renderable-File.zip")) | ||||||
| 			assertFileExist(t, filepath.Join(dstPath, "jpeg.jpg")) | 			assertFileExist(t, filepath.Join(dstPath, "jpeg.jpg")) | ||||||
| 		}) | 		}) | ||||||
| 	}) | 	}) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user