Files
summercms/modules/seaglass/README.md
Jakub Zych 71fa597830 feat(17-01): richeditor field type on the seaglass sanitizer
- 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
2026-10-12 15:15:38 +02:00

3.9 KiB

seaglass

HTML sanitizer for stored rich text: a bluemonday allowlist policy that returns clean HTML byte for byte and reports what it removed from the rest.

import "git.golem15.com/golem15/summercms/modules/seaglass"

Name: Glass the sea has worn smooth; rough HTML goes in and only the safe, smooth part comes out.

Overview

seaglass is the framework's one HTML policy for rich text an administrator stores, such as cabana's type: richeditor field. It wraps bluemonday v1.0.27 with the policy the Journal plugin uses for imported WinterCMS content, without that plugin's editor-specific rules. A tokenizing sanitizer checks attributes and URLs the way a browser parses them, so it does not share the bypasses of regex gates (<img src="x"onerror=…>, javascript&#58;, <svg/onload=…>).

The comparison is byte-stable: when the input and the policy's output tokenize to the same HTML tokens, the input itself is returned, so a value that only differs in escaping is never rewritten. Otherwise the policy's output is returned with a report of the removed elements, attributes and comments, which cabana shows to the administrator as save notices.

Role in the framework

A leaf utility with no SummerCMS dependency. cabana runs every richeditor value through it on create and update, top level and inside repeater rows; plugins that serve stored HTML in a public API can run it again on output, for content that was imported without passing the admin save.

Use cases:

  • Sanitize an HTML field on save and tell the editor what was removed.
  • Check that stored HTML is already clean (seaglass.Check), for example in an import report.
  • Sanitize HTML on output in a public API.

Features

  • bluemonday's UGC policy with links kept as written (no rel="nofollow"), tel: links next to http, https, mailto and relative URLs, and the class, dir, microdata, footnote role and inline color, font-size, font-weight and text-align styles imported WinterCMS content uses.
  • Everything else is removed: script, style, iframe, object, embed, form, svg, math, comments, every on* attribute, target, rel, and URLs with any other scheme.
  • seaglass.SanitizeReport returns HTML the policy keeps whole byte for byte with a zero seaglass.Removals; a tag that repeats an attribute is always a change.
  • seaglass.Removals names the removed elements and attributes (at most ten distinct names each, in first-seen order) and counts every removal and every removed comment.

Usage

clean, report := seaglass.SanitizeReport(`<p onclick="x()">Hi</p><script>alert(1)</script>`)
// clean == "<p>Hi</p>"
// report.Elements == []string{"script"}, report.Attributes == []string{"onclick"}

if err := seaglass.Check(stored); err != nil {
	// stored HTML would change under the policy
}

API reference

Identifier Description
seaglass.Sanitize Returns the HTML filtered through the policy: the input itself when the policy keeps all of it.
seaglass.SanitizeReport seaglass.Sanitize with a seaglass.Removals report of what was removed; a zero report exactly when the input is returned.
seaglass.Check Returns an error when the policy would change the HTML, nil when it keeps it whole.
seaglass.Removals What was removed: Elements and Attributes (distinct names), ElementCount and AttributeCount (every removal), Comments, and Changed (the HTML differs from the input).

Dependencies

  • SummerCMS modules: none.
  • Third-party: github.com/microcosm-cc/bluemonday v1.0.27 (the policy; decision note .planning/notes/2026-10-11-framework-html-sanitizer-bluemonday.md), golang.org/x/net/html (the token comparison and the removal report).
  • Standard library: errors, fmt, io, regexp, slices, strings.

Testing

go test ./modules/seaglass/...

The tests run in memory and need no external services.