Fix escaping in post summary

Unescaping post content after sanitizing it. This will prevent double
escaping when summary is rendered by html/template package which does
escaping by default.
Fixes #340
This commit is contained in:
Josip Antoliš 2020-09-05 01:58:45 +02:00
parent 7c1244e6b1
commit eb76faa129
3 changed files with 44 additions and 8 deletions

View file

@ -140,9 +140,7 @@ func applyBasicMarkdown(data []byte) string {
func postTitle(content, friendlyId string) string { func postTitle(content, friendlyId string) string {
const maxTitleLen = 80 const maxTitleLen = 80
// Strip HTML tags with bluemonday's StrictPolicy, then unescape the HTML content = stripHTMLWithoutEscaping(content)
// entities added in by sanitizing the content.
content = html.UnescapeString(bluemonday.StrictPolicy().Sanitize(content))
content = strings.TrimLeftFunc(stripmd.Strip(content), unicode.IsSpace) content = strings.TrimLeftFunc(stripmd.Strip(content), unicode.IsSpace)
eol := strings.IndexRune(content, '\n') eol := strings.IndexRune(content, '\n')
@ -160,9 +158,7 @@ func postTitle(content, friendlyId string) string {
func friendlyPostTitle(content, friendlyId string) string { func friendlyPostTitle(content, friendlyId string) string {
const maxTitleLen = 80 const maxTitleLen = 80
// Strip HTML tags with bluemonday's StrictPolicy, then unescape the HTML content = stripHTMLWithoutEscaping(content)
// entities added in by sanitizing the content.
content = html.UnescapeString(bluemonday.StrictPolicy().Sanitize(content))
content = strings.TrimLeftFunc(stripmd.Strip(content), unicode.IsSpace) content = strings.TrimLeftFunc(stripmd.Strip(content), unicode.IsSpace)
eol := strings.IndexRune(content, '\n') eol := strings.IndexRune(content, '\n')
@ -179,6 +175,12 @@ func friendlyPostTitle(content, friendlyId string) string {
return title return title
} }
// Strip HTML tags with bluemonday's StrictPolicy, then unescape the HTML
// entities added in by sanitizing the content.
func stripHTMLWithoutEscaping(content string) string {
return html.UnescapeString(bluemonday.StrictPolicy().Sanitize(content))
}
func getSanitizationPolicy() *bluemonday.Policy { func getSanitizationPolicy() *bluemonday.Policy {
policy := bluemonday.UGCPolicy() policy := bluemonday.UGCPolicy()
policy.AllowAttrs("src", "style").OnElements("iframe", "video", "audio") policy.AllowAttrs("src", "style").OnElements("iframe", "video", "audio")

View file

@ -211,8 +211,7 @@ func (p Post) Summary() string {
if p.Content == "" { if p.Content == "" {
return "" return ""
} }
// Strip out HTML p.Content = stripHTMLWithoutEscaping(p.Content)
p.Content = bluemonday.StrictPolicy().Sanitize(p.Content)
// and Markdown // and Markdown
p.Content = stripmd.Strip(p.Content) p.Content = stripmd.Strip(p.Content)

35
posts_test.go Normal file
View file

@ -0,0 +1,35 @@
package writefreely_test
import (
"testing"
"github.com/guregu/null/zero"
"github.com/stretchr/testify/assert"
"github.com/writeas/writefreely"
)
func TestPostSummary(t *testing.T) {
testCases := map[string]struct {
given writefreely.Post
expected string
}{
"no special chars": {givenPost("Content."), "Content."},
"HTML content": {givenPost("Content <p>with a</p> paragraph."), "Content with a paragraph."},
"content with escaped char": {givenPost("Content&#39;s all OK."), "Content's all OK."},
"multiline content": {givenPost(`Content
in
multiple
lines.`), "Content in multiple lines."},
}
for name, test := range testCases {
t.Run(name, func(t *testing.T) {
actual := test.given.Summary()
assert.Equal(t, test.expected, actual)
})
}
}
func givenPost(content string) writefreely.Post {
return writefreely.Post{Title: zero.StringFrom("Title"), Content: content}
}