- 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
66 lines
3.9 KiB
Markdown
66 lines
3.9 KiB
Markdown
# 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](https://github.com/microcosm-cc/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:`, `<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](../cabana/README.md) 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
|
|
|
|
```go
|
|
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
|
|
|
|
```sh
|
|
go test ./modules/seaglass/...
|
|
```
|
|
|
|
The tests run in memory and need no external services.
|