- seaglass (new module): bluemonday v1.0.27 policy ported from Journal without the Blog Editor rules, byte-stable token comparison, SanitizeReport, Sanitize, Check, Removals - cabana: type richeditor (common keys plus size); every create and update value sanitized before Fill, non-string values are a 422, removals become meta.notices; settings and relation forms refuse the type - backend lang richeditor.removed_* and markup_changed (en, pl plurals) - TestSeaglassSmoke, TestCompanionsRichEditorSmoke, README and docs
54 lines
2.2 KiB
Go
54 lines
2.2 KiB
Go
package seaglass_test
|
|
|
|
import (
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/seaglass"
|
|
)
|
|
|
|
// TestSeaglassSmoke checks the two promises of the sanitizer: clean HTML
|
|
// comes back byte for byte with an empty report, and unsafe constructs are
|
|
// removed and named.
|
|
func TestSeaglassSmoke(t *testing.T) {
|
|
clean := "<p>Mushrooms <strong>can</strong> help:</p>\n<ul><li>focus</li><li><a href=\"https://example.com/a?b=1&c=2\">sleep</a></li></ul>\n<p>Call <a href=\"tel:+48123\">us</a> or <a href=\"/kontakt\">write</a>.</p>"
|
|
got, report := seaglass.SanitizeReport(clean)
|
|
if got != clean || report.Changed || report.ElementCount+report.AttributeCount+report.Comments != 0 {
|
|
t.Fatalf("clean HTML changed: %q %+v", got, report)
|
|
}
|
|
if err := seaglass.Check(clean); err != nil {
|
|
t.Fatalf("Check(clean) = %v", err)
|
|
}
|
|
// Re-escaping alone is not a change: bluemonday writes ' here.
|
|
quoted := `<p>It's "fine"</p>`
|
|
if got := seaglass.Sanitize(quoted); got != quoted {
|
|
t.Fatalf("re-escaped text changed: %q", got)
|
|
}
|
|
|
|
dirty := `<p onclick="steal()">Hi <a href="javascript:alert(1)">there</a></p><script>alert(1)</script><!-- note -->`
|
|
got, report = seaglass.SanitizeReport(dirty)
|
|
for _, bad := range []string{"onclick", "javascript", "<script", "alert", "<!--"} {
|
|
if strings.Contains(got, bad) {
|
|
t.Fatalf("%q survived: %q", bad, got)
|
|
}
|
|
}
|
|
if !strings.Contains(got, "Hi ") || !report.Changed {
|
|
t.Fatalf("sanitized = %q %+v", got, report)
|
|
}
|
|
// A link whose only href is refused loses its tag (its text stays), so
|
|
// it is reported as a removed a element.
|
|
if !slices.Contains(report.Elements, "script") || !slices.Contains(report.Elements, "a") || !slices.Contains(report.Attributes, "onclick") || report.Comments != 1 {
|
|
t.Fatalf("report = %+v", report)
|
|
}
|
|
if err := seaglass.Check(dirty); err == nil {
|
|
t.Fatal("Check(dirty) = nil")
|
|
}
|
|
// Bypasses of regex gates are removed too.
|
|
for _, payload := range []string{`<img src="x"onerror="alert(1)">`, `<a href="javascript:alert(1)">x</a>`, `<svg/onload=alert(1)>`, `<a href="https://a" href="javascript:alert(1)">x</a>`} {
|
|
if err := seaglass.Check(payload); err == nil {
|
|
t.Fatalf("payload passed: %s", payload)
|
|
}
|
|
}
|
|
}
|