Files
summercms/modules/breakwater
Jakub Zych 98bc6de8f5 feat(breakwater): Markdown renderer and HTML sanitizer with the forum policy (D-17)
- 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
2026-10-18 14:52:30 +02:00
..

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, a with href, rel and target, table, thead, tbody, tr, th, td, hr, del, ins, sub, sup. Everything else, including img, figure, script, style, iframe, form, svg, every on* attribute, class and id, is removed.
  • URL schemes: http, https and mailto. No tel: and no relative URLs; a link whose href is refused loses its tag and keeps its text.
  • Every link gets rel="nofollow"; every fully-qualified http or https link also gets target="_blank" and rel="noopener". A caller-supplied rel or target with 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/bluemonday v1.0.27 (the policy; decision note .planning/notes/2026-10-18-bluemonday-forum-policy.md), github.com/yuin/goldmark v1.8.6 with extension.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.