diff --git a/modules/markup/markdown.go b/modules/markup/markdown.go
index fa91553ab..51afe48ec 100644
--- a/modules/markup/markdown.go
+++ b/modules/markup/markdown.go
@@ -14,7 +14,6 @@ import (
"strings"
"github.com/Unknwon/com"
- "github.com/microcosm-cc/bluemonday"
"github.com/russross/blackfriday"
"golang.org/x/net/html"
@@ -27,22 +26,6 @@ const (
ISSUE_NAME_STYLE_ALPHANUMERIC = "alphanumeric"
)
-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() {
- // We only want to allow HighlightJS specific classes for code blocks
- Sanitizer.AllowAttrs("class").Matching(regexp.MustCompile(`^language-\w+`)).OnElements("code")
-
- // Checkboxes
- Sanitizer.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input")
- Sanitizer.AllowAttrs("checked", "disabled").OnElements("input")
-
- // Custom URL-Schemes
- Sanitizer.AllowURLSchemes(setting.Markdown.CustomURLSchemes...)
-}
-
var validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://|^mailto:`)
// isLink reports whether link fits valid format.
@@ -480,7 +463,7 @@ func Render(rawBytes []byte, urlPrefix string, metas map[string]string) []byte {
urlPrefix = strings.Replace(urlPrefix, space, spaceEncoded, -1)
result := RenderRaw(rawBytes, urlPrefix)
result = PostProcess(result, urlPrefix, metas)
- result = Sanitizer.SanitizeBytes(result)
+ result = SanitizeBytes(result)
return result
}
diff --git a/modules/markup/markdown_test.go b/modules/markup/markdown_test.go
index 2d06a149f..cbf753121 100644
--- a/modules/markup/markdown_test.go
+++ b/modules/markup/markdown_test.go
@@ -1,13 +1,14 @@
package markup_test
import (
- . "github.com/gogits/gogs/modules/markup"
- . "github.com/smartystreets/goconvey/convey"
+ "bytes"
"testing"
- "bytes"
- "github.com/gogits/gogs/modules/setting"
"github.com/russross/blackfriday"
+ . "github.com/smartystreets/goconvey/convey"
+
+ . "github.com/gogits/gogs/modules/markup"
+ "github.com/gogits/gogs/modules/setting"
)
func TestMarkdown(t *testing.T) {
diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go
new file mode 100644
index 000000000..f685657bf
--- /dev/null
+++ b/modules/markup/sanitizer.go
@@ -0,0 +1,55 @@
+// 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 markup
+
+import (
+ "regexp"
+ "sync"
+
+ "github.com/microcosm-cc/bluemonday"
+ log "gopkg.in/clog.v1"
+
+ "github.com/gogits/gogs/modules/setting"
+)
+
+// 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("Markup: 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("Markup: sanitizer initialized")
+ })
+}
+
+// Sanitize takes a string that contains a HTML fragment or document and applies policy whitelist.
+func Sanitize(s string) string {
+ 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 {
+ return sanitizer.policy.SanitizeBytes(b)
+}
diff --git a/modules/markup/sanitizer_test.go b/modules/markup/sanitizer_test.go
new file mode 100644
index 000000000..a8f41fbf8
--- /dev/null
+++ b/modules/markup/sanitizer_test.go
@@ -0,0 +1,38 @@
+// 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 markup_test
+
+import (
+ "testing"
+
+ . "github.com/smartystreets/goconvey/convey"
+
+ . "github.com/gogits/gogs/modules/markup"
+)
+
+func Test_Sanitizer(t *testing.T) {
+ BuildSanitizer()
+ Convey("Sanitize HTML string and bytes", t, func() {
+ testCases := []string{
+ // Regular
+ `Google`, `Google`,
+
+ // Code highlighting class
+ `
`, `
`,
+ `
`, `
`,
+ `
`, `
`,
+
+ // Input checkbox
+ ``, ``,
+ ``, ``,
+ ``, ``,
+ }
+
+ for i := 0; i < len(testCases); i += 2 {
+ So(Sanitize(testCases[i]), ShouldEqual, testCases[i+1])
+ So(string(SanitizeBytes([]byte(testCases[i]))), ShouldEqual, testCases[i+1])
+ }
+ })
+}
diff --git a/modules/template/template.go b/modules/template/template.go
index 0bd5fa3f1..faae266ba 100644
--- a/modules/template/template.go
+++ b/modules/template/template.go
@@ -125,7 +125,7 @@ func Safe(raw string) template.HTML {
}
func Str2html(raw string) template.HTML {
- return template.HTML(markup.Sanitizer.Sanitize(raw))
+ return template.HTML(markup.Sanitize(raw))
}
func List(l *list.List) chan interface{} {
diff --git a/routers/install.go b/routers/install.go
index 2e6bee100..9d9bb6d18 100644
--- a/routers/install.go
+++ b/routers/install.go
@@ -62,7 +62,7 @@ func GlobalInit() {
if setting.InstallLock {
highlight.NewContext()
- markup.BuildSanitizer()
+ markup.NewSanitizer()
if err := models.NewEngine(); err != nil {
log.Fatal(2, "Fail to initialize ORM engine: %v", err)
}