code.gitea.io/gitea@v1.19.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/modules/emoji" 14 "code.gitea.io/gitea/modules/git" 15 "code.gitea.io/gitea/modules/log" 16 . "code.gitea.io/gitea/modules/markup" 17 "code.gitea.io/gitea/modules/markup/markdown" 18 "code.gitea.io/gitea/modules/setting" 19 "code.gitea.io/gitea/modules/util" 20 21 "github.com/stretchr/testify/assert" 22 ) 23 24 var localMetas = map[string]string{ 25 "user": "gogits", 26 "repo": "gogs", 27 "repoPath": "../../tests/gitea-repositories-meta/user13/repo11.git/", 28 } 29 30 func TestMain(m *testing.M) { 31 setting.InitProviderAllowEmpty() 32 setting.LoadCommonSettings() 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 = TestAppURL 41 test := func(input, expected string) { 42 buffer, err := RenderString(&RenderContext{ 43 Ctx: git.DefaultContext, 44 RelativePath: ".md", 45 URLPrefix: TestRepoURL, 46 Metas: localMetas, 47 }, input) 48 assert.NoError(t, err) 49 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) 50 } 51 52 sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d" 53 repo := TestRepoURL 54 commit := util.URLJoin(repo, "commit", sha) 55 tree := util.URLJoin(repo, "tree", sha, "src") 56 57 file := util.URLJoin(repo, "commit", sha, "example.txt") 58 fileWithExtra := file + ":" 59 fileWithHash := file + "#L2" 60 fileWithHasExtra := file + "#L2:" 61 commitCompare := util.URLJoin(repo, "compare", sha+"..."+sha) 62 commitCompareWithHash := commitCompare + "#L2" 63 64 test(sha, `<p><a href="`+commit+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`) 65 test(sha[:7], `<p><a href="`+commit[:len(commit)-(40-7)]+`" rel="nofollow"><code>65f1bf2</code></a></p>`) 66 test(sha[:39], `<p><a href="`+commit[:len(commit)-(40-39)]+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`) 67 test(commit, `<p><a href="`+commit+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`) 68 test(tree, `<p><a href="`+tree+`" rel="nofollow"><code>65f1bf27bc/src</code></a></p>`) 69 70 test(file, `<p><a href="`+file+`" rel="nofollow"><code>65f1bf27bc/example.txt</code></a></p>`) 71 test(fileWithExtra, `<p><a href="`+file+`" rel="nofollow"><code>65f1bf27bc/example.txt</code></a>:</p>`) 72 test(fileWithHash, `<p><a href="`+fileWithHash+`" rel="nofollow"><code>65f1bf27bc/example.txt (L2)</code></a></p>`) 73 test(fileWithHasExtra, `<p><a href="`+fileWithHash+`" rel="nofollow"><code>65f1bf27bc/example.txt (L2)</code></a>:</p>`) 74 test(commitCompare, `<p><a href="`+commitCompare+`" rel="nofollow"><code>65f1bf27bc...65f1bf27bc</code></a></p>`) 75 test(commitCompareWithHash, `<p><a href="`+commitCompareWithHash+`" rel="nofollow"><code>65f1bf27bc...65f1bf27bc (L2)</code></a></p>`) 76 77 test("commit "+sha, `<p>commit <a href="`+commit+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`) 78 test("/home/gitea/"+sha, "<p>/home/gitea/"+sha+"</p>") 79 test("deadbeef", `<p>deadbeef</p>`) 80 test("d27ace93", `<p>d27ace93</p>`) 81 test(sha[:14]+".x", `<p>`+sha[:14]+`.x</p>`) 82 83 expected14 := `<a href="` + commit[:len(commit)-(40-14)] + `" rel="nofollow"><code>` + sha[:10] + `</code></a>` 84 test(sha[:14]+".", `<p>`+expected14+`.</p>`) 85 test(sha[:14]+",", `<p>`+expected14+`,</p>`) 86 test("["+sha[:14]+"]", `<p>[`+expected14+`]</p>`) 87 } 88 89 func TestRender_CrossReferences(t *testing.T) { 90 setting.AppURL = TestAppURL 91 92 test := func(input, expected string) { 93 buffer, err := RenderString(&RenderContext{ 94 Ctx: git.DefaultContext, 95 RelativePath: "a.md", 96 URLPrefix: setting.AppSubURL, 97 Metas: localMetas, 98 }, input) 99 assert.NoError(t, err) 100 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) 101 } 102 103 test( 104 "gogits/gogs#12345", 105 `<p><a href="`+util.URLJoin(TestAppURL, "gogits", "gogs", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogits/gogs#12345</a></p>`) 106 test( 107 "go-gitea/gitea#12345", 108 `<p><a href="`+util.URLJoin(TestAppURL, "go-gitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">go-gitea/gitea#12345</a></p>`) 109 test( 110 "/home/gitea/go-gitea/gitea#12345", 111 `<p>/home/gitea/go-gitea/gitea#12345</p>`) 112 test( 113 util.URLJoin(TestAppURL, "gogitea", "gitea", "issues", "12345"), 114 `<p><a href="`+util.URLJoin(TestAppURL, "gogitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogitea/gitea#12345</a></p>`) 115 test( 116 util.URLJoin(TestAppURL, "go-gitea", "gitea", "issues", "12345"), 117 `<p><a href="`+util.URLJoin(TestAppURL, "go-gitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">go-gitea/gitea#12345</a></p>`) 118 test( 119 util.URLJoin(TestAppURL, "gogitea", "some-repo-name", "issues", "12345"), 120 `<p><a href="`+util.URLJoin(TestAppURL, "gogitea", "some-repo-name", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogitea/some-repo-name#12345</a></p>`) 121 } 122 123 func TestMisc_IsSameDomain(t *testing.T) { 124 setting.AppURL = TestAppURL 125 126 sha := "b6dd6210eaebc915fd5be5579c58cce4da2e2579" 127 commit := util.URLJoin(TestRepoURL, "commit", sha) 128 129 assert.True(t, IsSameDomain(commit)) 130 assert.False(t, IsSameDomain("http://google.com/ncr")) 131 assert.False(t, IsSameDomain("favicon.ico")) 132 } 133 134 func TestRender_links(t *testing.T) { 135 setting.AppURL = TestAppURL 136 137 test := func(input, expected string) { 138 buffer, err := RenderString(&RenderContext{ 139 Ctx: git.DefaultContext, 140 RelativePath: "a.md", 141 URLPrefix: TestRepoURL, 142 }, input) 143 assert.NoError(t, err) 144 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) 145 } 146 // Text that should be turned into URL 147 148 defaultCustom := setting.Markdown.CustomURLSchemes 149 setting.Markdown.CustomURLSchemes = []string{"ftp", "magnet"} 150 InitializeSanitizer() 151 CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes) 152 153 test( 154 "https://www.example.com", 155 `<p><a href="https://www.example.com" rel="nofollow">https://www.example.com</a></p>`) 156 test( 157 "http://www.example.com", 158 `<p><a href="http://www.example.com" rel="nofollow">http://www.example.com</a></p>`) 159 test( 160 "https://example.com", 161 `<p><a href="https://example.com" rel="nofollow">https://example.com</a></p>`) 162 test( 163 "http://example.com", 164 `<p><a href="http://example.com" rel="nofollow">http://example.com</a></p>`) 165 test( 166 "http://foo.com/blah_blah", 167 `<p><a href="http://foo.com/blah_blah" rel="nofollow">http://foo.com/blah_blah</a></p>`) 168 test( 169 "http://foo.com/blah_blah/", 170 `<p><a href="http://foo.com/blah_blah/" rel="nofollow">http://foo.com/blah_blah/</a></p>`) 171 test( 172 "http://www.example.com/wpstyle/?p=364", 173 `<p><a href="http://www.example.com/wpstyle/?p=364" rel="nofollow">http://www.example.com/wpstyle/?p=364</a></p>`) 174 test( 175 "https://www.example.com/foo/?bar=baz&inga=42&quux", 176 `<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>`) 177 test( 178 "http://142.42.1.1/", 179 `<p><a href="http://142.42.1.1/" rel="nofollow">http://142.42.1.1/</a></p>`) 180 test( 181 "https://github.com/go-gitea/gitea/?p=aaa/bbb.html#ccc-ddd", 182 `<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>`) 183 test( 184 "https://en.wikipedia.org/wiki/URL_(disambiguation)", 185 `<p><a href="https://en.wikipedia.org/wiki/URL_(disambiguation)" rel="nofollow">https://en.wikipedia.org/wiki/URL_(disambiguation)</a></p>`) 186 test( 187 "https://foo_bar.example.com/", 188 `<p><a href="https://foo_bar.example.com/" rel="nofollow">https://foo_bar.example.com/</a></p>`) 189 test( 190 "https://stackoverflow.com/questions/2896191/what-is-go-used-fore", 191 `<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>`) 192 test( 193 "https://username:password@gitea.com", 194 `<p><a href="https://username:password@gitea.com" rel="nofollow">https://username:password@gitea.com</a></p>`) 195 test( 196 "ftp://gitea.com/file.txt", 197 `<p><a href="ftp://gitea.com/file.txt" rel="nofollow">ftp://gitea.com/file.txt</a></p>`) 198 test( 199 "magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download", 200 `<p><a href="magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download" rel="nofollow">magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download</a></p>`) 201 202 // Test that should *not* be turned into URL 203 test( 204 "www.example.com", 205 `<p>www.example.com</p>`) 206 test( 207 "example.com", 208 `<p>example.com</p>`) 209 test( 210 "test.example.com", 211 `<p>test.example.com</p>`) 212 test( 213 "http://", 214 `<p>http://</p>`) 215 test( 216 "https://", 217 `<p>https://</p>`) 218 test( 219 "://", 220 `<p>://</p>`) 221 test( 222 "www", 223 `<p>www</p>`) 224 test( 225 "ftps://gitea.com", 226 `<p>ftps://gitea.com</p>`) 227 228 // Restore previous settings 229 setting.Markdown.CustomURLSchemes = defaultCustom 230 InitializeSanitizer() 231 CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes) 232 } 233 234 func TestRender_email(t *testing.T) { 235 setting.AppURL = TestAppURL 236 237 test := func(input, expected string) { 238 res, err := RenderString(&RenderContext{ 239 Ctx: git.DefaultContext, 240 RelativePath: "a.md", 241 URLPrefix: TestRepoURL, 242 }, input) 243 assert.NoError(t, err) 244 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res)) 245 } 246 // Text that should be turned into email link 247 248 test( 249 "info@gitea.com", 250 `<p><a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a></p>`) 251 test( 252 "(info@gitea.com)", 253 `<p>(<a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a>)</p>`) 254 test( 255 "[info@gitea.com]", 256 `<p>[<a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a>]</p>`) 257 test( 258 "info@gitea.com.", 259 `<p><a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a>.</p>`) 260 test( 261 "firstname+lastname@gitea.com", 262 `<p><a href="mailto:firstname+lastname@gitea.com" rel="nofollow">firstname+lastname@gitea.com</a></p>`) 263 test( 264 "send email to info@gitea.co.uk.", 265 `<p>send email to <a href="mailto:info@gitea.co.uk" rel="nofollow">info@gitea.co.uk</a>.</p>`) 266 267 // Test that should *not* be turned into email links 268 test( 269 "\"info@gitea.com\"", 270 `<p>"info@gitea.com"</p>`) 271 test( 272 "/home/gitea/mailstore/info@gitea/com", 273 `<p>/home/gitea/mailstore/info@gitea/com</p>`) 274 test( 275 "git@try.gitea.io:go-gitea/gitea.git", 276 `<p>git@try.gitea.io:go-gitea/gitea.git</p>`) 277 test( 278 "gitea@3", 279 `<p>gitea@3</p>`) 280 test( 281 "gitea@gmail.c", 282 `<p>gitea@gmail.c</p>`) 283 test( 284 "email@domain@domain.com", 285 `<p>email@domain@domain.com</p>`) 286 test( 287 "email@domain..com", 288 `<p>email@domain..com</p>`) 289 } 290 291 func TestRender_emoji(t *testing.T) { 292 setting.AppURL = TestAppURL 293 setting.StaticURLPrefix = TestAppURL 294 295 test := func(input, expected string) { 296 expected = strings.ReplaceAll(expected, "&", "&") 297 buffer, err := RenderString(&RenderContext{ 298 Ctx: git.DefaultContext, 299 RelativePath: "a.md", 300 URLPrefix: TestRepoURL, 301 }, input) 302 assert.NoError(t, err) 303 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) 304 } 305 306 // Make sure we can successfully match every emoji in our dataset with regex 307 for i := range emoji.GemojiData { 308 test( 309 emoji.GemojiData[i].Emoji, 310 `<p><span class="emoji" aria-label="`+emoji.GemojiData[i].Description+`">`+emoji.GemojiData[i].Emoji+`</span></p>`) 311 } 312 for i := range emoji.GemojiData { 313 test( 314 ":"+emoji.GemojiData[i].Aliases[0]+":", 315 `<p><span class="emoji" aria-label="`+emoji.GemojiData[i].Description+`">`+emoji.GemojiData[i].Emoji+`</span></p>`) 316 } 317 318 // Text that should be turned into or recognized as emoji 319 test( 320 ":gitea:", 321 `<p><span class="emoji" aria-label="gitea"><img alt=":gitea:" src="`+setting.StaticURLPrefix+`/assets/img/emoji/gitea.png"/></span></p>`) 322 test( 323 ":custom-emoji:", 324 `<p>:custom-emoji:</p>`) 325 setting.UI.CustomEmojisMap["custom-emoji"] = ":custom-emoji:" 326 test( 327 ":custom-emoji:", 328 `<p><span class="emoji" aria-label="custom-emoji"><img alt=":custom-emoji:" src="`+setting.StaticURLPrefix+`/assets/img/emoji/custom-emoji.png"/></span></p>`) 329 test( 330 "θΏζ―ε符:1::+1: someπ \U0001f44d:custom-emoji: :gitea:", 331 `<p>θΏζ―ε符:1:<span class="emoji" aria-label="thumbs up">π</span> some<span class="emoji" aria-label="crocodile">π</span> `+ 332 `<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> `+ 333 `<span class="emoji" aria-label="gitea"><img alt=":gitea:" src="`+setting.StaticURLPrefix+`/assets/img/emoji/gitea.png"/></span></p>`) 334 test( 335 "Some text with π in the middle", 336 `<p>Some text with <span class="emoji" aria-label="grinning face with smiling eyes">π</span> in the middle</p>`) 337 test( 338 "Some text with :smile: in the middle", 339 `<p>Some text with <span class="emoji" aria-label="grinning face with smiling eyes">π</span> in the middle</p>`) 340 test( 341 "Some text with ππ 2 emoji next to each other", 342 `<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>`) 343 test( 344 "ππ€ͺππ€β", 345 `<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>`) 346 347 // should match nothing 348 test( 349 "2001:0db8:85a3:0000:0000:8a2e:0370:7334", 350 `<p>2001:0db8:85a3:0000:0000:8a2e:0370:7334</p>`) 351 test( 352 ":not exist:", 353 `<p>:not exist:</p>`) 354 } 355 356 func TestRender_ShortLinks(t *testing.T) { 357 setting.AppURL = TestAppURL 358 tree := util.URLJoin(TestRepoURL, "src", "master") 359 360 test := func(input, expected, expectedWiki string) { 361 buffer, err := markdown.RenderString(&RenderContext{ 362 Ctx: git.DefaultContext, 363 URLPrefix: tree, 364 }, input) 365 assert.NoError(t, err) 366 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) 367 buffer, err = markdown.RenderString(&RenderContext{ 368 Ctx: git.DefaultContext, 369 URLPrefix: TestRepoURL, 370 Metas: localMetas, 371 IsWiki: true, 372 }, input) 373 assert.NoError(t, err) 374 assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(buffer)) 375 } 376 377 rawtree := util.URLJoin(TestRepoURL, "raw", "master") 378 url := util.URLJoin(tree, "Link") 379 otherURL := util.URLJoin(tree, "Other-Link") 380 encodedURL := util.URLJoin(tree, "Link%3F") 381 imgurl := util.URLJoin(rawtree, "Link.jpg") 382 otherImgurl := util.URLJoin(rawtree, "Link+Other.jpg") 383 encodedImgurl := util.URLJoin(rawtree, "Link+%23.jpg") 384 notencodedImgurl := util.URLJoin(rawtree, "some", "path", "Link+#.jpg") 385 urlWiki := util.URLJoin(TestRepoURL, "wiki", "Link") 386 otherURLWiki := util.URLJoin(TestRepoURL, "wiki", "Other-Link") 387 encodedURLWiki := util.URLJoin(TestRepoURL, "wiki", "Link%3F") 388 imgurlWiki := util.URLJoin(TestRepoURL, "wiki", "raw", "Link.jpg") 389 otherImgurlWiki := util.URLJoin(TestRepoURL, "wiki", "raw", "Link+Other.jpg") 390 encodedImgurlWiki := util.URLJoin(TestRepoURL, "wiki", "raw", "Link+%23.jpg") 391 notencodedImgurlWiki := util.URLJoin(TestRepoURL, "wiki", "raw", "some", "path", "Link+#.jpg") 392 favicon := "http://google.com/favicon.ico" 393 394 test( 395 "[[Link]]", 396 `<p><a href="`+url+`" rel="nofollow">Link</a></p>`, 397 `<p><a href="`+urlWiki+`" rel="nofollow">Link</a></p>`) 398 test( 399 "[[Link.jpg]]", 400 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Link.jpg" alt="Link.jpg"/></a></p>`, 401 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="Link.jpg" alt="Link.jpg"/></a></p>`) 402 test( 403 "[["+favicon+"]]", 404 `<p><a href="`+favicon+`" rel="nofollow"><img src="`+favicon+`" title="favicon.ico" alt="`+favicon+`"/></a></p>`, 405 `<p><a href="`+favicon+`" rel="nofollow"><img src="`+favicon+`" title="favicon.ico" alt="`+favicon+`"/></a></p>`) 406 test( 407 "[[Name|Link]]", 408 `<p><a href="`+url+`" rel="nofollow">Name</a></p>`, 409 `<p><a href="`+urlWiki+`" rel="nofollow">Name</a></p>`) 410 test( 411 "[[Name|Link.jpg]]", 412 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Name" alt="Name"/></a></p>`, 413 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="Name" alt="Name"/></a></p>`) 414 test( 415 "[[Name|Link.jpg|alt=AltName]]", 416 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="AltName" alt="AltName"/></a></p>`, 417 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="AltName" alt="AltName"/></a></p>`) 418 test( 419 "[[Name|Link.jpg|title=Title]]", 420 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Title" alt="Title"/></a></p>`, 421 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="Title" alt="Title"/></a></p>`) 422 test( 423 "[[Name|Link.jpg|alt=AltName|title=Title]]", 424 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Title" alt="AltName"/></a></p>`, 425 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="Title" alt="AltName"/></a></p>`) 426 test( 427 "[[Name|Link.jpg|alt=\"AltName\"|title='Title']]", 428 `<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Title" alt="AltName"/></a></p>`, 429 `<p><a href="`+imgurlWiki+`" rel="nofollow"><img src="`+imgurlWiki+`" title="Title" alt="AltName"/></a></p>`) 430 test( 431 "[[Name|Link Other.jpg|alt=\"AltName\"|title='Title']]", 432 `<p><a href="`+otherImgurl+`" rel="nofollow"><img src="`+otherImgurl+`" title="Title" alt="AltName"/></a></p>`, 433 `<p><a href="`+otherImgurlWiki+`" rel="nofollow"><img src="`+otherImgurlWiki+`" title="Title" alt="AltName"/></a></p>`) 434 test( 435 "[[Link]] [[Other Link]]", 436 `<p><a href="`+url+`" rel="nofollow">Link</a> <a href="`+otherURL+`" rel="nofollow">Other Link</a></p>`, 437 `<p><a href="`+urlWiki+`" rel="nofollow">Link</a> <a href="`+otherURLWiki+`" rel="nofollow">Other Link</a></p>`) 438 test( 439 "[[Link?]]", 440 `<p><a href="`+encodedURL+`" rel="nofollow">Link?</a></p>`, 441 `<p><a href="`+encodedURLWiki+`" rel="nofollow">Link?</a></p>`) 442 test( 443 "[[Link]] [[Other Link]] [[Link?]]", 444 `<p><a href="`+url+`" rel="nofollow">Link</a> <a href="`+otherURL+`" rel="nofollow">Other Link</a> <a href="`+encodedURL+`" rel="nofollow">Link?</a></p>`, 445 `<p><a href="`+urlWiki+`" rel="nofollow">Link</a> <a href="`+otherURLWiki+`" rel="nofollow">Other Link</a> <a href="`+encodedURLWiki+`" rel="nofollow">Link?</a></p>`) 446 test( 447 "[[Link #.jpg]]", 448 `<p><a href="`+encodedImgurl+`" rel="nofollow"><img src="`+encodedImgurl+`" title="Link #.jpg" alt="Link #.jpg"/></a></p>`, 449 `<p><a href="`+encodedImgurlWiki+`" rel="nofollow"><img src="`+encodedImgurlWiki+`" title="Link #.jpg" alt="Link #.jpg"/></a></p>`) 450 test( 451 "[[Name|Link #.jpg|alt=\"AltName\"|title='Title']]", 452 `<p><a href="`+encodedImgurl+`" rel="nofollow"><img src="`+encodedImgurl+`" title="Title" alt="AltName"/></a></p>`, 453 `<p><a href="`+encodedImgurlWiki+`" rel="nofollow"><img src="`+encodedImgurlWiki+`" title="Title" alt="AltName"/></a></p>`) 454 test( 455 "[[some/path/Link #.jpg]]", 456 `<p><a href="`+notencodedImgurl+`" rel="nofollow"><img src="`+notencodedImgurl+`" title="Link #.jpg" alt="some/path/Link #.jpg"/></a></p>`, 457 `<p><a href="`+notencodedImgurlWiki+`" rel="nofollow"><img src="`+notencodedImgurlWiki+`" title="Link #.jpg" alt="some/path/Link #.jpg"/></a></p>`) 458 test( 459 "<p><a href=\"https://example.org\">[[foobar]]</a></p>", 460 `<p><a href="https://example.org" rel="nofollow">[[foobar]]</a></p>`, 461 `<p><a href="https://example.org" rel="nofollow">[[foobar]]</a></p>`) 462 } 463 464 func TestRender_RelativeImages(t *testing.T) { 465 setting.AppURL = TestAppURL 466 tree := util.URLJoin(TestRepoURL, "src", "master") 467 468 test := func(input, expected, expectedWiki string) { 469 buffer, err := markdown.RenderString(&RenderContext{ 470 Ctx: git.DefaultContext, 471 URLPrefix: tree, 472 Metas: localMetas, 473 }, input) 474 assert.NoError(t, err) 475 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) 476 buffer, err = markdown.RenderString(&RenderContext{ 477 Ctx: git.DefaultContext, 478 URLPrefix: TestRepoURL, 479 Metas: localMetas, 480 IsWiki: true, 481 }, input) 482 assert.NoError(t, err) 483 assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(buffer)) 484 } 485 486 rawwiki := util.URLJoin(TestRepoURL, "wiki", "raw") 487 mediatree := util.URLJoin(TestRepoURL, "media", "master") 488 489 test( 490 `<img src="Link">`, 491 `<img src="`+util.URLJoin(mediatree, "Link")+`"/>`, 492 `<img src="`+util.URLJoin(rawwiki, "Link")+`"/>`) 493 494 test( 495 `<img src="./icon.png">`, 496 `<img src="`+util.URLJoin(mediatree, "icon.png")+`"/>`, 497 `<img src="`+util.URLJoin(rawwiki, "icon.png")+`"/>`) 498 } 499 500 func Test_ParseClusterFuzz(t *testing.T) { 501 setting.AppURL = TestAppURL 502 503 localMetas := map[string]string{ 504 "user": "go-gitea", 505 "repo": "gitea", 506 } 507 508 data := "<A><maTH><tr><MN><bodY ΓΏ><temPlate></template><tH><tr></A><tH><d<bodY " 509 510 var res strings.Builder 511 err := PostProcess(&RenderContext{ 512 Ctx: git.DefaultContext, 513 URLPrefix: "https://example.com", 514 Metas: localMetas, 515 }, strings.NewReader(data), &res) 516 assert.NoError(t, err) 517 assert.NotContains(t, res.String(), "<html") 518 519 data = "<!DOCTYPE html>\n<A><maTH><tr><MN><bodY ΓΏ><temPlate></template><tH><tr></A><tH><d<bodY " 520 521 res.Reset() 522 err = PostProcess(&RenderContext{ 523 Ctx: git.DefaultContext, 524 URLPrefix: "https://example.com", 525 Metas: localMetas, 526 }, strings.NewReader(data), &res) 527 528 assert.NoError(t, err) 529 assert.NotContains(t, res.String(), "<html") 530 } 531 532 func TestIssue16020(t *testing.T) { 533 setting.AppURL = TestAppURL 534 535 localMetas := map[string]string{ 536 "user": "go-gitea", 537 "repo": "gitea", 538 } 539 540 data := `<img src="data:image/png;base64,i//V"/>` 541 542 var res strings.Builder 543 err := PostProcess(&RenderContext{ 544 Ctx: git.DefaultContext, 545 URLPrefix: "https://example.com", 546 Metas: localMetas, 547 }, strings.NewReader(data), &res) 548 assert.NoError(t, err) 549 assert.Equal(t, data, res.String()) 550 } 551 552 func BenchmarkEmojiPostprocess(b *testing.B) { 553 data := "π₯° " 554 for len(data) < 1<<16 { 555 data += data 556 } 557 b.ResetTimer() 558 for i := 0; i < b.N; i++ { 559 var res strings.Builder 560 err := PostProcess(&RenderContext{ 561 Ctx: git.DefaultContext, 562 URLPrefix: "https://example.com", 563 Metas: localMetas, 564 }, strings.NewReader(data), &res) 565 assert.NoError(b, err) 566 } 567 } 568 569 func TestFuzz(t *testing.T) { 570 s := "t/l/issues/8#/../../a" 571 renderContext := RenderContext{ 572 Ctx: git.DefaultContext, 573 URLPrefix: "https://example.com/go-gitea/gitea", 574 Metas: map[string]string{ 575 "user": "go-gitea", 576 "repo": "gitea", 577 }, 578 } 579 580 err := PostProcess(&renderContext, strings.NewReader(s), io.Discard) 581 582 assert.NoError(t, err) 583 } 584 585 func TestIssue18471(t *testing.T) { 586 data := `http://domain/org/repo/compare/783b039...da951ce` 587 588 var res strings.Builder 589 err := PostProcess(&RenderContext{ 590 Ctx: git.DefaultContext, 591 URLPrefix: "https://example.com", 592 Metas: localMetas, 593 }, strings.NewReader(data), &res) 594 595 assert.NoError(t, err) 596 assert.Equal(t, res.String(), "<a href=\"http://domain/org/repo/compare/783b039...da951ce\" class=\"compare\"><code class=\"nohighlight\">783b039...da951ce</code></a>") 597 }