summaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorKim "BKC" Carlbäcker <kim.carlbacker@gmail.com>2017-04-13 04:52:24 +0200
committerLunny Xiao <xiaolunwen@gmail.com>2017-04-13 10:52:24 +0800
commitd409d3ab57894de853bbc5fbacf32628b4d8fa1e (patch)
tree3387b30bad4f0f3fbc740b28cdbf4e23620ce96d /modules
parent21290d4e80711fb0de8dd101db8c6d1c5171a627 (diff)
downloadgitea-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.go21
-rw-r--r--modules/markdown/sanitizer.go66
-rw-r--r--modules/markdown/sanitizer_test.go44
-rw-r--r--modules/templates/helper.go2
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&#32;ui&#32;tab&#32;active&#32;menu&#32;attached&#32;animating&#32;sidebar&#32;following&#32;bar&#32;center"></code>`, `<code></code>`,
+ `<code class="language-lol&#32;ui&#32;tab&#32;active&#32;menu&#32;attached&#32;animating&#32;sidebar&#32;following&#32;bar&#32;center">
+<code class="language-lol&#32;ui&#32;container&#32;input&#32;huge&#32;basic&#32;segment&#32;center">&nbsp;</code>
+<img src="https://try.gogs.io/img/favicon.png" width="200" height="200">
+<code class="language-lol&#32;ui&#32;container&#32;input&#32;massive&#32;basic&#32;segment">Hello there! Something has gone wrong, we are working on it.</code>
+<code class="language-lol&#32;ui&#32;container&#32;input&#32;huge&#32;basic&#32;segment">In the meantime, play a game with us at&nbsp;<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