forked from tangled.org/core
Monorepo for Tangled

appview/pages: rework sanitizer

- initialize sanitizer once, and reuse for life
- improve policies, and allow sanitizer to hold multiple policies
(this will come in handy, for PR titles, repo description, profiles
description etc.)
- add general safe items to allow list, most of these are generated by
goldmark GFM

Signed-off-by: oppiliappan <me@oppi.li>

authored by oppi.li and committed by Tangled f5c2ed8f 55f1fc1f

Changed files
+64 -13
appview
pages
+55 -6
appview/pages/markup/markdown.go
··· 7 "io" 8 "net/url" 9 "path" 10 "strings" 11 12 "github.com/microcosm-cc/bluemonday" ··· 40 repoinfo.RepoInfo 41 IsDev bool 42 RendererType RendererType 43 } 44 45 func (rctx *RenderContext) RenderMarkdown(source string) string { ··· 145 } 146 } 147 148 - func (rctx *RenderContext) Sanitize(html string) string { 149 policy := bluemonday.UGCPolicy() 150 151 // video 152 - policy.AllowElements("video") 153 - policy.AllowAttrs("controls").OnElements("video") 154 - policy.AllowElements("source") 155 - policy.AllowAttrs("src", "type").OnElements("source") 156 157 // centering content 158 policy.AllowElements("center") ··· 173 "margin-top", 174 "margin-bottom", 175 ) 176 - return policy.Sanitize(html) 177 } 178 179 type MarkdownTransformer struct {
··· 7 "io" 8 "net/url" 9 "path" 10 + "regexp" 11 "strings" 12 13 "github.com/microcosm-cc/bluemonday" ··· 41 repoinfo.RepoInfo 42 IsDev bool 43 RendererType RendererType 44 + Sanitizer Sanitizer 45 + } 46 + 47 + type Sanitizer struct { 48 + defaultPolicy *bluemonday.Policy 49 } 50 51 func (rctx *RenderContext) RenderMarkdown(source string) string { ··· 151 } 152 } 153 154 + func (rctx *RenderContext) SanitizeDefault(html string) string { 155 + return rctx.Sanitizer.defaultPolicy.Sanitize(html) 156 + } 157 + 158 + func NewSanitizer() Sanitizer { 159 + return Sanitizer{ 160 + defaultPolicy: defaultPolicy(), 161 + } 162 + } 163 + func defaultPolicy() *bluemonday.Policy { 164 policy := bluemonday.UGCPolicy() 165 166 + // Allow generally safe attributes 167 + generalSafeAttrs := []string{ 168 + "abbr", "accept", "accept-charset", 169 + "accesskey", "action", "align", "alt", 170 + "aria-describedby", "aria-hidden", "aria-label", "aria-labelledby", 171 + "axis", "border", "cellpadding", "cellspacing", "char", 172 + "charoff", "charset", "checked", 173 + "clear", "cols", "colspan", "color", 174 + "compact", "coords", "datetime", "dir", 175 + "disabled", "enctype", "for", "frame", 176 + "headers", "height", "hreflang", 177 + "hspace", "ismap", "label", "lang", 178 + "maxlength", "media", "method", 179 + "multiple", "name", "nohref", "noshade", 180 + "nowrap", "open", "prompt", "readonly", "rel", "rev", 181 + "rows", "rowspan", "rules", "scope", 182 + "selected", "shape", "size", "span", 183 + "start", "summary", "tabindex", "target", 184 + "title", "type", "usemap", "valign", "value", 185 + "vspace", "width", "itemprop", 186 + } 187 + 188 + generalSafeElements := []string{ 189 + "h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8", "br", "b", "i", "strong", "em", "a", "pre", "code", "img", "tt", 190 + "div", "ins", "del", "sup", "sub", "p", "ol", "ul", "table", "thead", "tbody", "tfoot", "blockquote", "label", 191 + "dl", "dt", "dd", "kbd", "q", "samp", "var", "hr", "ruby", "rt", "rp", "li", "tr", "td", "th", "s", "strike", "summary", 192 + "details", "caption", "figure", "figcaption", 193 + "abbr", "bdo", "cite", "dfn", "mark", "small", "span", "time", "video", "wbr", 194 + } 195 + 196 + policy.AllowAttrs(generalSafeAttrs...).OnElements(generalSafeElements...) 197 + 198 // video 199 + policy.AllowAttrs("src", "autoplay", "controls").OnElements("video") 200 + 201 + // checkboxes 202 + policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input") 203 + policy.AllowAttrs("checked", "disabled", "data-source-position").OnElements("input") 204 205 // centering content 206 policy.AllowElements("center") ··· 221 "margin-top", 222 "margin-bottom", 223 ) 224 + 225 + return policy 226 } 227 228 type MarkdownTransformer struct {
+9 -7
appview/pages/pages.go
··· 59 IsDev: config.Core.Dev, 60 CamoUrl: config.Camo.Host, 61 CamoSecret: config.Camo.SharedSecret, 62 } 63 64 p := &Pages{ ··· 517 p.rctx.RendererType = markup.RendererTypeRepoMarkdown 518 519 if params.ReadmeFileName != "" { 520 - var htmlString string 521 ext := filepath.Ext(params.ReadmeFileName) 522 switch ext { 523 case ".md", ".markdown", ".mdown", ".mkdn", ".mkd": 524 - htmlString = p.rctx.Sanitize(htmlString) 525 - htmlString = p.rctx.RenderMarkdown(params.Readme) 526 params.Raw = false 527 - params.HTMLReadme = template.HTML(htmlString) 528 default: 529 params.Raw = true 530 } ··· 663 p.rctx.RepoInfo = params.RepoInfo 664 p.rctx.RendererType = markup.RendererTypeRepoMarkdown 665 htmlString := p.rctx.RenderMarkdown(params.Contents) 666 - params.RenderedContents = template.HTML(p.rctx.Sanitize(htmlString)) 667 } 668 } 669 ··· 1170 if params.ShowRendered { 1171 switch markup.GetFormat(params.String.Filename) { 1172 case markup.FormatMarkdown: 1173 - p.rctx.RendererType = markup.RendererTypeDefault 1174 htmlString := p.rctx.RenderMarkdown(params.String.Contents) 1175 - params.RenderedContents = template.HTML(p.rctx.Sanitize(htmlString)) 1176 } 1177 } 1178
··· 59 IsDev: config.Core.Dev, 60 CamoUrl: config.Camo.Host, 61 CamoSecret: config.Camo.SharedSecret, 62 + Sanitizer: markup.NewSanitizer(), 63 } 64 65 p := &Pages{ ··· 518 p.rctx.RendererType = markup.RendererTypeRepoMarkdown 519 520 if params.ReadmeFileName != "" { 521 ext := filepath.Ext(params.ReadmeFileName) 522 switch ext { 523 case ".md", ".markdown", ".mdown", ".mkdn", ".mkd": 524 params.Raw = false 525 + htmlString := p.rctx.RenderMarkdown(params.Readme) 526 + sanitized := p.rctx.SanitizeDefault(htmlString) 527 + params.HTMLReadme = template.HTML(sanitized) 528 default: 529 params.Raw = true 530 } ··· 663 p.rctx.RepoInfo = params.RepoInfo 664 p.rctx.RendererType = markup.RendererTypeRepoMarkdown 665 htmlString := p.rctx.RenderMarkdown(params.Contents) 666 + sanitized := p.rctx.SanitizeDefault(htmlString) 667 + params.RenderedContents = template.HTML(sanitized) 668 } 669 } 670 ··· 1171 if params.ShowRendered { 1172 switch markup.GetFormat(params.String.Filename) { 1173 case markup.FormatMarkdown: 1174 + p.rctx.RendererType = markup.RendererTypeRepoMarkdown 1175 htmlString := p.rctx.RenderMarkdown(params.String.Contents) 1176 + sanitized := p.rctx.SanitizeDefault(htmlString) 1177 + params.RenderedContents = template.HTML(sanitized) 1178 } 1179 } 1180