| 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "encoding/xml" |
| 5 | "strings" |
| 6 | "testing" |
| 7 | ) |
| 8 | |
| 9 | // A browser parses the sanitized bytes as an XML document, so the output must be |
| 10 | // well-formed on its own. Emitting the element's namespace twice makes the |
| 11 | // whole document a parse error, and the picture silently fails to load. |
| 12 | func TestSanitizeMarkdownSVGStaysWellFormedForTheRenderer(t *testing.T) { |
| 13 | app := NewApp() |
| 14 | for _, test := range []struct { |
| 15 | name string |
| 16 | body string |
| 17 | }{ |
| 18 | {"namespaced root", `<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10"><rect width="1" height="1"/></svg>`}, |
| 19 | {"namespace-free root", `<svg width="10" height="10"><rect width="1" height="1"/></svg>`}, |
| 20 | {"prolog and comment", "<?xml version=\"1.0\"?>\n<!-- x -->\n<svg xmlns=\"http://www.w3.org/2000/svg\"><text x=\"1\" y=\"1\">hi</text></svg>"}, |
| 21 | } { |
| 22 | t.Run(test.name, func(t *testing.T) { |
| 23 | view := app.SanitizeMarkdownSVG(test.body) |
| 24 | if !view.OK { |
| 25 | t.Fatalf("a valid SVG was refused: %+v", view) |
| 26 | } |
| 27 | if n := strings.Count(view.SVG, "xmlns="); n > 1 { |
| 28 | t.Fatalf("the sanitized SVG carries %d xmlns attributes; a browser rejects the duplicate:\n%s", n, view.SVG) |
| 29 | } |
| 30 | var root struct { |
| 31 | XMLName xml.Name |
| 32 | Space string `xml:"xmlns,attr"` |
| 33 | } |
| 34 | if err := xml.Unmarshal([]byte(view.SVG), &root); err != nil { |
| 35 | t.Fatalf("the sanitized SVG is not well-formed XML: %v\n%s", err, view.SVG) |
| 36 | } |
| 37 | if root.XMLName.Local != "svg" || root.XMLName.Space != "http://www.w3.org/2000/svg" { |
| 38 | t.Fatalf("the sanitized root is not an SVG document element: %+v\n%s", root.XMLName, view.SVG) |
| 39 | } |
| 40 | }) |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | func TestSanitizeMarkdownSVGRejectsEscapedCSSReferences(t *testing.T) { |
| 45 | view := NewApp().SanitizeMarkdownSVG(`<svg><rect style="fill:u\72 l(https://example.invalid/pixel)"/></svg>`) |
| 46 | if !view.OK { |
| 47 | t.Fatalf("document should remain previewable: %+v", view) |
| 48 | } |
| 49 | if strings.Contains(view.SVG, "example.invalid") || strings.Contains(view.SVG, `\72`) { |
| 50 | t.Fatalf("escaped external reference survived sanitizing: %s", view.SVG) |
| 51 | } |
| 52 | } |
| 53 |