// Copyright 2017 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package markup_test import ( "io" "strings" "testing" "code.gitea.io/gitea/modules/emoji" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" testModule "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" ) var ( testRepoOwnerName = "user13" testRepoName = "repo11" localMetas = map[string]string{ "user": testRepoOwnerName, "repo": testRepoName, } localWikiMetas = map[string]string{ "user": testRepoOwnerName, "repo": testRepoName, "markupContentMode": "wiki", } ) type mockRepo struct { OwnerName string RepoName string } func (m *mockRepo) GetOwnerName() string { return m.OwnerName } func (m *mockRepo) GetName() string { return m.RepoName } func newMockRepo(ownerName, repoName string) gitrepo.Repository { return &mockRepo{ OwnerName: ownerName, RepoName: repoName, } } func TestRender_Commits(t *testing.T) { setting.AppURL = markup.TestAppURL test := func(input, expected string) { buffer, err := markup.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, RelativePath: ".md", Links: markup.Links{ AbsolutePrefix: true, Base: markup.TestRepoURL, }, Repo: newMockRepo(testRepoOwnerName, testRepoName), Metas: localMetas, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) } sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d" repo := markup.TestAppURL + testRepoOwnerName + "/" + testRepoName + "/" commit := util.URLJoin(repo, "commit", sha) tree := util.URLJoin(repo, "tree", sha, "src") file := util.URLJoin(repo, "commit", sha, "example.txt") fileWithExtra := file + ":" fileWithHash := file + "#L2" fileWithHasExtra := file + "#L2:" commitCompare := util.URLJoin(repo, "compare", sha+"..."+sha) commitCompareWithHash := commitCompare + "#L2" test(sha, `
`) test(sha[:7], ``) test(sha[:39], ``) test(commit, ``) test(tree, ``) test(file, ``) test(fileWithExtra, ``) test(fileWithHash, ``) test(fileWithHasExtra, ``) test(commitCompare, ``) test(commitCompareWithHash, ``) test("commit "+sha, `commit 65f1bf27bc
/home/gitea/"+sha+"
") test("deadbeef", `deadbeef
`) test("d27ace93", `d27ace93
`) test(sha[:14]+".x", ``+sha[:14]+`.x
`) expected14 := `` + sha[:10] + `
`
test(sha[:14]+".", ``+expected14+`.
`) test(sha[:14]+",", ``+expected14+`,
`) test("["+sha[:14]+"]", `[`+expected14+`]
`) } func TestRender_CrossReferences(t *testing.T) { setting.AppURL = markup.TestAppURL defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)() test := func(input, expected string) { buffer, err := markup.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, RelativePath: "a.md", Links: markup.Links{ AbsolutePrefix: true, Base: setting.AppSubURL, }, Metas: localMetas, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) } test( "test-owner/test-repo#12345", ``) test( "go-gitea/gitea#12345", ``) test( "/home/gitea/go-gitea/gitea#12345", `/home/gitea/go-gitea/gitea#12345
`) test( util.URLJoin(markup.TestAppURL, "gogitea", "gitea", "issues", "12345"), ``) test( util.URLJoin(markup.TestAppURL, "go-gitea", "gitea", "issues", "12345"), ``) test( util.URLJoin(markup.TestAppURL, "gogitea", "some-repo-name", "issues", "12345"), ``) inputURL := "https://host/a/b/commit/0123456789012345678901234567890123456789/foo.txt?a=b#L2-L3" test( inputURL, ``) } func TestRender_links(t *testing.T) { setting.AppURL = markup.TestAppURL defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)() test := func(input, expected string) { buffer, err := markup.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, RelativePath: "a.md", Links: markup.Links{ Base: markup.TestRepoURL, }, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) } oldCustomURLSchemes := setting.Markdown.CustomURLSchemes markup.ResetDefaultSanitizerForTesting() defer func() { setting.Markdown.CustomURLSchemes = oldCustomURLSchemes markup.ResetDefaultSanitizerForTesting() markup.CustomLinkURLSchemes(oldCustomURLSchemes) }() setting.Markdown.CustomURLSchemes = []string{"ftp", "magnet"} markup.CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes) // Text that should be turned into URL test( "https://www.example.com", ``) test( "http://www.example.com", ``) test( "https://example.com", ``) test( "http://example.com", ``) test( "http://foo.com/blah_blah", ``) test( "http://foo.com/blah_blah/", ``) test( "http://www.example.com/wpstyle/?p=364", `http://www.example.com/wpstyle/?p=364
`) test( "https://www.example.com/foo/?bar=baz&inga=42&quux", `https://www.example.com/foo/?bar=baz&inga=42&quux
`) test( "http://142.42.1.1/", ``) test( "https://github.com/go-gitea/gitea/?p=aaa/bbb.html#ccc-ddd", `https://github.com/go-gitea/gitea/?p=aaa/bbb.html#ccc-ddd
`) test( "https://en.wikipedia.org/wiki/URL_(disambiguation)", `https://en.wikipedia.org/wiki/URL_(disambiguation)
`) test( "https://foo_bar.example.com/", ``) test( "https://stackoverflow.com/questions/2896191/what-is-go-used-fore", `https://stackoverflow.com/questions/2896191/what-is-go-used-fore
`) test( "https://username:password@gitea.com", `https://username:password@gitea.com
`) test( "ftp://gitea.com/file.txt", ``) test( "magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download", `magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download
`) test( `[link](https://example.com)`, ``) test( `[link](mailto:test@example.com)`, ``) test( `[link](javascript:xss)`, `link
`) // Test that should *not* be turned into URL test( "www.example.com", `www.example.com
`) test( "example.com", `example.com
`) test( "test.example.com", `test.example.com
`) test( "http://", `http://
`) test( "https://", `https://
`) test( "://", `://
`) test( "www", `www
`) test( "ftps://gitea.com", `ftps://gitea.com
`) } func TestRender_email(t *testing.T) { setting.AppURL = markup.TestAppURL defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)() test := func(input, expected string) { res, err := markup.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, RelativePath: "a.md", Links: markup.Links{ Base: markup.TestRepoURL, }, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res)) } // Text that should be turned into email link test( "info@gitea.com", ``) test( "(info@gitea.com)", ``) test( "[info@gitea.com]", ``) test( "info@gitea.com.", ``) test( "firstname+lastname@gitea.com", ``) test( "send email to info@gitea.co.uk.", `send email to info@gitea.co.uk.
`) test( `j.doe@example.com, j.doe@example.com. j.doe@example.com; j.doe@example.com? j.doe@example.com!`, `j.doe@example.com, j.doe@example.com. j.doe@example.com; j.doe@example.com? j.doe@example.com!
`) // Test that should *not* be turned into email links test( "\"info@gitea.com\"", `"info@gitea.com"
`) test( "/home/gitea/mailstore/info@gitea/com", `/home/gitea/mailstore/info@gitea/com
`) test( "git@try.gitea.io:go-gitea/gitea.git", `git@try.gitea.io:go-gitea/gitea.git
`) test( "gitea@3", `gitea@3
`) test( "gitea@gmail.c", `gitea@gmail.c
`) test( "email@domain@domain.com", `email@domain@domain.com
`) test( "email@domain..com", `email@domain..com
`) } func TestRender_emoji(t *testing.T) { setting.AppURL = markup.TestAppURL setting.StaticURLPrefix = markup.TestAppURL test := func(input, expected string) { expected = strings.ReplaceAll(expected, "&", "&") buffer, err := markup.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, RelativePath: "a.md", Links: markup.Links{ Base: markup.TestRepoURL, }, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) } // Make sure we can successfully match every emoji in our dataset with regex for i := range emoji.GemojiData { test( emoji.GemojiData[i].Emoji, ``+emoji.GemojiData[i].Emoji+`
`) } for i := range emoji.GemojiData { test( ":"+emoji.GemojiData[i].Aliases[0]+":", ``+emoji.GemojiData[i].Emoji+`
`) } // Text that should be turned into or recognized as emoji test( ":gitea:", ``) test( ":custom-emoji:", `
:custom-emoji:
`) setting.UI.CustomEmojisMap["custom-emoji"] = ":custom-emoji:" test( ":custom-emoji:", ``) test( "θΏζ―ε符:1::+1: someπ \U0001f44d:custom-emoji: :gitea:", `
θΏζ―ε符:1:π someπ `+ `π `+ `
`) test( "Some text with π in the middle", `Some text with π in the middle
`) test( "Some text with :smile: in the middle", `Some text with π in the middle
`) test( "Some text with ππ 2 emoji next to each other", `Some text with ππ 2 emoji next to each other
`) test( "ππ€ͺππ€β", `ππ€ͺππ€β
`) // should match nothing test( "2001:0db8:85a3:0000:0000:8a2e:0370:7334", `2001:0db8:85a3:0000:0000:8a2e:0370:7334
`) test( ":not exist:", `:not exist:
`) } func TestRender_ShortLinks(t *testing.T) { setting.AppURL = markup.TestAppURL tree := util.URLJoin(markup.TestRepoURL, "src", "master") test := func(input, expected, expectedWiki string) { buffer, err := markdown.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, Links: markup.Links{ Base: markup.TestRepoURL, BranchPath: "master", }, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer))) buffer, err = markdown.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, Links: markup.Links{ Base: markup.TestRepoURL, }, Metas: localWikiMetas, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(string(buffer))) } mediatree := util.URLJoin(markup.TestRepoURL, "media", "master") url := util.URLJoin(tree, "Link") otherURL := util.URLJoin(tree, "Other-Link") encodedURL := util.URLJoin(tree, "Link%3F") imgurl := util.URLJoin(mediatree, "Link.jpg") otherImgurl := util.URLJoin(mediatree, "Link+Other.jpg") encodedImgurl := util.URLJoin(mediatree, "Link+%23.jpg") notencodedImgurl := util.URLJoin(mediatree, "some", "path", "Link+#.jpg") urlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "Link") otherURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "Other-Link") encodedURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "Link%3F") imgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link.jpg") otherImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+Other.jpg") encodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+%23.jpg") notencodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "some", "path", "Link+#.jpg") renderableFileURL := util.URLJoin(tree, "markdown_file.md") renderableFileURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "markdown_file.md") unrenderableFileURL := util.URLJoin(tree, "file.zip") unrenderableFileURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "file.zip") favicon := "http://google.com/favicon.ico" test( "[[Link]]", ``, ``) test( "[[Link.-]]", ``, ``) test( "[[Link.jpg]]", ``, ``) test( "[["+favicon+"]]", ``, ``) test( "[[Name|Link]]", ``, ``) test( "[[Name|Link.jpg]]", ``, ``) test( "[[Name|Link.jpg|alt=AltName]]", ``, ``) test( "[[Name|Link.jpg|title=Title]]", ``, ``) test( "[[Name|Link.jpg|alt=AltName|title=Title]]", ``, ``) test( "[[Name|Link.jpg|alt=\"AltName\"|title='Title']]", ``, ``) test( "[[Name|Link Other.jpg|alt=\"AltName\"|title='Title']]", ``, ``) test( "[[Link]] [[Other Link]]", ``, ``) test( "[[Link?]]", ``, ``) test( "[[Link]] [[Other Link]] [[Link?]]", ``, ``) test( "[[markdown_file.md]]", ``, ``) test( "[[file.zip]]", ``, ``) test( "[[Link #.jpg]]", ``, ``) test( "[[Name|Link #.jpg|alt=\"AltName\"|title='Title']]", ``, ``) test( "[[some/path/Link #.jpg]]", ``, ``) test( "", ``, ``) } func TestRender_RelativeMedias(t *testing.T) { render := func(input string, isWiki bool, links markup.Links) string { buffer, err := markdown.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, Links: links, Metas: util.Iif(isWiki, localWikiMetas, localMetas), }, input) assert.NoError(t, err) return strings.TrimSpace(string(buffer)) } out := render(``, false, markup.Links{Base: "/test-owner/test-repo"}) assert.Equal(t, ``, out) out = render(``, true, markup.Links{Base: "/test-owner/test-repo"}) assert.Equal(t, ``, out) out = render(``, false, markup.Links{Base: "/test-owner/test-repo", BranchPath: "test-branch"}) assert.Equal(t, ``, out) out = render(``, true, markup.Links{Base: "/test-owner/test-repo", BranchPath: "test-branch"}) assert.Equal(t, ``, out) out = render(``, true, markup.Links{Base: "/test-owner/test-repo", BranchPath: "test-branch"}) assert.Equal(t, ``, out) out = render(`