diff --git a/http/response/builder.go b/http/response/builder.go index cff217e7..43e64c88 100644 --- a/http/response/builder.go +++ b/http/response/builder.go @@ -96,7 +96,7 @@ func (b *Builder) writeHeaders() { b.headers["X-XSS-Protection"] = "1; mode=block" b.headers["X-Content-Type-Options"] = "nosniff" b.headers["X-Frame-Options"] = "DENY" - b.headers["Content-Security-Policy"] = "default-src 'self'; img-src *; media-src *; frame-src *" + b.headers["Content-Security-Policy"] = "default-src 'self'; img-src * data:; media-src *; frame-src *" for key, value := range b.headers { b.w.Header().Set(key, value) diff --git a/http/response/builder_test.go b/http/response/builder_test.go index d2438a01..a2f33072 100644 --- a/http/response/builder_test.go +++ b/http/response/builder_test.go @@ -32,7 +32,7 @@ func TestResponseHasCommonHeaders(t *testing.T) { "X-XSS-Protection": "1; mode=block", "X-Content-Type-Options": "nosniff", "X-Frame-Options": "DENY", - "Content-Security-Policy": "default-src 'self'; img-src *; media-src *; frame-src *", + "Content-Security-Policy": "default-src 'self'; img-src * data:; media-src *; frame-src *", } for header, expected := range headers { diff --git a/reader/sanitizer/sanitizer.go b/reader/sanitizer/sanitizer.go index 477c98e2..2da7b483 100644 --- a/reader/sanitizer/sanitizer.go +++ b/reader/sanitizer/sanitizer.go @@ -111,7 +111,7 @@ func sanitizeAttributes(baseURL, tagName string, attributes []html.Attribute) ([ } else { continue } - } else if tagName == "img" && attribute.Key == "src" && strings.HasPrefix(attribute.Val, "data:") { + } else if tagName == "img" && attribute.Key == "src" && isValidDataAttribute(attribute.Val) { value = attribute.Val } else { value, err = url.AbsoluteURL(baseURL, value) @@ -480,3 +480,24 @@ func isValidWidthOrDensityDescriptor(value string) bool { _, err := strconv.ParseFloat(value[0:len(value)-1], 32) return err == nil } + +func isValidDataAttribute(value string) bool { + var dataAttributeAllowList = []string{ + "data:image/avif", + "data:image/apng", + "data:image/png", + "data:image/svg", + "data:image/svg+xml", + "data:image/jpg", + "data:image/jpeg", + "data:image/gif", + "data:image/webp", + } + + for _, prefix := range dataAttributeAllowList { + if strings.HasPrefix(value, prefix) { + return true + } + } + return false +} diff --git a/reader/sanitizer/sanitizer_test.go b/reader/sanitizer/sanitizer_test.go index f19e2cf9..9bf4528e 100644 --- a/reader/sanitizer/sanitizer_test.go +++ b/reader/sanitizer/sanitizer_test.go @@ -15,6 +15,16 @@ func TestValidInput(t *testing.T) { } } +func TestImgWithTextDataURL(t *testing.T) { + input := `Example` + expected := `` + output := Sanitize("http://example.org/", input) + + if output != expected { + t.Errorf(`Wrong output: %s`, output) + } +} + func TestImgWithDataURL(t *testing.T) { input := `Example` expected := `Example`