// 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) { t.Parallel() tests := []struct { name, src string absent []string present []string }{ { "raw html block", "\n\n
hi
\n", []string{" b c \n", []string{""}, []string{"

a "}, }, { "javascript link", "[x](javascript:alert(1)) [y](JaVaScRiPt:alert(1)) [z](java\tscript:alert(1))\n", []string{"javascript", "JaVaScRiPt", "href=\"java"}, []string{"

x y "}, }, { "data and other schemes", "[a](data:text/html,) [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", "![logo](x.svg) ![remote](https://evil.example/p.png) ![](data:image/svg+xml;base64,PHN2Zz4=)\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) { t.Parallel() 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{"b`, "alignment is an attribute, never a style") assert.NotContains(t, out, "style=") assert.Contains(t, out, "gone") assert.Contains(t, out, `type="checkbox"`) }