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
5.8 kB 168 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 // checkboxes 78 policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input") 79 policy.AllowAttrs("checked", "disabled", "data-source-position").OnElements("input") 80 81 // for code blocks 82 policy.AllowAttrs("class").Matching(regexp.MustCompile(`chroma|mermaid`)).OnElements("pre") 83 policy.AllowAttrs("class").Matching(regexp.MustCompile(`anchor|footnote-ref|footnote-backref`)).OnElements("a") 84 policy.AllowAttrs("class").Matching(regexp.MustCompile(`heading`)).OnElements("h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8") 85 policy.AllowAttrs("class").Matching(regexp.MustCompile(strings.Join(slices.Collect(maps.Values(chroma.StandardTypes)), "|"))).OnElements("span") 86 87 // at-mentions 88 policy.AllowAttrs("class").Matching(regexp.MustCompile(`mention`)).OnElements("a") 89 90 // centering content 91 policy.AllowElements("center") 92 93 policy.AllowAttrs("align", "style", "width", "height").Globally() 94 policy.AllowStyles( 95 "margin", 96 "padding", 97 "text-align", 98 "font-weight", 99 "text-decoration", 100 "padding-left", 101 "padding-right", 102 "padding-top", 103 "padding-bottom", 104 "margin-left", 105 "margin-right", 106 "margin-top", 107 "margin-bottom", 108 ) 109 110 // math: the math extension emits <span class="math inline|display"> wrapping 111 // the raw LaTeX (delimited by \( \) / \[ \]). MathJax renders it client-side, 112 // so the sanitizer only needs to preserve these carrier spans. 113 policy.AllowAttrs("class").Matching(regexp.MustCompile(`^math (inline|display)$`)).OnElements("span") 114 115 // raw MathML: markdown is rendered with html.WithUnsafe(), so hand-authored 116 // <math>...</math> in source passes through to here. Browsers render 117 // presentation MathML natively, so preserve the elements and their attributes. 118 mathAttrs := []string{ 119 "accent", "columnalign", "columnlines", "columnspan", "dir", "display", 120 "displaystyle", "encoding", "fence", "form", "largeop", "linebreak", 121 "linethickness", "lspace", "mathcolor", "mathsize", "mathvariant", "minsize", 122 "movablelimits", "notation", "rowalign", "rspace", "rowspacing", "rowspan", 123 "scriptlevel", "stretchy", "symmetric", "title", "voffset", "width", 124 } 125 mathElements := []string{ 126 "annotation", "math", "menclose", "merror", "mfrac", "mi", "mmultiscripts", 127 "mn", "mo", "mover", "mpadded", "mprescripts", "mroot", "mrow", "mspace", 128 "msqrt", "mstyle", "msub", "msubsup", "msup", "mtable", "mtd", "mtext", 129 "mtr", "munder", "munderover", "semantics", 130 } 131 policy.AllowNoAttrs().OnElements(mathElements...) 132 policy.AllowAttrs(mathAttrs...).OnElements(mathElements...) 133 134 // goldmark-callout 135 policy.AllowAttrs("data-callout").OnElements("details") 136 137 return policy 138} 139 140func buildDescriptionPolicy() *bluemonday.Policy { 141 policy := bluemonday.NewPolicy() 142 policy.AllowStandardURLs() 143 144 // allow italics and bold. 145 policy.AllowElements("i", "b", "em", "strong") 146 147 // allow code. 148 policy.AllowElements("code") 149 150 // allow links 151 policy.AllowAttrs("href", "target", "rel").OnElements("a") 152 153 return policy 154} 155 156func buildLogsPolicy() *bluemonday.Policy { 157 policy := bluemonday.NewPolicy() 158 159 policy.AllowElements("p", "span") 160 161 // allow italics and bold 162 policy.AllowElements("i", "b", "em", "strong") 163 164 // allow fg/bg classes from terminal-to-html 165 policy.AllowAttrs("class").Matching(regexp.MustCompile(`term-*`)).OnElements("span") 166 167 return policy 168}