package classes import ( "strings" "testing" ) // TestFormatHTMLRejectsUnsafeHTML substitutes JOURNAL-003/004 Twig XSS // templates (Phase 16). Script, iframe, event handlers, and javascript / // vbscript / data schemes must fail the reject gate. Footnotes and tables // from goldmark extensions still pass. func TestFormatHTMLRejectsUnsafeHTML(t *testing.T) { html, err := FormatHTML("# Hello") if err != nil || !strings.Contains(html, "

") { t.Fatalf("safe heading: html=%s err=%v", html, err) } table, err := FormatHTML("| a | b |\n| - | - |\n| 1 | 2 |") if err != nil { t.Fatalf("table markdown: %v", err) } if !strings.Contains(table, "alert(1)"}, {"mixed-case script", ""}, {"script inside an HTML block", "
\n

Intro

\n\n
"}, {"script after markdown", "# Title\n\nText.\n\n"}, {"iframe", ``}, {"upper-case iframe", ``}, {"object", ``}, {"embed", ``}, {"event", ``}, {"svg onload", ``}, {"mixed-case event handler", `

Click

`}, {"event handler in an inline element", "Text with hover."}, {"javascript", "[x](javascript:alert(1))"}, {"mixed-case javascript href", `x`}, {"vbscript", "[x](vbscript:msgbox(1))"}, {"raw vbscript href", `x`}, {"data", "[x](data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==)"}, {"raw data URL attribute", ``}, {"markdown image with a data URL", "![x](data:image/png;base64,iVBORw0KGgo=)"}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { out, err := FormatHTML(tc.src) if err == nil { t.Fatalf("FormatHTML accepted unsafe input %q: %s", tc.src, out) } if out != "" { t.Fatalf("FormatHTML returned HTML with its error: %s", out) } }) } } // TestFormatHTMLKeepsRawHTML pins D-03: raw HTML in content passes through // unchanged, as Winter's Markdown::parse does, so an imported HTML body // survives an admin save, an import or an API write. func TestFormatHTMLKeepsRawHTML(t *testing.T) { const paragraphs = "

Grzyby rosną w lesie.

\n

Druga część.

" out, err := FormatHTML(paragraphs) if err != nil { t.Fatalf("html paragraphs: %v", err) } if strings.TrimSpace(out) != paragraphs { t.Fatalf("html paragraphs changed:\n%s", out) } mixed, err := FormatHTML("# Tytuł\n\nTekst.\n\n
Autor
") if err != nil { t.Fatalf("markdown with a raw footer: %v", err) } if !strings.Contains(mixed, "

Tytuł

") || !strings.Contains(mixed, `
Autor
`) { t.Fatalf("raw footer was dropped:\n%s", mixed) } // Imported-style bodies: each round-trips unchanged after trimming. bodies := []struct { name string src string }{ {"paragraphs", "

Pierwszy akapit.

\n

Drugi akapit z kursywą.

\n

Trzeci.

"}, {"class attributes", `

Wstęp

`}, {"https link", `

Zobacz źródło.

`}, {"https image", `

Grzyby

`}, {"entity in an attribute", `

link

`}, {"table", "\n\n
AB
"}, {"surrounding whitespace", "\n\n

Trimmed.

\n"}, } for _, tc := range bodies { t.Run(tc.name, func(t *testing.T) { out, err := FormatHTML(tc.src) if err != nil { t.Fatalf("FormatHTML(%q): %v", tc.src, err) } if strings.TrimSpace(out) != strings.TrimSpace(tc.src) { t.Fatalf("html changed:\n got %q\nwant %q", strings.TrimSpace(out), strings.TrimSpace(tc.src)) } if strings.Contains(out, "raw HTML omitted") { t.Fatalf("goldmark placeholder in output: %s", out) } }) } t.Run("markdown with a trailing raw footer and style", func(t *testing.T) { const footer = `` const style = "" src := "## Nagłówek\n\nTekst z **pogrubieniem** i [linkiem](https://example.test).\n\n" + footer + "\n\n" + style out, err := FormatHTML(src) if err != nil { t.Fatalf("markdown with footer and style: %v", err) } for _, want := range []string{ "

Nagłówek

", "pogrubieniem", `linkiem`, footer, style, } { if !strings.Contains(out, want) { t.Fatalf("output lacks %q:\n%s", want, out) } } if strings.Contains(out, "raw HTML omitted") { t.Fatalf("goldmark placeholder in output: %s", out) } }) } // TestJournalEndToEnd keeps `go test ./... -run TestJournalEndToEnd` // from reporting [no tests to run] in this package. func TestJournalEndToEnd(t *testing.T) {}