You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

html_test.go 25KB

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