Compare commits

...

4 Commits

Author SHA1 Message Date
JSS
997011bfb2 markup: expand test coverage and sanitize notice banner output
Add previously covered autolink test cases that were dropped during the
goldmark migration (hosts without dots, https variants, single-digit
issues, cross-repo issues). Add new test suites for link rewriting with
both path-only and absolute URL prefixes, and for HTML passthrough
behavior confirming raw HTML is stripped without WithUnsafe.

Sanitize RawMarkdown output in the server notice banner to prevent
potential XSS, since it was the only call site not passing through
SanitizeBytes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 08:49:17 -05:00
JSS
60f62b6583 markup: address review feedback for goldmark migration
- Use lazyregexp for linkifyURLRegexp to avoid compile-at-init overhead.
- Replace stdlib log with clog/v2 and return HTML-escaped body on
  conversion error instead of nil.
- Handle absolute URL prefixes in linkTransformer using net/url to
  preserve scheme and host.
- Remove goldmarkhtml.WithUnsafe() from RawMarkdown renderer options.
- Use exact assertions in autolink tests instead of Contains.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 23:28:15 -05:00
deepsource-autofix[bot]
3b7e331191 style: format code with Go fmt and Gofumpt
This commit fixes the style issues introduced in f9b4c5a according to the output
from Go fmt and Gofumpt.

Details: https://github.com/gogs/gogs/pull/8163
2026-02-08 05:48:28 +00:00
Joe Chen
f9b4c5a3ff markup: migrate from blackfriday to goldmark
Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019c3baf-c434-7794-9efd-084363bad1a2
2026-02-08 00:48:11 -05:00
5 changed files with 253 additions and 116 deletions

2
go.mod
View File

@@ -36,7 +36,6 @@ require (
github.com/olekukonko/tablewriter v1.1.3
github.com/pquerna/otp v1.5.0
github.com/prometheus/client_golang v1.23.0
github.com/russross/blackfriday v1.6.0
github.com/sergi/go-diff v1.4.0
github.com/sourcegraph/run v0.12.0
github.com/stretchr/testify v1.11.1
@@ -45,6 +44,7 @@ require (
github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6
github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e
github.com/urfave/cli v1.22.17
github.com/yuin/goldmark v1.7.16
golang.org/x/crypto v0.47.0
golang.org/x/image v0.35.0
golang.org/x/net v0.48.0

4
go.sum
View File

@@ -399,8 +399,6 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
@@ -457,6 +455,8 @@ github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ=
github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE=
github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
go.bobheadxi.dev/streamline v1.2.1 h1:IqKSA1TbeuDqCzYNAwtlh8sqf3tsQus8XgJdkCWFT8c=

View File

@@ -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, "")))
}

View File

@@ -3,11 +3,21 @@ package markup
import (
"bytes"
"fmt"
"html"
"net/url"
"path"
"path/filepath"
"strings"
"github.com/russross/blackfriday"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer"
goldmarkhtml "github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
log "unknwon.dev/clog/v2"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/lazyregexp"
@@ -25,40 +35,69 @@ func IsMarkdownFile(name string) bool {
return false
}
// MarkdownRenderer is a extended version of underlying Markdown render object.
type MarkdownRenderer struct {
blackfriday.Renderer
urlPrefix string
}
var (
validLinksPattern = lazyregexp.New(`^[a-z][\w-]+://|^mailto:`)
linkifyURLRegexp = lazyregexp.New(`^(?:http|https|ftp)://[-a-zA-Z0-9@:%._+~#=]{1,256}(?:\.[a-z]+)?(?::\d+)?(?:[/#?][-a-zA-Z0-9@:%_+.~#$!?&/=();,'\^{}\[\]` + "`" + `]*)?`)
)
var validLinksPattern = lazyregexp.New(`^[a-z][\w-]+://|^mailto:`)
// isLink reports whether link fits valid format.
func isLink(link []byte) bool {
return validLinksPattern.Match(link)
}
// Link defines how formal links should be processed to produce corresponding HTML elements.
func (r *MarkdownRenderer) Link(out *bytes.Buffer, link, title, content []byte) {
if len(link) > 0 && !isLink(link) {
if link[0] != '#' {
link = []byte(path.Join(r.urlPrefix, string(link)))
}
}
r.Renderer.Link(out, link, title, content)
type linkTransformer struct {
urlPrefix string
}
// AutoLink defines how auto-detected links should be processed to produce corresponding HTML elements.
// Reference for kind: https://github.com/russross/blackfriday/blob/master/markdown.go#L69-L76
func (r *MarkdownRenderer) AutoLink(out *bytes.Buffer, link []byte, kind int) {
if kind != blackfriday.LINK_TYPE_NORMAL {
r.Renderer.AutoLink(out, link, kind)
return
func (t *linkTransformer) Transform(node *ast.Document, reader text.Reader, _ parser.Context) {
_ = ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
return ast.WalkContinue, nil
}
if link, ok := n.(*ast.Link); ok {
dest := link.Destination
if len(dest) > 0 && !isLink(dest) && dest[0] != '#' {
link.Destination = []byte(joinURLPath(t.urlPrefix, string(dest)))
}
}
return ast.WalkContinue, nil
})
}
// joinURLPath joins a base URL prefix with a relative path. It uses net/url for
// absolute URL prefixes to preserve the scheme and host, and path.Join for
// path-only prefixes.
func joinURLPath(prefix, relPath string) string {
u, err := url.Parse(prefix)
if err != nil || u.Scheme == "" {
return path.Join(prefix, relPath)
}
u.Path = path.Join(u.Path, relPath)
return u.String()
}
type gogsRenderer struct {
urlPrefix string
}
func (r *gogsRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(ast.KindAutoLink, r.renderAutoLink)
}
func (r *gogsRenderer) renderAutoLink(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.AutoLink)
if !entering {
return ast.WalkContinue, nil
}
// Since this method could only possibly serve one link at a time,
// we do not need to find all.
if n.AutoLinkType != ast.AutoLinkURL {
url := n.URL(source)
escaped := html.EscapeString(string(url))
_, _ = fmt.Fprintf(w, `<a href="mailto:%s">%s</a>`, escaped, escaped)
return ast.WalkContinue, nil
}
link := n.URL(source)
if bytes.HasPrefix(link, []byte(conf.Server.ExternalURL)) {
m := CommitPattern.Find(link)
if m != nil {
@@ -68,8 +107,9 @@ func (r *MarkdownRenderer) AutoLink(out *bytes.Buffer, link []byte, kind int) {
if j == -1 {
j = len(m)
}
_, _ = fmt.Fprintf(out, ` <code><a href="%s">%s</a></code>`, m, tool.ShortSHA1(string(m[i+7:j])))
return
escapedURL := html.EscapeString(string(m))
_, _ = fmt.Fprintf(w, ` <code><a href="%s">%s</a></code>`, escapedURL, tool.ShortSHA1(string(m[i+7:j])))
return ast.WalkContinue, nil
}
m = IssueFullPattern.Find(link)
@@ -82,78 +122,64 @@ func (r *MarkdownRenderer) AutoLink(out *bytes.Buffer, link []byte, kind int) {
}
index := string(m[i+7 : j])
escapedURL := html.EscapeString(string(m))
fullRepoURL := conf.Server.ExternalURL + strings.TrimPrefix(r.urlPrefix, "/")
var link string
var href string
if strings.HasPrefix(string(m), fullRepoURL) {
// Use a short issue reference if the URL refers to this repository
link = fmt.Sprintf(`<a href="%s">#%s</a>`, m, index)
href = fmt.Sprintf(`<a href="%s">#%s</a>`, escapedURL, html.EscapeString(index))
} else {
// Use a cross-repository issue reference if the URL refers to a different repository
repo := string(m[len(conf.Server.ExternalURL) : i-1])
link = fmt.Sprintf(`<a href="%s">%s#%s</a>`, m, repo, index)
repo := html.EscapeString(string(m[len(conf.Server.ExternalURL) : i-1]))
href = fmt.Sprintf(`<a href="%s">%s#%s</a>`, escapedURL, repo, html.EscapeString(index))
}
out.WriteString(link)
return
_, _ = w.WriteString(href)
return ast.WalkContinue, nil
}
}
r.Renderer.AutoLink(out, link, kind)
}
// ListItem defines how list items should be processed to produce corresponding HTML elements.
func (r *MarkdownRenderer) ListItem(out *bytes.Buffer, text []byte, flags int) {
// Detect procedures to draw checkboxes.
switch {
case bytes.HasPrefix(text, []byte("[ ] ")):
text = append([]byte(`<input type="checkbox" disabled="" />`), text[3:]...)
case bytes.HasPrefix(text, []byte("[x] ")):
text = append([]byte(`<input type="checkbox" disabled="" checked="" />`), text[3:]...)
}
r.Renderer.ListItem(out, text, flags)
escapedLink := html.EscapeString(string(link))
_, _ = fmt.Fprintf(w, `<a href="%s">%s</a>`, escapedLink, escapedLink)
return ast.WalkContinue, nil
}
// RawMarkdown renders content in Markdown syntax to HTML without handling special links.
func RawMarkdown(body []byte, urlPrefix string) []byte {
htmlFlags := 0
htmlFlags |= blackfriday.HTML_SKIP_STYLE
htmlFlags |= blackfriday.HTML_OMIT_CONTENTS
extensions := []goldmark.Extender{
extension.Table,
extension.Strikethrough,
extension.TaskList,
extension.NewLinkify(extension.WithLinkifyURLRegexp(linkifyURLRegexp.Regexp())),
}
if conf.Smartypants.Enabled {
htmlFlags |= blackfriday.HTML_USE_SMARTYPANTS
if conf.Smartypants.Fractions {
htmlFlags |= blackfriday.HTML_SMARTYPANTS_FRACTIONS
}
if conf.Smartypants.Dashes {
htmlFlags |= blackfriday.HTML_SMARTYPANTS_DASHES
}
if conf.Smartypants.LatexDashes {
htmlFlags |= blackfriday.HTML_SMARTYPANTS_LATEX_DASHES
}
if conf.Smartypants.AngledQuotes {
htmlFlags |= blackfriday.HTML_SMARTYPANTS_ANGLED_QUOTES
}
extensions = append(extensions, extension.Typographer)
}
renderer := &MarkdownRenderer{
Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""),
urlPrefix: urlPrefix,
rendererOpts := []renderer.Option{
renderer.WithNodeRenderers(
util.Prioritized(&gogsRenderer{urlPrefix: urlPrefix}, 0),
),
}
// set up the parser
extensions := 0
extensions |= blackfriday.EXTENSION_NO_INTRA_EMPHASIS
extensions |= blackfriday.EXTENSION_TABLES
extensions |= blackfriday.EXTENSION_FENCED_CODE
extensions |= blackfriday.EXTENSION_AUTOLINK
extensions |= blackfriday.EXTENSION_STRIKETHROUGH
extensions |= blackfriday.EXTENSION_SPACE_HEADERS
extensions |= blackfriday.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK
if conf.Markdown.EnableHardLineBreak {
extensions |= blackfriday.EXTENSION_HARD_LINE_BREAK
rendererOpts = append(rendererOpts, goldmarkhtml.WithHardWraps())
}
return blackfriday.Markdown(body, renderer, extensions)
md := goldmark.New(
goldmark.WithExtensions(extensions...),
goldmark.WithParserOptions(
parser.WithASTTransformers(
util.Prioritized(&linkTransformer{urlPrefix: urlPrefix}, 0),
),
),
goldmark.WithRendererOptions(rendererOpts...),
)
var buf bytes.Buffer
if err := md.Convert(body, &buf); err != nil {
log.Error("Failed to convert Markdown: %v", err)
return []byte(html.EscapeString(string(body)))
}
return buf.Bytes()
}
// Markdown takes a string or []byte and renders to HTML in Markdown syntax with special links.

View File

@@ -1,11 +1,9 @@
package markup_test
import (
"bytes"
"strings"
"testing"
"github.com/russross/blackfriday"
"github.com/stretchr/testify/assert"
"gogs.io/gogs/internal/conf"
@@ -13,7 +11,9 @@ import (
)
func Test_IsMarkdownFile(t *testing.T) {
// TODO: Refactor to accept a list of extensions
oldExts := conf.Markdown.FileExtensions
defer func() { conf.Markdown.FileExtensions = oldExts }()
conf.Markdown.FileExtensions = strings.Split(".md,.markdown,.mdown,.mkd", ",")
tests := []struct {
ext string
@@ -32,41 +32,152 @@ func Test_IsMarkdownFile(t *testing.T) {
}
}
func Test_Markdown(t *testing.T) {
// TODO: Refactor to accept URL
func Test_RawMarkdown_AutoLink(t *testing.T) {
oldURL := conf.Server.ExternalURL
defer func() { conf.Server.ExternalURL = oldURL }()
conf.Server.ExternalURL = "http://localhost:3000/"
htmlFlags := 0
htmlFlags |= blackfriday.HTML_SKIP_STYLE
htmlFlags |= blackfriday.HTML_OMIT_CONTENTS
renderer := &MarkdownRenderer{
Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""),
}
tests := []struct {
input string
expVal string
name string
input string
want string
}{
// Issue URL
{input: "http://localhost:3000/user/repo/issues/3333", expVal: "<a href=\"http://localhost:3000/user/repo/issues/3333\">#3333</a>"},
{input: "http://1111/2222/ssss-issues/3333?param=blah&blahh=333", expVal: "<a href=\"http://1111/2222/ssss-issues/3333?param=blah&amp;blahh=333\">http://1111/2222/ssss-issues/3333?param=blah&amp;blahh=333</a>"},
{input: "http://test.com/issues/33333", expVal: "<a href=\"http://test.com/issues/33333\">http://test.com/issues/33333</a>"},
{input: "http://test.com/issues/3", expVal: "<a href=\"http://test.com/issues/3\">http://test.com/issues/3</a>"},
{input: "http://issues/333", expVal: "<a href=\"http://issues/333\">http://issues/333</a>"},
{input: "https://issues/333", expVal: "<a href=\"https://issues/333\">https://issues/333</a>"},
{input: "http://tissues/0", expVal: "<a href=\"http://tissues/0\">http://tissues/0</a>"},
// Commit URL
{input: "http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae", expVal: " <code><a href=\"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae\">d8a994ef24</a></code>"},
{input: "http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2", expVal: " <code><a href=\"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2\">d8a994ef24</a></code>"},
{input: "https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2", expVal: "<a href=\"https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2\">https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2</a>"},
{input: "https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae", expVal: "<a href=\"https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae\">https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae</a>"},
{
name: "issue URL from same instance",
input: "http://localhost:3000/user/repo/issues/3333",
want: "<p><a href=\"http://localhost:3000/user/repo/issues/3333\">#3333</a></p>\n",
},
{
name: "non-matching issue-like URL",
input: "http://1111/2222/ssss-issues/3333?param=blah&blahh=333",
want: "<p><a href=\"http://1111/2222/ssss-issues/3333?param=blah&amp;blahh=333\">http://1111/2222/ssss-issues/3333?param=blah&amp;blahh=333</a></p>\n",
},
{
name: "external issue URL",
input: "http://test.com/issues/33333",
want: "<p><a href=\"http://test.com/issues/33333\">http://test.com/issues/33333</a></p>\n",
},
{
name: "commit URL from same instance",
input: "http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae",
want: "<p> <code><a href=\"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae\">d8a994ef24</a></code></p>\n",
},
{
name: "commit URL with fragment from same instance",
input: "http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2",
want: "<p> <code><a href=\"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2\">d8a994ef24</a></code></p>\n",
},
{
name: "external commit URL",
input: "https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2",
want: "<p><a href=\"https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2\">https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2</a></p>\n",
},
{
name: "issue URL with single digit",
input: "http://test.com/issues/3",
want: "<p><a href=\"http://test.com/issues/3\">http://test.com/issues/3</a></p>\n",
},
{
name: "host without dot in issue-like URL",
input: "http://issues/333",
want: "<p><a href=\"http://issues/333\">http://issues/333</a></p>\n",
},
{
name: "https host without dot in issue-like URL",
input: "https://issues/333",
want: "<p><a href=\"https://issues/333\">https://issues/333</a></p>\n",
},
{
name: "host without dot resembling keyword",
input: "http://tissues/0",
want: "<p><a href=\"http://tissues/0\">http://tissues/0</a></p>\n",
},
{
name: "https commit-like URL without dot",
input: "https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae",
want: "<p><a href=\"https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae\">https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae</a></p>\n",
},
}
for _, test := range tests {
t.Run("", func(t *testing.T) {
buf := new(bytes.Buffer)
renderer.AutoLink(buf, []byte(test.input), blackfriday.LINK_TYPE_NORMAL)
assert.Equal(t, test.expVal, buf.String())
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, "<p><a href=\"http://localhost:3000/other/repo/issues/42\">other/repo#42</a></p>\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, "<p><a href=\"http://localhost:3000/user/myrepo/issues/42#issuecomment-1\">#42</a></p>\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: "<p><a href=\"/user/repo/src/branch/main/other-file.md\">text</a></p>\n",
},
{
name: "relative link with absolute URL prefix",
input: "[text](other-file.md)",
urlPrefix: "http://localhost:3000/user/repo/src/branch/main",
want: "<p><a href=\"http://localhost:3000/user/repo/src/branch/main/other-file.md\">text</a></p>\n",
},
{
name: "absolute link not rewritten",
input: "[text](https://example.com/page)",
urlPrefix: "/user/repo/src/branch/main",
want: "<p><a href=\"https://example.com/page\">text</a></p>\n",
},
{
name: "anchor-only link not rewritten",
input: "[text](#section)",
urlPrefix: "/user/repo/src/branch/main",
want: "<p><a href=\"#section\">text</a></p>\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 <em>world</em>",
want: "<p>Hello <!-- raw HTML omitted -->world<!-- raw HTML omitted --></p>\n",
},
{
name: "block HTML tags are stripped",
input: "<div>content</div>",
want: "<!-- raw HTML omitted -->\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)
})
}
}