// SPDX-License-Identifier: AGPL-3.0-or-later
package web
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func renderMD(t *testing.T, src string) (string, []Heading) {
t.Helper()
h, outline, err := newMarkdown().render([]byte(src), "demo", "main", "docs")
require.NoError(t, err)
return string(h), outline
}
// AC4: raw HTML, javascript: links and SVG are neutralised.
func TestMarkdownNeutralisation(t *testing.T) {
tests := []struct {
name, src string
absent []string
present []string
}{
{
"raw html block", "\n\n
hi
\n",
[]string{") [b](vbscript:x) [c](file:///etc/passwd) [d](mailto:a@b.c) [e](//evil.example/x) [f](ftp://h/x)\n",
[]string{"href=\"data", "vbscript", "file:", "mailto", "evil.example", "ftp:"}, []string{"a b c d e f
"},
},
{
"autolinks", " \n",
[]string{"href=\"javascript", "mailto:"}, []string{`href="https://ok.example/a"`, "me@example.org"},
},
{
"svg and images", "  \n\n\n",
[]string{"
[logo]`, `[remote]`, `[image]`},
},
{
"escaped text", "# x & \"q\"\n\nplain tags & \"quotes\"\n",
[]string{"", ""}, []string{"&", """},
},
{
"title injection", "[x](https://a.example \"t\\\" onmouseover=\\\"alert(1)\")\n",
[]string{"onmouseover=\"", " title="}, []string{`href="https://a.example"`},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
out, _ := renderMD(t, tt.src)
for _, s := range tt.absent {
assert.NotContains(t, out, s)
}
for _, s := range tt.present {
assert.Contains(t, out, s)
}
// Whatever the input: no inline script, no event handler, no
// style, no element that loads something.
for _, banned := range []string{"