aboutsummaryrefslogtreecommitdiffstats
path: root/modules/markup/sanitizer.go
diff options
context:
space:
mode:
authorKN4CK3R <admin@oldschoolhack.me>2021-06-23 23:09:51 +0200
committerGitHub <noreply@github.com>2021-06-23 17:09:51 -0400
commitc9c7afda1a80bda7b61ded222163db796132b78f (patch)
tree2145a5afe7c7a19d450b7def199dc302e1c8d6f2 /modules/markup/sanitizer.go
parenteb324a9402878a13029116bafa8ccce527796522 (diff)
downloadgitea-c9c7afda1a80bda7b61ded222163db796132b78f.tar.gz
gitea-c9c7afda1a80bda7b61ded222163db796132b78f.zip
Add sanitizer rules per renderer (#16110)
* Added sanitizer rules per renderer. * Updated documentation. Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Diffstat (limited to 'modules/markup/sanitizer.go')
-rw-r--r--modules/markup/sanitizer.go88
1 files changed, 57 insertions, 31 deletions
diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go
index 5611bd06ad..9342d65de5 100644
--- a/modules/markup/sanitizer.go
+++ b/modules/markup/sanitizer.go
@@ -19,8 +19,9 @@ import (
// Sanitizer is a protection wrapper of *bluemonday.Policy which does not allow
// any modification to the underlying policies once it's been created.
type Sanitizer struct {
- policy *bluemonday.Policy
- init sync.Once
+ defaultPolicy *bluemonday.Policy
+ rendererPolicies map[string]*bluemonday.Policy
+ init sync.Once
}
var sanitizer = &Sanitizer{}
@@ -30,47 +31,57 @@ var sanitizer = &Sanitizer{}
// entire application lifecycle.
func NewSanitizer() {
sanitizer.init.Do(func() {
- ReplaceSanitizer()
+ InitializeSanitizer()
})
}
-// ReplaceSanitizer replaces the current sanitizer to account for changes in settings
-func ReplaceSanitizer() {
- sanitizer.policy = bluemonday.UGCPolicy()
+// InitializeSanitizer (re)initializes the current sanitizer to account for changes in settings
+func InitializeSanitizer() {
+ sanitizer.rendererPolicies = map[string]*bluemonday.Policy{}
+ sanitizer.defaultPolicy = createDefaultPolicy()
+
+ for name, renderer := range renderers {
+ sanitizerRules := renderer.SanitizerRules()
+ if len(sanitizerRules) > 0 {
+ policy := createDefaultPolicy()
+ addSanitizerRules(policy, sanitizerRules)
+ sanitizer.rendererPolicies[name] = policy
+ }
+ }
+}
+
+func createDefaultPolicy() *bluemonday.Policy {
+ policy := bluemonday.UGCPolicy()
// For Chroma markdown plugin
- sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^is-loading$`)).OnElements("pre")
- sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(chroma )?language-[\w-]+$`)).OnElements("code")
+ policy.AllowAttrs("class").Matching(regexp.MustCompile(`^is-loading$`)).OnElements("pre")
+ policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(chroma )?language-[\w-]+$`)).OnElements("code")
// Checkboxes
- sanitizer.policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input")
- sanitizer.policy.AllowAttrs("checked", "disabled", "data-source-position").OnElements("input")
+ policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input")
+ policy.AllowAttrs("checked", "disabled", "data-source-position").OnElements("input")
// Custom URL-Schemes
if len(setting.Markdown.CustomURLSchemes) > 0 {
- sanitizer.policy.AllowURLSchemes(setting.Markdown.CustomURLSchemes...)
+ policy.AllowURLSchemes(setting.Markdown.CustomURLSchemes...)
}
// Allow classes for anchors
- sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`ref-issue`)).OnElements("a")
+ policy.AllowAttrs("class").Matching(regexp.MustCompile(`ref-issue`)).OnElements("a")
// Allow classes for task lists
- sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`task-list-item`)).OnElements("li")
+ policy.AllowAttrs("class").Matching(regexp.MustCompile(`task-list-item`)).OnElements("li")
// Allow icons
- sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^icon(\s+[\p{L}\p{N}_-]+)+$`)).OnElements("i")
+ policy.AllowAttrs("class").Matching(regexp.MustCompile(`^icon(\s+[\p{L}\p{N}_-]+)+$`)).OnElements("i")
// Allow unlabelled labels
- sanitizer.policy.AllowNoAttrs().OnElements("label")
+ policy.AllowNoAttrs().OnElements("label")
// Allow classes for emojis
- sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`emoji`)).OnElements("img")
+ policy.AllowAttrs("class").Matching(regexp.MustCompile(`emoji`)).OnElements("img")
// Allow icons, emojis, chroma syntax and keyword markup on span
- sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(emoji))$|^([a-z][a-z0-9]{0,2})$|^` + keywordClass + `$`)).OnElements("span")
-
- // Allow data tables
- sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`data-table`)).OnElements("table")
- sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`line-num`)).OnElements("th", "td")
+ policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(emoji))$|^([a-z][a-z0-9]{0,2})$|^` + keywordClass + `$`)).OnElements("span")
// Allow generally safe attributes
generalSafeAttrs := []string{"abbr", "accept", "accept-charset",
@@ -101,18 +112,29 @@ func ReplaceSanitizer() {
"abbr", "bdo", "cite", "dfn", "mark", "small", "span", "time", "wbr",
}
- sanitizer.policy.AllowAttrs(generalSafeAttrs...).OnElements(generalSafeElements...)
+ policy.AllowAttrs(generalSafeAttrs...).OnElements(generalSafeElements...)
- sanitizer.policy.AllowAttrs("itemscope", "itemtype").OnElements("div")
+ policy.AllowAttrs("itemscope", "itemtype").OnElements("div")
// FIXME: Need to handle longdesc in img but there is no easy way to do it
// Custom keyword markup
- for _, rule := range setting.ExternalSanitizerRules {
- if rule.Regexp != nil {
- sanitizer.policy.AllowAttrs(rule.AllowAttr).Matching(rule.Regexp).OnElements(rule.Element)
- } else {
- sanitizer.policy.AllowAttrs(rule.AllowAttr).OnElements(rule.Element)
+ addSanitizerRules(policy, setting.ExternalSanitizerRules)
+
+ return policy
+}
+
+func addSanitizerRules(policy *bluemonday.Policy, rules []setting.MarkupSanitizerRule) {
+ for _, rule := range rules {
+ if rule.AllowDataURIImages {
+ policy.AllowDataURIImages()
+ }
+ if rule.Element != "" {
+ if rule.Regexp != nil {
+ policy.AllowAttrs(rule.AllowAttr).Matching(rule.Regexp).OnElements(rule.Element)
+ } else {
+ policy.AllowAttrs(rule.AllowAttr).OnElements(rule.Element)
+ }
}
}
}
@@ -120,11 +142,15 @@ func ReplaceSanitizer() {
// Sanitize takes a string that contains a HTML fragment or document and applies policy whitelist.
func Sanitize(s string) string {
NewSanitizer()
- return sanitizer.policy.Sanitize(s)
+ return sanitizer.defaultPolicy.Sanitize(s)
}
// SanitizeReader sanitizes a Reader
-func SanitizeReader(r io.Reader) *bytes.Buffer {
+func SanitizeReader(r io.Reader, renderer string) *bytes.Buffer {
NewSanitizer()
- return sanitizer.policy.SanitizeReader(r)
+ policy, exist := sanitizer.rendererPolicies[renderer]
+ if !exist {
+ policy = sanitizer.defaultPolicy
+ }
+ return policy.SanitizeReader(r)
}