60 lines
2.1 KiB
Go
60 lines
2.1 KiB
Go
package sanitize
|
|
|
|
import (
|
|
"regexp"
|
|
|
|
"github.com/microcosm-cc/bluemonday"
|
|
)
|
|
|
|
var (
|
|
styleType = regexp.MustCompile(`(?i)^text\/css$`)
|
|
cssJSURL = regexp.MustCompile(`(?i)url\s*\(\s*['"]?javascript:[^)]*\)`)
|
|
)
|
|
|
|
// emailPolicy preserves HTML email layout (inline styles, <style>, tables) while
|
|
// stripping scripts and event handlers. Display happens in a sandboxed iframe.
|
|
func emailPolicy() *bluemonday.Policy {
|
|
p := bluemonday.UGCPolicy()
|
|
|
|
// Full documents and email structure
|
|
p.AllowElements("html", "head", "body", "title")
|
|
p.AllowElements("font", "main", "nav", "header", "footer")
|
|
|
|
// Inline styles + <style> blocks (requires AllowUnsafe for tag content)
|
|
p.AllowAttrs("type").Matching(styleType).OnElements("style")
|
|
p.AllowAttrs("style").Globally()
|
|
p.AllowStyling()
|
|
p.AllowUnsafe(true)
|
|
p.AllowElementsContent("style")
|
|
|
|
// Legacy table / font attributes common in newsletters
|
|
p.AllowAttrs("bgcolor", "color").OnElements("basefont", "font", "hr", "td", "table", "tr", "th")
|
|
p.AllowAttrs("border").Matching(bluemonday.Integer).OnElements("img", "table")
|
|
p.AllowAttrs("cellpadding", "cellspacing").Matching(bluemonday.Integer).OnElements("table")
|
|
p.AllowAttrs("width", "height", "align", "valign", "background", "colspan", "rowspan").
|
|
OnElements("table", "tbody", "tr", "td", "th", "thead", "tfoot", "colgroup", "col", "div", "p", "img")
|
|
|
|
// External CSS (resolved by the client when remote content is allowed)
|
|
p.AllowAttrs("rel", "href").OnElements("link")
|
|
p.AllowAttrs("type").Matching(styleType).OnElements("link")
|
|
p.AllowRelativeURLs(true)
|
|
p.AllowDataURIImages()
|
|
p.AllowURLSchemes("cid")
|
|
|
|
// Lazy-load / responsive images common in newsletters
|
|
p.AllowAttrs("srcset").OnElements("img", "source")
|
|
p.AllowAttrs("loading", "decoding", "sizes").OnElements("img", "source")
|
|
p.AllowAttrs(
|
|
"data-src", "data-original", "data-lazy-src", "data-srcset",
|
|
"data-href", "data-url", "data-image", "data-bg",
|
|
).OnElements("img", "source")
|
|
|
|
return p
|
|
}
|
|
|
|
var policy = emailPolicy()
|
|
|
|
func stripUnsafeCSSURLs(html string) string {
|
|
return cssJSURL.ReplaceAllString(html, "url(about:blank)")
|
|
}
|