This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / appview / pages / markup / sanitizer / sanitizer.go
6.0 kB 171 lines
1package sanitizer 2 3import ( 4 "maps" 5 "regexp" 6 "slices" 7 "strings" 8 9 "github.com/alecthomas/chroma/v2" 10 "github.com/microcosm-cc/bluemonday" 11) 12 13// shared policies built once at init; safe for concurrent use per bluemonday docs 14var ( 15 sharedDefaultPolicy *bluemonday.Policy 16 sharedDescriptionPolicy *bluemonday.Policy 17 sharedLogsPolicy *bluemonday.Policy 18) 19 20func init() { 21 sharedDefaultPolicy = buildDefaultPolicy() 22 sharedDescriptionPolicy = buildDescriptionPolicy() 23 sharedLogsPolicy = buildLogsPolicy() 24} 25 26func SanitizeDefault(html string) string { 27 return sharedDefaultPolicy.Sanitize(html) 28} 29func SanitizeDescription(html string) string { 30 return sharedDescriptionPolicy.Sanitize(html) 31} 32func SanitizeLogs(html string) string { 33 return sharedLogsPolicy.Sanitize(html) 34} 35 36func buildDefaultPolicy() *bluemonday.Policy { 37 policy := bluemonday.UGCPolicy() 38 39 // Allow generally safe attributes 40 generalSafeAttrs := []string{ 41 "abbr", "accept", "accept-charset", 42 "accesskey", "action", "align", "alt", 43 "aria-describedby", "aria-hidden", "aria-label", "aria-labelledby", 44 "axis", "border", "cellpadding", "cellspacing", "char", 45 "charoff", "charset", "checked", 46 "clear", "cols", "colspan", "color", 47 "compact", "coords", "datetime", "dir", 48 "disabled", "enctype", "for", "frame", 49 "headers", "height", "hreflang", 50 "hspace", "ismap", "label", "lang", 51 "maxlength", "media", "method", 52 "multiple", "name", "nohref", "noshade", 53 "nowrap", "open", "prompt", "readonly", "rel", "rev", 54 "rows", "rowspan", "rules", "scope", 55 "selected", "shape", "size", "span", 56 "start", "summary", "tabindex", "target", 57 "title", "type", "usemap", "valign", "value", 58 "vspace", "width", "itemprop", 59 } 60 61 generalSafeElements := []string{ 62 "h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8", "br", "b", "i", "strong", "em", "a", "pre", "code", "img", "tt", 63 "div", "ins", "del", "sup", "sub", "p", "ol", "ul", "table", "thead", "tbody", "tfoot", "blockquote", "label", 64 "dl", "dt", "dd", "kbd", "q", "samp", "var", "hr", "ruby", "rt", "rp", "li", "tr", "td", "th", "s", "strike", "summary", 65 "details", "caption", "figure", "figcaption", 66 "abbr", "bdo", "cite", "dfn", "mark", "small", "span", "time", "video", "wbr", 67 } 68 69 policy.AllowAttrs(generalSafeAttrs...).OnElements(generalSafeElements...) 70 71 // video 72 policy.AllowAttrs("src", "autoplay", "controls").OnElements("video") 73 74 // picture/source for modern image formats (avif, webp, etc.) 75 policy.AllowAttrs("srcset", "type", "media").OnElements("source") 76 77 // marker the editor uses to preview an uncommitted blob image; see markdown.go 78 policy.AllowAttrs("data-blob-cid").OnElements("img", "source") 79 80 // checkboxes 81 policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input") 82 policy.AllowAttrs("checked", "disabled", "data-source-position").OnElements("input") 83 84 // for code blocks 85 policy.AllowAttrs("class").Matching(regexp.MustCompile(`chroma|mermaid`)).OnElements("pre") 86 policy.AllowAttrs("class").Matching(regexp.MustCompile(`anchor|footnote-ref|footnote-backref`)).OnElements("a") 87 policy.AllowAttrs("class").Matching(regexp.MustCompile(`heading`)).OnElements("h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8") 88 policy.AllowAttrs("class").Matching(regexp.MustCompile(strings.Join(slices.Collect(maps.Values(chroma.StandardTypes)), "|"))).OnElements("span") 89 90 // at-mentions 91 policy.AllowAttrs("class").Matching(regexp.MustCompile(`mention`)).OnElements("a") 92 93 // centering content 94 policy.AllowElements("center") 95 96 policy.AllowAttrs("align", "style", "width", "height").Globally() 97 policy.AllowStyles( 98 "margin", 99 "padding", 100 "text-align", 101 "font-weight", 102 "text-decoration", 103 "padding-left", 104 "padding-right", 105 "padding-top", 106 "padding-bottom", 107 "margin-left", 108 "margin-right", 109 "margin-top", 110 "margin-bottom", 111 ) 112 113 // math: the math extension emits <span class="math inline|display"> wrapping 114 // the raw LaTeX (delimited by \( \) / \[ \]). MathJax renders it client-side, 115 // so the sanitizer only needs to preserve these carrier spans. 116 policy.AllowAttrs("class").Matching(regexp.MustCompile(`^math (inline|display)$`)).OnElements("span") 117 118 // raw MathML: markdown is rendered with html.WithUnsafe(), so hand-authored 119 // <math>...</math> in source passes through to here. Browsers render 120 // presentation MathML natively, so preserve the elements and their attributes. 121 mathAttrs := []string{ 122 "accent", "columnalign", "columnlines", "columnspan", "dir", "display", 123 "displaystyle", "encoding", "fence", "form", "largeop", "linebreak", 124 "linethickness", "lspace", "mathcolor", "mathsize", "mathvariant", "minsize", 125 "movablelimits", "notation", "rowalign", "rspace", "rowspacing", "rowspan", 126 "scriptlevel", "stretchy", "symmetric", "title", "voffset", "width", 127 } 128 mathElements := []string{ 129 "annotation", "math", "menclose", "merror", "mfrac", "mi", "mmultiscripts", 130 "mn", "mo", "mover", "mpadded", "mprescripts", "mroot", "mrow", "mspace", 131 "msqrt", "mstyle", "msub", "msubsup", "msup", "mtable", "mtd", "mtext", 132 "mtr", "munder", "munderover", "semantics", 133 } 134 policy.AllowNoAttrs().OnElements(mathElements...) 135 policy.AllowAttrs(mathAttrs...).OnElements(mathElements...) 136 137 // goldmark-callout 138 policy.AllowAttrs("data-callout").OnElements("details") 139 140 return policy 141} 142 143func buildDescriptionPolicy() *bluemonday.Policy { 144 policy := bluemonday.NewPolicy() 145 policy.AllowStandardURLs() 146 147 // allow italics and bold. 148 policy.AllowElements("i", "b", "em", "strong") 149 150 // allow code. 151 policy.AllowElements("code") 152 153 // allow links 154 policy.AllowAttrs("href", "target", "rel").OnElements("a") 155 156 return policy 157} 158 159func buildLogsPolicy() *bluemonday.Policy { 160 policy := bluemonday.NewPolicy() 161 162 policy.AllowElements("p", "span") 163 164 // allow italics and bold 165 policy.AllowElements("i", "b", "em", "strong") 166 167 // allow fg/bg classes from terminal-to-html 168 policy.AllowAttrs("class").Matching(regexp.MustCompile(`term-*`)).OnElements("span") 169 170 return policy 171}