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 := ``
+ expected := ``
+ output := Sanitize("http://example.org/", input)
+
+ if output != expected {
+ t.Errorf(`Wrong output: %s`, output)
+ }
+}
+
func TestImgWithDataURL(t *testing.T) {
input := ``
expected := ``