diff options
author | Kim "BKC" Carlbäcker <kim.carlbacker@gmail.com> | 2017-04-13 04:52:24 +0200 |
---|---|---|
committer | Lunny Xiao <xiaolunwen@gmail.com> | 2017-04-13 10:52:24 +0800 |
commit | d409d3ab57894de853bbc5fbacf32628b4d8fa1e (patch) | |
tree | 3387b30bad4f0f3fbc740b28cdbf4e23620ce96d /modules | |
parent | 21290d4e80711fb0de8dd101db8c6d1c5171a627 (diff) | |
download | gitea-d409d3ab57894de853bbc5fbacf32628b4d8fa1e.tar.gz gitea-d409d3ab57894de853bbc5fbacf32628b4d8fa1e.zip |
Sanitation fix from Gogs (#1461)
* Santiation fix from Gogs
* Linting
* Fix build-errors
* still not working
* Fix all the things!
* gofmt
* Add code-injection checks
Diffstat (limited to 'modules')
-rw-r--r-- | modules/markdown/markdown.go | 21 | ||||
-rw-r--r-- | modules/markdown/sanitizer.go | 66 | ||||
-rw-r--r-- | modules/markdown/sanitizer_test.go | 44 | ||||
-rw-r--r-- | modules/templates/helper.go | 2 |
4 files changed, 112 insertions, 21 deletions
diff --git a/modules/markdown/markdown.go b/modules/markdown/markdown.go index 52459e360e..813fabe178 100644 --- a/modules/markdown/markdown.go +++ b/modules/markdown/markdown.go @@ -15,7 +15,6 @@ import ( "strings" "github.com/Unknwon/com" - "github.com/microcosm-cc/bluemonday" "github.com/russross/blackfriday" "golang.org/x/net/html" @@ -29,24 +28,6 @@ const ( IssueNameStyleAlphanumeric = "alphanumeric" ) -// Sanitizer markdown sanitizer -var Sanitizer = bluemonday.UGCPolicy() - -// BuildSanitizer initializes sanitizer with allowed attributes based on settings. -// This function should only be called once during entire application lifecycle. -func BuildSanitizer() { - // Normal markdown-stuff - Sanitizer.AllowAttrs("class").Matching(regexp.MustCompile(`[\p{L}\p{N}\s\-_',:\[\]!\./\\\(\)&]*`)).OnElements("code", "div", "ul", "ol", "dl") - - // Checkboxes - Sanitizer.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input") - Sanitizer.AllowAttrs("checked", "disabled").OnElements("input") - Sanitizer.AllowNoAttrs().OnElements("label") - - // Custom URL-Schemes - Sanitizer.AllowURLSchemes(setting.Markdown.CustomURLSchemes...) -} - // IsMarkdownFile reports whether name looks like a Markdown file // based on its extension. func IsMarkdownFile(name string) bool { @@ -708,7 +689,7 @@ func render(rawBytes []byte, urlPrefix string, metas map[string]string, isWikiMa urlPrefix = strings.Replace(urlPrefix, " ", "+", -1) result := RenderRaw(rawBytes, urlPrefix, isWikiMarkdown) result = PostProcess(result, urlPrefix, metas, isWikiMarkdown) - result = Sanitizer.SanitizeBytes(result) + result = SanitizeBytes(result) return result } diff --git a/modules/markdown/sanitizer.go b/modules/markdown/sanitizer.go new file mode 100644 index 0000000000..14e8fc1b22 --- /dev/null +++ b/modules/markdown/sanitizer.go @@ -0,0 +1,66 @@ +// Copyright 2017 The Gitea Authors. All rights reserved. +// Copyright 2017 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package markdown + +import ( + "regexp" + "sync" + + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + + "github.com/microcosm-cc/bluemonday" +) + +// 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 +} + +var sanitizer = &Sanitizer{} + +// NewSanitizer initializes sanitizer with allowed attributes based on settings. +// Multiple calls to this function will only create one instance of Sanitizer during +// entire application lifecycle. +func NewSanitizer() { + log.Trace("Markdown: sanitizer initialization requested") + sanitizer.init.Do(func() { + sanitizer.policy = bluemonday.UGCPolicy() + // We only want to allow HighlightJS specific classes for code blocks + sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^language-\w+$`)).OnElements("code") + + // Checkboxes + sanitizer.policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input") + sanitizer.policy.AllowAttrs("checked", "disabled").OnElements("input") + + // Custom URL-Schemes + sanitizer.policy.AllowURLSchemes(setting.Markdown.CustomURLSchemes...) + + log.Trace("Markdown: sanitizer initialized") + }) +} + +// Sanitize takes a string that contains a HTML fragment or document and applies policy whitelist. +func Sanitize(s string) string { + if sanitizer.policy == nil { + NewSanitizer() + } + return sanitizer.policy.Sanitize(s) +} + +// SanitizeBytes takes a []byte slice that contains a HTML fragment or document and applies policy whitelist. +func SanitizeBytes(b []byte) []byte { + if len(b) == 0 { + // nothing to sanitize + return b + } + if sanitizer.policy == nil { + NewSanitizer() + } + return sanitizer.policy.SanitizeBytes(b) +} diff --git a/modules/markdown/sanitizer_test.go b/modules/markdown/sanitizer_test.go new file mode 100644 index 0000000000..77a4b33c84 --- /dev/null +++ b/modules/markdown/sanitizer_test.go @@ -0,0 +1,44 @@ +// Copyright 2017 The Gitea Authors. All rights reserved. +// Copyright 2017 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package markdown + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_Sanitizer(t *testing.T) { + NewSanitizer() + testCases := []string{ + // Regular + `<a onblur="alert(secret)" href="http://www.google.com">Google</a>`, `<a href="http://www.google.com" rel="nofollow">Google</a>`, + + // Code highlighting class + `<code class="random string"></code>`, `<code></code>`, + `<code class="language-random ui tab active menu attached animating sidebar following bar center"></code>`, `<code></code>`, + `<code class="language-go"></code>`, `<code class="language-go"></code>`, + + // Input checkbox + `<input type="hidden">`, ``, + `<input type="checkbox">`, `<input type="checkbox">`, + `<input checked disabled autofocus>`, `<input checked="" disabled="">`, + + // Code highlight injection + `<code class="language-random ui tab active menu attached animating sidebar following bar center"></code>`, `<code></code>`, + `<code class="language-lol ui tab active menu attached animating sidebar following bar center"> +<code class="language-lol ui container input huge basic segment center"> </code> +<img src="https://try.gogs.io/img/favicon.png" width="200" height="200"> +<code class="language-lol ui container input massive basic segment">Hello there! Something has gone wrong, we are working on it.</code> +<code class="language-lol ui container input huge basic segment">In the meantime, play a game with us at <a href="http://example.com/">example.com</a>.</code> +</code>`, "<code>\n<code>\u00a0</code>\n<img src=\"https://try.gogs.io/img/favicon.png\" width=\"200\" height=\"200\">\n<code>Hello there! Something has gone wrong, we are working on it.</code>\n<code>In the meantime, play a game with us at\u00a0<a href=\"http://example.com/\" rel=\"nofollow\">example.com</a>.</code>\n</code>", + } + + for i := 0; i < len(testCases); i += 2 { + assert.Equal(t, testCases[i+1], Sanitize(testCases[i])) + assert.Equal(t, testCases[i+1], string(SanitizeBytes([]byte(testCases[i])))) + } +} diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 51877f8039..235b649547 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -164,7 +164,7 @@ func Safe(raw string) template.HTML { // Str2html render Markdown text to HTML func Str2html(raw string) template.HTML { - return template.HTML(markdown.Sanitizer.Sanitize(raw)) + return template.HTML(markdown.Sanitize(raw)) } // List traversings the list |