code.gitea.io/gitea@v1.22.3/modules/markup/html_test.go (about) 1 // Copyright 2017 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package markup_test 5 6 import ( 7 "context" 8 "io" 9 "os" 10 "strings" 11 "testing" 12 13 "code.gitea.io/gitea/models/unittest" 14 "code.gitea.io/gitea/modules/emoji" 15 "code.gitea.io/gitea/modules/git" 16 "code.gitea.io/gitea/modules/log" 17 "code.gitea.io/gitea/modules/markup" 18 "code.gitea.io/gitea/modules/markup/markdown" 19 "code.gitea.io/gitea/modules/setting" 20 "code.gitea.io/gitea/modules/util" 21 22 "github.com/stretchr/testify/assert" 23 ) 24 25 var localMetas = map[string]string{ 26 "user": "gogits", 27 "repo": "gogs", 28 "repoPath": "../../tests/gitea-repositories-meta/user13/repo11.git/", 29 } 30 31 func TestMain(m *testing.M) { 32 unittest.InitSettings() 33 if err := git.InitSimple(context.Background()); err != nil { 34 log.Fatal("git init failed, err: %v", err) 35 } 36 os.Exit(m.Run()) 37 } 38 39 func TestRender_Commits(t *testing.T) { 40 setting.AppURL = markup.TestAppURL 41 test := func(input, expected string) { 42 buffer, err := markup.RenderString(&markup.RenderContext{ 43 Ctx: git.DefaultContext, 44 RelativePath: ".md", 45 Links: markup.Links{ 46 AbsolutePrefix: true, 47 Base: markup.TestRepoURL, 48 }, 49 Metas: localMetas, 50 }, input) 51 assert.NoError(t, err) 52 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) 53 } 54 55 sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d" 56 repo := "http://localhost:3000/gogits/gogs" 57 commit := util.URLJoin(repo, "commit", sha) 58 tree := util.URLJoin(repo, "tree", sha, "src") 59 60 file := util.URLJoin(repo, "commit", sha, "example.txt") 61 fileWithExtra := file + ":" 62 fileWithHash := file + "#L2" 63 fileWithHasExtra := file + "#L2:" 64 commitCompare := util.URLJoin(repo, "compare", sha+"..."+sha) 65 commitCompareWithHash := commitCompare + "#L2" 66 67 test(sha, `<p><a href="`+commit+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`) 68 test(sha[:7], `<p><a href="`+commit[:len(commit)-(40-7)]+`" rel="nofollow"><code>65f1bf2</code></a></p>`) 69 test(sha[:39], `<p><a href="`+commit[:len(commit)-(40-39)]+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`) 70 test(commit, `<p><a href="`+commit+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`) 71 test(tree, `<p><a href="`+tree+`" rel="nofollow"><code>65f1bf27bc/src</code></a></p>`) 72 73 test(file, `<p><a href="`+file+`" rel="nofollow"><code>65f1bf27bc/example.txt</code></a></p>`) 74 test(fileWithExtra, `<p><a href="`+file+`" rel="nofollow"><code>65f1bf27bc/example.txt</code></a>:</p>`) 75 test(fileWithHash, `<p><a href="`+fileWithHash+`" rel="nofollow"><code>65f1bf27bc/example.txt (L2)</code></a></p>`) 76 test(fileWithHasExtra, `<p><a href="`+fileWithHash+`" rel="nofollow"><code>65f1bf27bc/example.txt (L2)</code></a>:</p>`) 77 test(commitCompare, `<p><a href="`+commitCompare+`" rel="nofollow"><code>65f1bf27bc...65f1bf27bc</code></a></p>`) 78 test(commitCompareWithHash, `<p><a href="`+commitCompareWithHash+`" rel="nofollow"><code>65f1bf27bc...65f1bf27bc (L2)</code></a></p>`) 79 80 test("commit "+sha, `<p>commit <a href="`+commit+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`) 81 test("/home/gitea/"+sha, "<p>/home/gitea/"+sha+"</p>") 82 test("deadbeef", `<p>deadbeef</p>`) 83 test("d27ace93", `<p>d27ace93</p>`) 84 test(sha[:14]+".x", `<p>`+sha[:14]+`.x</p>`) 85 86 expected14 := `<a href="` + commit[:len(commit)-(40-14)] + `" rel="nofollow"><code>` + sha[:10] + `</code></a>` 87 test(sha[:14]+".", `<p>`+expected14+`.</p>`) 88 test(sha[:14]+",", `<p>`+expected14+`,</p>`) 89 test("["+sha[:14]+"]", `<p>[`+expected14+`]</p>`) 90 } 91 92 func TestRender_CrossReferences(t *testing.T) { 93 setting.AppURL = markup.TestAppURL 94 95 test := func(input, expected string) { 96 buffer, err := markup.RenderString(&markup.RenderContext{ 97 Ctx: git.DefaultContext, 98 RelativePath: "a.md", 99 Links: markup.Links{ 100 AbsolutePrefix: true, 101 Base: setting.AppSubURL, 102 }, 103 Metas: localMetas, 104 }, input) 105 assert.NoError(t, err) 106 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) 107 } 108 109 test( 110 "test-owner/test-repo#12345", 111 `<p><a href="`+util.URLJoin(markup.TestAppURL, "test-owner", "test-repo", "issues", "12345")+`" class="ref-issue" rel="nofollow">test-owner/test-repo#12345</a></p>`) 112 test( 113 "go-gitea/gitea#12345", 114 `<p><a href="`+util.URLJoin(markup.TestAppURL, "go-gitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">go-gitea/gitea#12345</a></p>`) 115 test( 116 "/home/gitea/go-gitea/gitea#12345", 117 `<p>/home/gitea/go-gitea/gitea#12345</p>`) 118 test( 119 util.URLJoin(markup.TestAppURL, "gogitea", "gitea", "issues", "12345"), 120 `<p><a href="`+util.URLJoin(markup.TestAppURL, "gogitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogitea/gitea#12345</a></p>`) 121 test( 122 util.URLJoin(markup.TestAppURL, "go-gitea", "gitea", "issues", "12345"), 123 `<p><a href="`+util.URLJoin(markup.TestAppURL, "go-gitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">go-gitea/gitea#12345</a></p>`) 124 test( 125 util.URLJoin(markup.TestAppURL, "gogitea", "some-repo-name", "issues", "12345"), 126 `<p><a href="`+util.URLJoin(markup.TestAppURL, "gogitea", "some-repo-name", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogitea/some-repo-name#12345</a></p>`) 127 128 inputURL := "https://host/a/b/commit/0123456789012345678901234567890123456789/foo.txt?a=b#L2-L3" 129 test( 130 inputURL, 131 `<p><a href="`+inputURL+`" rel="nofollow"><code>0123456789/foo.txt (L2-L3)</code></a></p>`) 132 } 133 134 func TestMisc_IsSameDomain(t *testing.T) { 135 setting.AppURL = markup.TestAppURL 136 137 sha := "b6dd6210eaebc915fd5be5579c58cce4da2e2579" 138 commit := util.URLJoin(markup.TestRepoURL, "commit", sha) 139 140 assert.True(t, markup.IsSameDomain(commit)) 141 assert.False(t, markup.IsSameDomain("http://google.com/ncr")) 142 assert.False(t, markup.IsSameDomain("favicon.ico")) 143 } 144 145 func TestRender_links(t *testing.T) { 146 setting.AppURL = markup.TestAppURL 147 148 test := func(input, expected string) { 149 buffer, err := markup.RenderString(&markup.RenderContext{ 150 Ctx: git.DefaultContext, 151 RelativePath: "a.md", 152 Links: markup.Links{ 153 Base: markup.TestRepoURL, 154 }, 155 }, input) 156 assert.NoError(t, err) 157 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) 158 } 159 160 oldCustomURLSchemes := setting.Markdown.CustomURLSchemes 161 markup.ResetDefaultSanitizerForTesting() 162 defer func() { 163 setting.Markdown.CustomURLSchemes = oldCustomURLSchemes 164 markup.ResetDefaultSanitizerForTesting() 165 markup.CustomLinkURLSchemes(oldCustomURLSchemes) 166 }() 167 setting.Markdown.CustomURLSchemes = []string{"ftp", "magnet"} 168 markup.CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes) 169 170 // Text that should be turned into URL 171 test( 172 "https://www.example.com", 173 `<p><a href="https://www.example.com" rel="nofollow">https://www.example.com</a></p>`) 174 test( 175 "http://www.example.com", 176 `<p><a href="http://www.example.com" rel="nofollow">http://www.example.com</a></p>`) 177 test( 178 "https://example.com", 179 `<p><a href="https://example.com" rel="nofollow">https://example.com</a></p>`) 180 test( 181 "http://example.com", 182 `<p><a href="http://example.com" rel="nofollow">http://example.com</a></p>`) 183 test( 184 "http://foo.com/blah_blah", 185 `<p><a href="http://foo.com/blah_blah" rel="nofollow">http://foo.com/blah_blah</a></p>`) 186 test( 187 "http://foo.com/blah_blah/", 188 `<p><a href="http://foo.com/blah_blah/" rel="nofollow">http://foo.com/blah_blah/</a></p>`) 189 test( 190 "http://www.example.com/wpstyle/?p=364", 191 `<p><a href="http://www.example.com/wpstyle/?p=364" rel="nofollow">http://www.example.com/wpstyle/?p=364</a></p>`) 192 test( 193 "https://www.example.com/foo/?bar=baz&inga=42&quux", 194 `<p><a href="https://www.example.com/foo/?bar=baz&inga=42&quux" rel="nofollow">https://www.example.com/foo/?bar=baz&inga=42&quux</a></p>`) 195 test( 196 "http://142.42.1.1/", 197 `<p><a href="http://142.42.1.1/" rel="nofollow">http://142.42.1.1/</a></p>`) 198 test( 199 "https://github.com/go-gitea/gitea/?p=aaa/bbb.html#ccc-ddd", 200 `<p><a href="https://github.com/go-gitea/gitea/?p=aaa/bbb.html#ccc-ddd" rel="nofollow">https://github.com/go-gitea/gitea/?p=aaa/bbb.html#ccc-ddd</a></p>`) 201 test( 202 "https://en.wikipedia.org/wiki/URL_(disambiguation)", 203 `<p><a href="https://en.wikipedia.org/wiki/URL_(disambiguation)" rel="nofollow">https://en.wikipedia.org/wiki/URL_(disambiguation)</a></p>`) 204 test( 205 "https://foo_bar.example.com/", 206 `<p><a href="https://foo_bar.example.com/" rel="nofollow">https://foo_bar.example.com/</a></p>`) 207 test( 208 "https://stackoverflow.com/questions/2896191/what-is-go-used-fore", 209 `<p><a href="https://stackoverflow.com/questions/2896191/what-is-go-used-fore" rel="nofollow">https://stackoverflow.com/questions/2896191/what-is-go-used-fore</a></p>`) 210 test( 211 "https://username:password@gitea.com", 212 `<p><a href="https://username:password@gitea.com" rel="nofollow">https://username:password@gitea.com</a></p>`) 213 test( 214 "ftp://gitea.com/file.txt", 215 `<p><a href="ftp://gitea.com/file.txt" rel="nofollow">ftp://gitea.com/file.txt</a></p>`) 216 test( 217 "magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download", 218 `<p><a href="magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download" rel="nofollow">magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download</a></p>`) 219 test( 220 `[link](https://example.com)`, 221 `<p><a href="https://example.com" rel="nofollow">link</a></p>`) 222 test( 223 `[link](mailto:test@example.com)`, 224 `<p><a href="mailto:test@example.com" rel="nofollow">link</a></p>`) 225 test( 226 `[link](javascript:xss)`, 227 `<p>link</p>`) 228 229 // Test that should *not* be turned into URL 230 test( 231 "www.example.com", 232 `<p>www.example.com</p>`) 233 test( 234 "example.com", 235 `<p>example.com</p>`) 236 test( 237 "test.example.com", 238 `<p>test.example.com</p>`) 239 test( 240 "http://", 241 `<p>http://</p>`) 242 test( 243 "https://", 244 `<p>https://</p>`) 245 test( 246 "://", 247 `<p>://</p>`) 248 test( 249 "www", 250 `<p>www</p>`) 251 test( 252 "ftps://gitea.com", 253 `<p>ftps://gitea.com</p>`) 254 } 255 256 func TestRender_email(t *testing.T) { 257 setting.AppURL = markup.TestAppURL 258 259 test := func(input, expected string) { 260 res, err := markup.RenderString(&markup.RenderContext{ 261 Ctx: git.DefaultContext, 262 RelativePath: "a.md", 263 Links: markup.Links{ 264 Base: markup.TestRepoURL, 265 }, 266 }, input) 267 assert.NoError(t, err) 268 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res)) 269 } 270 // Text that should be turned into email link 271 272 test( 273 "info@gitea.com", 274 `<p><a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a></p>`) 275 test( 276 "(info@gitea.com)", 277 `<p>(<a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a>)</p>`) 278 test( 279 "[info@gitea.com]", 280 `<p>[<a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a>]</p>`) 281 test( 282 "info@gitea.com.", 283 `<p><a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a>.</p>`) 284 test( 285 "firstname+lastname@gitea.com", 286 `<p><a href="mailto:firstname+lastname@gitea.com" rel="nofollow">firstname+lastname@gitea.com</a></p>`) 287 test( 288 "send email to info@gitea.co.uk.", 289 `<p>send email to <a href="mailto:info@gitea.co.uk" rel="nofollow">info@gitea.co.uk</a>.</p>`) 290 291 test( 292 `j.doe@example.com, 293 j.doe@example.com. 294 j.doe@example.com; 295 j.doe@example.com? 296 j.doe@example.com!`, 297 `<p><a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>,<br/> 298 <a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>.<br/> 299 <a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>;<br/> 300 <a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>?<br/> 301 <a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>!</p>`) 302 303 // Test that should *not* be turned into email links 304 test( 305 "\"info@gitea.com\"", 306 `<p>"info@gitea.com"</p>`) 307 test( 308 "/home/gitea/mailstore/info@gitea/com", 309 `<p>/home/gitea/mailstore/info@gitea/com</p>`) 310 test( 311 "git@try.gitea.io:go-gitea/gitea.git", 312 `<p>git@try.gitea.io:go-gitea/gitea.git</p>`) 313 test( 314 "gitea@3", 315 `<p>gitea@3</p>`) 316 test( 317 "gitea@gmail.c", 318 `<p>gitea@gmail.c</p>`) 319 test( 320 "email@domain@domain.com", 321 `<p>email@domain@domain.com</p>`) 322 test( 323 "email@domain..com", 324 `<p>email@domain..com</p>`) 325 } 326 327 func TestRender_emoji(t *testing.T) { 328 setting.AppURL = markup.TestAppURL 329 setting.StaticURLPrefix = markup.TestAppURL 330 331 test := func(input, expected string) { 332 expected = strings.ReplaceAll(expected, "&", "&") 333 buffer, err := markup.RenderString(&markup.RenderContext{ 334 Ctx: git.DefaultContext, 335 RelativePath: "a.md", 336 Links: markup.Links{ 337 Base: markup.TestRepoURL, 338 }, 339 }, input) 340 assert.NoError(t, err) 341 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) 342 } 343 344 // Make sure we can successfully match every emoji in our dataset with regex 345 for i := range emoji.GemojiData { 346 test( 347 emoji.GemojiData[i].Emoji, 348 `<p><span class="emoji" aria-label="`+emoji.GemojiData[i].Description+`">`+emoji.GemojiData[i].Emoji+`</span></p>`) 349 } 350 for i := range emoji.GemojiData { 351 test( 352 ":"+emoji.GemojiData[i].Aliases[0]+":", 353 `<p><span class="emoji" aria-label="`+emoji.GemojiData[i].Description+`">`+emoji.GemojiData[i].Emoji+`</span></p>`) 354 } 355 356 // Text that should be turned into or recognized as emoji 357 test( 358 ":gitea:", 359 `<p><span class="emoji" aria-label="gitea"><img alt=":gitea:" src="`+setting.StaticURLPrefix+`/assets/img/emoji/gitea.png"/></span></p>`) 360 test( 361 ":custom-emoji:", 362 `<p>:custom-emoji:</p>`) 363 setting.UI.CustomEmojisMap["custom-emoji"] = ":custom-emoji:" 364 test( 365 ":custom-emoji:", 366 `<p><span class="emoji" aria-label="custom-emoji"><img alt=":custom-emoji:" src="`+setting.StaticURLPrefix+`/assets/img/emoji/custom-emoji.png"/></span></p>`) 367 test( 368 "θΏζ―ε符:1::+1: someπ \U0001f44d:custom-emoji: :gitea:", 369 `<p>θΏζ―ε符:1:<span class="emoji" aria-label="thumbs up">π</span> some<span class="emoji" aria-label="crocodile">π</span> `+ 370 `<span class="emoji" aria-label="thumbs up">π</span><span class="emoji" aria-label="custom-emoji"><img alt=":custom-emoji:" src="`+setting.StaticURLPrefix+`/assets/img/emoji/custom-emoji.png"/></span> `+ 371 `<span class="emoji" aria-label="gitea"><img alt=":gitea:" src="`+setting.StaticURLPrefix+`/assets/img/emoji/gitea.png"/></span></p>`) 372 test( 373 "Some text with π in the middle", 374 `<p>Some text with <span class="emoji" aria-label="grinning face with smiling eyes">π</span> in the middle</p>`) 375 test( 376 "Some text with :smile: in the middle", 377 `<p>Some text with <span class="emoji" aria-label="grinning face with smiling eyes">π</span> in the middle</p>`) 378 test( 379 "Some text with ππ 2 emoji next to each other", 380 `<p>Some text with <span class="emoji" aria-label="grinning face with smiling eyes">π</span><span class="emoji" aria-label="grinning face with smiling eyes">π</span> 2 emoji next to each other</p>`) 381 test( 382 "ππ€ͺππ€β", 383 `<p><span class="emoji" aria-label="smiling face with sunglasses">π</span><span class="emoji" aria-label="zany face">π€ͺ</span><span class="emoji" aria-label="locked with key">π</span><span class="emoji" aria-label="money-mouth face">π€</span><span class="emoji" aria-label="red question mark">β</span></p>`) 384 385 // should match nothing 386 test( 387 "2001:0db8:85a3:0000:0000:8a2e:0370:7334", 388 `<p>2001:0db8:85a3:0000:0000:8a2e:0370:7334</p>`) 389 test( 390 ":not exist:", 391 `<p>:not exist:</p>`) 392 } 393 394 func TestRender_ShortLinks(t *testing.T) { 395 setting.AppURL = markup.TestAppURL 396 tree := util.URLJoin(markup.TestRepoURL, "src", "master") 397 398 test := func(input, expected, expectedWiki string) { 399 buffer, err := markdown.RenderString(&markup.RenderContext{ 400 Ctx: git.DefaultContext, 401 Links: markup.Links{ 402 Base: markup.TestRepoURL, 403 BranchPath: "master", 404 }, 405 }, input) 406 assert.NoError(t, err) 407 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer))) 408 buffer, err = markdown.RenderString(&markup.RenderContext{ 409 Ctx: git.DefaultContext, 410 Links: markup.Links{ 411 Base: markup.TestRepoURL, 412 }, 413 Metas: localMetas, 414 IsWiki: true, 415 }, input) 416 assert.NoError(t, err) 417 assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(string(buffer))) 418 } 419 420 mediatree := util.URLJoin(markup.TestRepoURL, "media", "master") 421 url := util.URLJoin(tree, "Link") 422 otherURL := util.URLJoin(tree, "Other-Link") 423 encodedURL := util.URLJoin(tree, "Link%3F") 424 imgurl := util.URLJoin(mediatree, "Link.jpg") 425 otherImgurl := util.URLJoin(mediatree, "Link+Other.jpg") 426 encodedImgurl := util.URLJoin(mediatree, "Link+%23.jpg") 427 notencodedImgurl := util.URLJoin(mediatree, "some", "path", "Link+#.jpg") 428 urlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "Link") 429 otherURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "Other-Link") 430 encodedURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "Link%3F") 431 imgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link.jpg") 432 otherImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+Other.jpg") 433 encodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+%23.jpg") 434 notencodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "some", "path", "Link+#.jpg") 435 renderableFileURL := util.URLJoin(tree, "markdown_file.md") 436 renderableFileURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "markdown_file.md") 437 unrenderableFileURL := util.URLJoin(tree, "file.zip") 438 unrenderableFileURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "file.zip") 439 favicon := "http://google.com/favicon.ico" 440 441 test( 442 "[[Link]]", 443 `<p><a href="`+url+`" rel="nofollow">Link</a></p>`, 444 `<p><a href="`+urlWiki+`" rel="nofollow">Link</a></p>`) 445 test( 446 "[[Link.-]]", 447 `<p><a href="http://localhost:3000/test-owner/test-repo/src/master/Link.-" rel="nofollow">Link.-</a></p>`, 448 `<p><a href="http://localhost:3000/test-owner/test-repo/wiki/Link.-" rel="nofollow">Link.-</a></p>`) 449 test( 450 "[[Link.jpg]]", 451 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Link.jpg" alt="Link.jpg"/></a></p>`, 452 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="Link.jpg" alt="Link.jpg"/></a></p>`) 453 test( 454 "[["+favicon+"]]", 455 `<p><a href="`+favicon+`" rel="nofollow"><img src="`+favicon+`" title="favicon.ico" alt="`+favicon+`"/></a></p>`, 456 `<p><a href="`+favicon+`" rel="nofollow"><img src="`+favicon+`" title="favicon.ico" alt="`+favicon+`"/></a></p>`) 457 test( 458 "[[Name|Link]]", 459 `<p><a href="`+url+`" rel="nofollow">Name</a></p>`, 460 `<p><a href="`+urlWiki+`" rel="nofollow">Name</a></p>`) 461 test( 462 "[[Name|Link.jpg]]", 463 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Name" alt="Name"/></a></p>`, 464 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="Name" alt="Name"/></a></p>`) 465 test( 466 "[[Name|Link.jpg|alt=AltName]]", 467 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="AltName" alt="AltName"/></a></p>`, 468 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="AltName" alt="AltName"/></a></p>`) 469 test( 470 "[[Name|Link.jpg|title=Title]]", 471 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Title" alt="Title"/></a></p>`, 472 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="Title" alt="Title"/></a></p>`) 473 test( 474 "[[Name|Link.jpg|alt=AltName|title=Title]]", 475 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Title" alt="AltName"/></a></p>`, 476 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="Title" alt="AltName"/></a></p>`) 477 test( 478 "[[Name|Link.jpg|alt=\"AltName\"|title='Title']]", 479 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Title" alt="AltName"/></a></p>`, 480 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="Title" alt="AltName"/></a></p>`) 481 test( 482 "[[Name|Link Other.jpg|alt=\"AltName\"|title='Title']]", 483 `<p><a href="`+otherImgurl+`" rel="nofollow"><img src="`+otherImgurl+`" title="Title" alt="AltName"/></a></p>`, 484 `<p><a href="`+otherImgurlWiki+`" rel="nofollow"><img src="`+otherImgurlWiki+`" title="Title" alt="AltName"/></a></p>`) 485 test( 486 "[[Link]] [[Other Link]]", 487 `<p><a href="`+url+`" rel="nofollow">Link</a> <a href="`+otherURL+`" rel="nofollow">Other Link</a></p>`, 488 `<p><a href="`+urlWiki+`" rel="nofollow">Link</a> <a href="`+otherURLWiki+`" rel="nofollow">Other Link</a></p>`) 489 test( 490 "[[Link?]]", 491 `<p><a href="`+encodedURL+`" rel="nofollow">Link?</a></p>`, 492 `<p><a href="`+encodedURLWiki+`" rel="nofollow">Link?</a></p>`) 493 test( 494 "[[Link]] [[Other Link]] [[Link?]]", 495 `<p><a href="`+url+`" rel="nofollow">Link</a> <a href="`+otherURL+`" rel="nofollow">Other Link</a> <a href="`+encodedURL+`" rel="nofollow">Link?</a></p>`, 496 `<p><a href="`+urlWiki+`" rel="nofollow">Link</a> <a href="`+otherURLWiki+`" rel="nofollow">Other Link</a> <a href="`+encodedURLWiki+`" rel="nofollow">Link?</a></p>`) 497 test( 498 "[[markdown_file.md]]", 499 `<p><a href="`+renderableFileURL+`" rel="nofollow">markdown_file.md</a></p>`, 500 `<p><a href="`+renderableFileURLWiki+`" rel="nofollow">markdown_file.md</a></p>`) 501 test( 502 "[[file.zip]]", 503 `<p><a href="`+unrenderableFileURL+`" rel="nofollow">file.zip</a></p>`, 504 `<p><a href="`+unrenderableFileURLWiki+`" rel="nofollow">file.zip</a></p>`) 505 test( 506 "[[Link #.jpg]]", 507 `<p><a href="`+encodedImgurl+`" rel="nofollow"><img src="`+encodedImgurl+`" title="Link #.jpg" alt="Link #.jpg"/></a></p>`, 508 `<p><a href="`+encodedImgurlWiki+`" rel="nofollow"><img src="`+encodedImgurlWiki+`" title="Link #.jpg" alt="Link #.jpg"/></a></p>`) 509 test( 510 "[[Name|Link #.jpg|alt=\"AltName\"|title='Title']]", 511 `<p><a href="`+encodedImgurl+`" rel="nofollow"><img src="`+encodedImgurl+`" title="Title" alt="AltName"/></a></p>`, 512 `<p><a href="`+encodedImgurlWiki+`" rel="nofollow"><img src="`+encodedImgurlWiki+`" title="Title" alt="AltName"/></a></p>`) 513 test( 514 "[[some/path/Link #.jpg]]", 515 `<p><a href="`+notencodedImgurl+`" rel="nofollow"><img src="`+notencodedImgurl+`" title="Link #.jpg" alt="some/path/Link #.jpg"/></a></p>`, 516 `<p><a href="`+notencodedImgurlWiki+`" rel="nofollow"><img src="`+notencodedImgurlWiki+`" title="Link #.jpg" alt="some/path/Link #.jpg"/></a></p>`) 517 test( 518 "<p><a href=\"https://example.org\">[[foobar]]</a></p>", 519 `<p><a href="https://example.org" rel="nofollow">[[foobar]]</a></p>`, 520 `<p><a href="https://example.org" rel="nofollow">[[foobar]]</a></p>`) 521 } 522 523 func TestRender_RelativeMedias(t *testing.T) { 524 render := func(input string, isWiki bool, links markup.Links) string { 525 buffer, err := markdown.RenderString(&markup.RenderContext{ 526 Ctx: git.DefaultContext, 527 Links: links, 528 Metas: localMetas, 529 IsWiki: isWiki, 530 }, input) 531 assert.NoError(t, err) 532 return strings.TrimSpace(string(buffer)) 533 } 534 535 out := render(`<img src="LINK">`, false, markup.Links{Base: "/test-owner/test-repo"}) 536 assert.Equal(t, `<a href="/test-owner/test-repo/LINK" target="_blank" rel="nofollow noopener"><img src="/test-owner/test-repo/LINK"/></a>`, out) 537 538 out = render(`<img src="LINK">`, true, markup.Links{Base: "/test-owner/test-repo"}) 539 assert.Equal(t, `<a href="/test-owner/test-repo/wiki/raw/LINK" target="_blank" rel="nofollow noopener"><img src="/test-owner/test-repo/wiki/raw/LINK"/></a>`, out) 540 541 out = render(`<img src="LINK">`, false, markup.Links{Base: "/test-owner/test-repo", BranchPath: "test-branch"}) 542 assert.Equal(t, `<a href="/test-owner/test-repo/media/test-branch/LINK" target="_blank" rel="nofollow noopener"><img src="/test-owner/test-repo/media/test-branch/LINK"/></a>`, out) 543 544 out = render(`<img src="LINK">`, true, markup.Links{Base: "/test-owner/test-repo", BranchPath: "test-branch"}) 545 assert.Equal(t, `<a href="/test-owner/test-repo/wiki/raw/LINK" target="_blank" rel="nofollow noopener"><img src="/test-owner/test-repo/wiki/raw/LINK"/></a>`, out) 546 547 out = render(`<img src="/LINK">`, true, markup.Links{Base: "/test-owner/test-repo", BranchPath: "test-branch"}) 548 assert.Equal(t, `<img src="/LINK"/>`, out) 549 550 out = render(`<video src="LINK">`, false, markup.Links{Base: "/test-owner/test-repo"}) 551 assert.Equal(t, `<video src="/test-owner/test-repo/LINK"></video>`, out) 552 553 out = render(`<video src="LINK">`, true, markup.Links{Base: "/test-owner/test-repo"}) 554 assert.Equal(t, `<video src="/test-owner/test-repo/wiki/raw/LINK"></video>`, out) 555 556 out = render(`<video src="/LINK">`, false, markup.Links{Base: "/test-owner/test-repo"}) 557 assert.Equal(t, `<video src="/LINK"></video>`, out) 558 } 559 560 func Test_ParseClusterFuzz(t *testing.T) { 561 setting.AppURL = markup.TestAppURL 562 563 localMetas := map[string]string{ 564 "user": "go-gitea", 565 "repo": "gitea", 566 } 567 568 data := "<A><maTH><tr><MN><bodY ΓΏ><temPlate></template><tH><tr></A><tH><d<bodY " 569 570 var res strings.Builder 571 err := markup.PostProcess(&markup.RenderContext{ 572 Ctx: git.DefaultContext, 573 Links: markup.Links{ 574 Base: "https://example.com", 575 }, 576 Metas: localMetas, 577 }, strings.NewReader(data), &res) 578 assert.NoError(t, err) 579 assert.NotContains(t, res.String(), "<html") 580 581 data = "<!DOCTYPE html>\n<A><maTH><tr><MN><bodY ΓΏ><temPlate></template><tH><tr></A><tH><d<bodY " 582 583 res.Reset() 584 err = markup.PostProcess(&markup.RenderContext{ 585 Ctx: git.DefaultContext, 586 Links: markup.Links{ 587 Base: "https://example.com", 588 }, 589 Metas: localMetas, 590 }, strings.NewReader(data), &res) 591 592 assert.NoError(t, err) 593 assert.NotContains(t, res.String(), "<html") 594 } 595 596 func TestPostProcess_RenderDocument(t *testing.T) { 597 setting.AppURL = markup.TestAppURL 598 setting.StaticURLPrefix = markup.TestAppURL // can't run standalone 599 600 localMetas := map[string]string{ 601 "user": "go-gitea", 602 "repo": "gitea", 603 "mode": "document", 604 } 605 606 test := func(input, expected string) { 607 var res strings.Builder 608 err := markup.PostProcess(&markup.RenderContext{ 609 Ctx: git.DefaultContext, 610 Links: markup.Links{ 611 AbsolutePrefix: true, 612 Base: "https://example.com", 613 }, 614 Metas: localMetas, 615 }, strings.NewReader(input), &res) 616 assert.NoError(t, err) 617 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res.String())) 618 } 619 620 // Issue index shouldn't be post processing in a document. 621 test( 622 "#1", 623 "#1") 624 625 // But cross-referenced issue index should work. 626 test( 627 "go-gitea/gitea#12345", 628 `<a href="`+util.URLJoin(markup.TestAppURL, "go-gitea", "gitea", "issues", "12345")+`" class="ref-issue">go-gitea/gitea#12345</a>`) 629 630 // Test that other post processing still works. 631 test( 632 ":gitea:", 633 `<span class="emoji" aria-label="gitea"><img alt=":gitea:" src="`+setting.StaticURLPrefix+`/assets/img/emoji/gitea.png"/></span>`) 634 test( 635 "Some text with π in the middle", 636 `Some text with <span class="emoji" aria-label="grinning face with smiling eyes">π</span> in the middle`) 637 test("http://localhost:3000/person/repo/issues/4#issuecomment-1234", 638 `<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234" class="ref-issue">person/repo#4 (comment)</a>`) 639 } 640 641 func TestIssue16020(t *testing.T) { 642 setting.AppURL = markup.TestAppURL 643 644 localMetas := map[string]string{ 645 "user": "go-gitea", 646 "repo": "gitea", 647 } 648 649 data := `<img src="data:image/png;base64,i//V"/>` 650 651 var res strings.Builder 652 err := markup.PostProcess(&markup.RenderContext{ 653 Ctx: git.DefaultContext, 654 Metas: localMetas, 655 }, strings.NewReader(data), &res) 656 assert.NoError(t, err) 657 assert.Equal(t, data, res.String()) 658 } 659 660 func BenchmarkEmojiPostprocess(b *testing.B) { 661 data := "π₯° " 662 for len(data) < 1<<16 { 663 data += data 664 } 665 b.ResetTimer() 666 for i := 0; i < b.N; i++ { 667 var res strings.Builder 668 err := markup.PostProcess(&markup.RenderContext{ 669 Ctx: git.DefaultContext, 670 Metas: localMetas, 671 }, strings.NewReader(data), &res) 672 assert.NoError(b, err) 673 } 674 } 675 676 func TestFuzz(t *testing.T) { 677 s := "t/l/issues/8#/../../a" 678 renderContext := markup.RenderContext{ 679 Ctx: git.DefaultContext, 680 Links: markup.Links{ 681 Base: "https://example.com/go-gitea/gitea", 682 }, 683 Metas: map[string]string{ 684 "user": "go-gitea", 685 "repo": "gitea", 686 }, 687 } 688 689 err := markup.PostProcess(&renderContext, strings.NewReader(s), io.Discard) 690 691 assert.NoError(t, err) 692 } 693 694 func TestIssue18471(t *testing.T) { 695 data := `http://domain/org/repo/compare/783b039...da951ce` 696 697 var res strings.Builder 698 err := markup.PostProcess(&markup.RenderContext{ 699 Ctx: git.DefaultContext, 700 Metas: localMetas, 701 }, strings.NewReader(data), &res) 702 703 assert.NoError(t, err) 704 assert.Equal(t, `<a href="http://domain/org/repo/compare/783b039...da951ce" class="compare"><code class="nohighlight">783b039...da951ce</code></a>`, res.String()) 705 } 706 707 func TestIsFullURL(t *testing.T) { 708 assert.True(t, markup.IsFullURLString("https://example.com")) 709 assert.True(t, markup.IsFullURLString("mailto:test@example.com")) 710 assert.True(t, markup.IsFullURLString("data:image/11111")) 711 assert.False(t, markup.IsFullURLString("/foo:bar")) 712 }