diff --git a/internal/context/notice.go b/internal/context/notice.go index 68a3ce341..d52ff259c 100644 --- a/internal/context/notice.go +++ b/internal/context/notice.go @@ -54,5 +54,5 @@ func (c *Context) renderNoticeBanner() { return } - c.Data["ServerNotice"] = string(markup.RawMarkdown(buf, "")) + c.Data["ServerNotice"] = string(markup.SanitizeBytes(markup.RawMarkdown(buf, ""))) } diff --git a/internal/markup/markdown_test.go b/internal/markup/markdown_test.go index 7a2dcd320..b61ca09e3 100644 --- a/internal/markup/markdown_test.go +++ b/internal/markup/markdown_test.go @@ -73,6 +73,106 @@ func Test_RawMarkdown_AutoLink(t *testing.T) { input: "https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2", want: "

https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2

\n", }, + { + name: "issue URL with single digit", + input: "http://test.com/issues/3", + want: "

http://test.com/issues/3

\n", + }, + { + name: "host without dot in issue-like URL", + input: "http://issues/333", + want: "

http://issues/333

\n", + }, + { + name: "https host without dot in issue-like URL", + input: "https://issues/333", + want: "

https://issues/333

\n", + }, + { + name: "host without dot resembling keyword", + input: "http://tissues/0", + want: "

http://tissues/0

\n", + }, + { + name: "https commit-like URL without dot", + input: "https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae", + want: "

https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae

\n", + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := string(RawMarkdown([]byte(test.input), "")) + assert.Equal(t, test.want, got) + }) + } + + t.Run("cross-repo issue URL from same instance", func(t *testing.T) { + got := string(RawMarkdown([]byte("http://localhost:3000/other/repo/issues/42"), "/user/myrepo")) + assert.Equal(t, "

other/repo#42

\n", got) + }) + + t.Run("same-repo issue URL with fragment", func(t *testing.T) { + got := string(RawMarkdown([]byte("http://localhost:3000/user/myrepo/issues/42#issuecomment-1"), "/user/myrepo")) + assert.Equal(t, "

#42

\n", got) + }) +} + +func Test_RawMarkdown_LinkRewriting(t *testing.T) { + tests := []struct { + name string + input string + urlPrefix string + want string + }{ + { + name: "relative link with path-only prefix", + input: "[text](other-file.md)", + urlPrefix: "/user/repo/src/branch/main", + want: "

text

\n", + }, + { + name: "relative link with absolute URL prefix", + input: "[text](other-file.md)", + urlPrefix: "http://localhost:3000/user/repo/src/branch/main", + want: "

text

\n", + }, + { + name: "absolute link not rewritten", + input: "[text](https://example.com/page)", + urlPrefix: "/user/repo/src/branch/main", + want: "

text

\n", + }, + { + name: "anchor-only link not rewritten", + input: "[text](#section)", + urlPrefix: "/user/repo/src/branch/main", + want: "

text

\n", + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := string(RawMarkdown([]byte(test.input), test.urlPrefix)) + assert.Equal(t, test.want, got) + }) + } +} + +func Test_RawMarkdown_HTMLPassthrough(t *testing.T) { + tests := []struct { + name string + input string + want string + }{ + { + name: "inline HTML tags are stripped", + input: "Hello world", + want: "

Hello world

\n", + }, + { + name: "block HTML tags are stripped", + input: "
content
", + want: "\n", + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) {