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