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