- modules/breakwater exports Render(markdown) and Purify(html): goldmark GFM in its default safe mode (raw HTML stripped, unsafe links dropped), then a named bluemonday policy built from NewPolicy, not the UGC preset - the policy is the PHP golem15/forum HTMLPurifier config 1:1: the fixed element list, a[href|rel|target], http/https/mailto only, rel=nofollow on every link, target=_blank plus noopener on fully-qualified links - tests cover the PHP ForumMarkdownRenderTest corpus, links per scheme, blank input, the Purify allow-list and concurrent use; testable examples feed the docs page snippets - module README, root module table row, introduction.md Utilities row and docs/services/markdown.md (which gate serves user content vs admin fields) - go.mod and go.sum unchanged: bluemonday v1.0.27 and goldmark v1.8.6 were already pinned
breakwater
Markdown renderer and HTML sanitizer for user-submitted content: GitHub Flavored Markdown through goldmark, then a fixed bluemonday allow-list that adds nofollow and target=_blank to links.
import "git.golem15.com/golem15/summercms/modules/breakwater"
Name: A breakwater takes the force of the open sea; user Markdown and HTML break on the policy and only calm, allow-listed HTML reaches the shore.
Overview
breakwater is the gate for content a site user submits and a plugin stores and serves: forum posts, comments, and any other Markdown or HTML that does not come from an administrator. breakwater.Render converts Markdown to HTML and breakwater.Purify sanitizes HTML that is already HTML; both return the policy's output, which the plugin stores on write and serves as is.
The policy is the PHP golem15/forum HTMLPurifier config 1:1 (decision note .planning/notes/2026-10-18-bluemonday-forum-policy.md): a short element list, links with http, https or mailto only, rel="nofollow" on every link and target="_blank" on fully-qualified ones. The Markdown pass is goldmark with GFM, in its default safe mode: raw HTML in the Markdown is stripped and a link with an unsafe destination is dropped before the policy runs, the analog of the PHP html_input=strip and allow_unsafe_links=false.
Not this module. seaglass is the policy for rich text an administrator stores through cabana's richeditor field: bluemonday's UGC policy, which keeps images, relative and tel: links, and links as written without nofollow. cabana.RenderMarkdown is the admin Markdown field preview: CommonMark without GFM behind a regex gate that refuses unsafe output instead of removing it. Plugins must not use either for user content.
Features
- Allowed elements:
p,br,strong,em,b,i,h1–h6,ul,ol,li,blockquote,pre,code,awithhref,relandtarget,table,thead,tbody,tr,th,td,hr,del,ins,sub,sup. Everything else, includingimg,figure,script,style,iframe,form,svg, everyon*attribute,classandid, is removed. - URL schemes:
http,httpsandmailto. Notel:and no relative URLs; a link whosehrefis refused loses its tag and keeps its text. - Every link gets
rel="nofollow"; every fully-qualifiedhttporhttpslink also getstarget="_blank"andrel="noopener". A caller-suppliedrelortargetwith any other value is dropped and rewritten. - Raw HTML in Markdown is stripped, not escaped into visible text, and HTML comments are removed.
- Blank or whitespace-only input renders and purifies to
"". - Safe for concurrent use: the Markdown engine and the policy are built once and shared.
Usage
// A forum post: store the rendered HTML on write and serve it as is.
html := breakwater.Render(post.Content)
// Content stored as HTML: the same allow-list, no Markdown pass.
clean := breakwater.Purify(training.ContentHTML)
breakwater.Render("See [the docs](https://example.com/docs) and <b onclick=\"x()\">this</b>.") gives <p>See <a href="https://example.com/docs" rel="nofollow noopener" target="_blank">the docs</a> and this.</p>.
API reference
| Identifier | Description |
|---|---|
breakwater.Render |
Converts user Markdown (GFM) to HTML with raw HTML stripped and unsafe links dropped, then returns it filtered through the policy; "" for blank input. |
breakwater.Purify |
Returns HTML filtered through the same policy with no Markdown pass, for content stored as HTML; "" for blank input. |
Dependencies
- SummerCMS modules: none.
- Third-party:
github.com/microcosm-cc/bluemondayv1.0.27 (the policy; decision note.planning/notes/2026-10-18-bluemonday-forum-policy.md),github.com/yuin/goldmarkv1.8.6 withextension.GFM(the Markdown pass). - Standard library:
bytes,regexp,strings.
Testing
go test ./modules/breakwater/...
The tests run in memory and need no external services. They cover the PHP ForumMarkdownRenderTest corpus (XSS cases, GFM table and fenced code), links and autolinks per scheme, blank input, the Purify allow-list and concurrent use.