A vibe coded tangled fork which supports pijul.
at master 151 lines 4.9 kB view raw
1package markup 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) 18 19func init() { 20 sharedDefaultPolicy = buildDefaultPolicy() 21 sharedDescriptionPolicy = buildDescriptionPolicy() 22} 23 24type Sanitizer struct { 25 defaultPolicy *bluemonday.Policy 26 descriptionPolicy *bluemonday.Policy 27} 28 29func NewSanitizer() Sanitizer { 30 return Sanitizer{ 31 defaultPolicy: sharedDefaultPolicy, 32 descriptionPolicy: sharedDescriptionPolicy, 33 } 34} 35 36func (s *Sanitizer) SanitizeDefault(html string) string { 37 return s.defaultPolicy.Sanitize(html) 38} 39func (s *Sanitizer) SanitizeDescription(html string) string { 40 return s.descriptionPolicy.Sanitize(html) 41} 42 43func buildDefaultPolicy() *bluemonday.Policy { 44 policy := bluemonday.UGCPolicy() 45 46 // Allow generally safe attributes 47 generalSafeAttrs := []string{ 48 "abbr", "accept", "accept-charset", 49 "accesskey", "action", "align", "alt", 50 "aria-describedby", "aria-hidden", "aria-label", "aria-labelledby", 51 "axis", "border", "cellpadding", "cellspacing", "char", 52 "charoff", "charset", "checked", 53 "clear", "cols", "colspan", "color", 54 "compact", "coords", "datetime", "dir", 55 "disabled", "enctype", "for", "frame", 56 "headers", "height", "hreflang", 57 "hspace", "ismap", "label", "lang", 58 "maxlength", "media", "method", 59 "multiple", "name", "nohref", "noshade", 60 "nowrap", "open", "prompt", "readonly", "rel", "rev", 61 "rows", "rowspan", "rules", "scope", 62 "selected", "shape", "size", "span", 63 "start", "summary", "tabindex", "target", 64 "title", "type", "usemap", "valign", "value", 65 "vspace", "width", "itemprop", 66 } 67 68 generalSafeElements := []string{ 69 "h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8", "br", "b", "i", "strong", "em", "a", "pre", "code", "img", "tt", 70 "div", "ins", "del", "sup", "sub", "p", "ol", "ul", "table", "thead", "tbody", "tfoot", "blockquote", "label", 71 "dl", "dt", "dd", "kbd", "q", "samp", "var", "hr", "ruby", "rt", "rp", "li", "tr", "td", "th", "s", "strike", "summary", 72 "details", "caption", "figure", "figcaption", 73 "abbr", "bdo", "cite", "dfn", "mark", "small", "span", "time", "video", "wbr", 74 } 75 76 policy.AllowAttrs(generalSafeAttrs...).OnElements(generalSafeElements...) 77 78 // video 79 policy.AllowAttrs("src", "autoplay", "controls").OnElements("video") 80 81 // checkboxes 82 policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input") 83 policy.AllowAttrs("checked", "disabled", "data-source-position").OnElements("input") 84 85 // for code blocks 86 policy.AllowAttrs("class").Matching(regexp.MustCompile(`chroma|mermaid`)).OnElements("pre") 87 policy.AllowAttrs("class").Matching(regexp.MustCompile(`anchor|footnote-ref|footnote-backref`)).OnElements("a") 88 policy.AllowAttrs("class").Matching(regexp.MustCompile(`heading`)).OnElements("h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8") 89 policy.AllowAttrs("class").Matching(regexp.MustCompile(strings.Join(slices.Collect(maps.Values(chroma.StandardTypes)), "|"))).OnElements("span") 90 91 // at-mentions 92 policy.AllowAttrs("class").Matching(regexp.MustCompile(`mention`)).OnElements("a") 93 94 // centering content 95 policy.AllowElements("center") 96 97 policy.AllowAttrs("align", "style", "width", "height").Globally() 98 policy.AllowStyles( 99 "margin", 100 "padding", 101 "text-align", 102 "font-weight", 103 "text-decoration", 104 "padding-left", 105 "padding-right", 106 "padding-top", 107 "padding-bottom", 108 "margin-left", 109 "margin-right", 110 "margin-top", 111 "margin-bottom", 112 ) 113 114 // math 115 mathAttrs := []string{ 116 "accent", "columnalign", "columnlines", "columnspan", "dir", "display", 117 "displaystyle", "encoding", "fence", "form", "largeop", "linebreak", 118 "linethickness", "lspace", "mathcolor", "mathsize", "mathvariant", "minsize", 119 "movablelimits", "notation", "rowalign", "rspace", "rowspacing", "rowspan", 120 "scriptlevel", "stretchy", "symmetric", "title", "voffset", "width", 121 } 122 mathElements := []string{ 123 "annotation", "math", "menclose", "merror", "mfrac", "mi", "mmultiscripts", 124 "mn", "mo", "mover", "mpadded", "mprescripts", "mroot", "mrow", "mspace", 125 "msqrt", "mstyle", "msub", "msubsup", "msup", "mtable", "mtd", "mtext", 126 "mtr", "munder", "munderover", "semantics", 127 } 128 policy.AllowNoAttrs().OnElements(mathElements...) 129 policy.AllowAttrs(mathAttrs...).OnElements(mathElements...) 130 131 // goldmark-callout 132 policy.AllowAttrs("data-callout").OnElements("details") 133 134 return policy 135} 136 137func buildDescriptionPolicy() *bluemonday.Policy { 138 policy := bluemonday.NewPolicy() 139 policy.AllowStandardURLs() 140 141 // allow italics and bold. 142 policy.AllowElements("i", "b", "em", "strong") 143 144 // allow code. 145 policy.AllowElements("code") 146 147 // allow links 148 policy.AllowAttrs("href", "target", "rel").OnElements("a") 149 150 return policy 151}