diff --git a/backend/app/store/comment.go b/backend/app/store/comment.go
index ba4e008f..f9ee42a0 100644
--- a/backend/app/store/comment.go
+++ b/backend/app/store/comment.go
@@ -1,6 +1,7 @@
package store
import (
+ "fmt"
"html/template"
"regexp"
"strings"
@@ -125,7 +126,8 @@ func (c *Comment) Sanitize() {
c.Orig = p.Sanitize(c.Orig)
c.User.ID = template.HTMLEscapeString(c.User.ID)
c.User.Name = c.escapeHTMLWithSome(c.User.Name)
- c.User.Picture = p.Sanitize(c.User.Picture)
+ c.User.Picture = c.sanitizeAsURL(c.User.Picture)
+ c.Locator.URL = c.sanitizeAsURL(c.Locator.URL)
}
// Snippet from comment's text
@@ -149,6 +151,20 @@ func (c *Comment) Snippet(limit int) string {
return string(snippet) + " ..."
}
+var reHref = regexp.MustCompile(`]*?\s+)?href="([^"]*)"`)
+
+// wrap with href to trigger bluemonday sanitizer
+// clean href after sanitizing done
+func (c *Comment) sanitizeAsURL(inp string) string {
+ h := fmt.Sprintf(``, inp)
+ clean := bluemonday.UGCPolicy().Sanitize(h)
+ match := reHref.FindStringSubmatch(clean)
+ if len(match) > 1 {
+ return match[1]
+ }
+ return "" // this shouldn't happen as we build the href
+}
+
func (c *Comment) escapeHTMLWithSome(inp string) string {
res := template.HTMLEscapeString(inp)
res = strings.Replace(res, """, "\"", -1)
diff --git a/backend/app/store/comment_test.go b/backend/app/store/comment_test.go
index 2fc413f7..2ffe2ac9 100644
--- a/backend/app/store/comment_test.go
+++ b/backend/app/store/comment_test.go
@@ -14,6 +14,7 @@ func TestComment_Sanitize(t *testing.T) {
inp Comment
out Comment
}{
+
{inp: Comment{}, out: Comment{}},
{
inp: Comment{
@@ -47,6 +48,25 @@ func TestComment_Sanitize(t *testing.T) {
inp: Comment{Text: "blah & & 123", User: User{Name: "name <> & ' ` \""}},
out: Comment{Text: `blah & & 123`, User: User{Name: "name <> & ' ` \""}},
},
+
+ {
+ inp: Comment{Text: "blah blah", Locator: Locator{URL: "javascript:alert('XSS1')"}},
+ out: Comment{Text: "blah blah", Locator: Locator{URL: ""}},
+ },
+ {
+ inp: Comment{Text: "blah blah", Locator: Locator{URL: "javascript:alert(document.domain)//"}},
+ out: Comment{Text: "blah blah", Locator: Locator{URL: ""}},
+ },
+ {
+ inp: Comment{Text: "blah blah", Locator: Locator{URL: ""}},
+ out: Comment{Text: "blah blah", Locator: Locator{URL: "%3Cscript%3Ealert%28%29%3C/script%3E"}},
+ },
+ {
+ inp: Comment{Text: "blah blah",
+ Locator: Locator{URL: "/p/2021/03/23/prep-747/#remark42__comment-1b365913-7056-4920-b9ad-01304bdda085"}},
+ out: Comment{Text: "blah blah",
+ Locator: Locator{URL: "/p/2021/03/23/prep-747/#remark42__comment-1b365913-7056-4920-b9ad-01304bdda085"}},
+ },
}
for n, tt := range tbl {
@@ -159,3 +179,40 @@ func TestComment_Snippet(t *testing.T) {
})
}
}
+
+func TestComment_sanitizeAsURL(t *testing.T) {
+
+ tbl := []struct {
+ inp, out string
+ }{
+ {
+ "/p/2021/03/23/prep-747/#remark42__comment-1b365913-7056-4920-b9ad-01304bdda085",
+ "/p/2021/03/23/prep-747/#remark42__comment-1b365913-7056-4920-b9ad-01304bdda085",
+ },
+ {
+ "https://radio-t.com/p/2021/03/23/prep-747/#remark42__comment-1b365913-7056-4920-b9ad-01304bdda085",
+ "https://radio-t.com/p/2021/03/23/prep-747/#remark42__comment-1b365913-7056-4920-b9ad-01304bdda085",
+ },
+ {
+ "javascript:alert(document.domain)//",
+ "",
+ },
+ {
+ "",
+ "%3Cscript%3Ealert%28%29%3C/script%3E",
+ },
+ {
+ "xxx",
+ "",
+ },
+ }
+
+ for i, tt := range tbl {
+ tt := tt
+ c := Comment{}
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ assert.Equal(t, tt.out, c.sanitizeAsURL(tt.inp))
+ })
+ }
+
+}