Allow images with data URLs
Only URLs with a mime-type image/* are allowed
This commit is contained in:
parent
9a9a271b1f
commit
864dd9f219
4 changed files with 34 additions and 3 deletions
|
@ -96,7 +96,7 @@ func (b *Builder) writeHeaders() {
|
||||||
b.headers["X-XSS-Protection"] = "1; mode=block"
|
b.headers["X-XSS-Protection"] = "1; mode=block"
|
||||||
b.headers["X-Content-Type-Options"] = "nosniff"
|
b.headers["X-Content-Type-Options"] = "nosniff"
|
||||||
b.headers["X-Frame-Options"] = "DENY"
|
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 {
|
for key, value := range b.headers {
|
||||||
b.w.Header().Set(key, value)
|
b.w.Header().Set(key, value)
|
||||||
|
|
|
@ -32,7 +32,7 @@ func TestResponseHasCommonHeaders(t *testing.T) {
|
||||||
"X-XSS-Protection": "1; mode=block",
|
"X-XSS-Protection": "1; mode=block",
|
||||||
"X-Content-Type-Options": "nosniff",
|
"X-Content-Type-Options": "nosniff",
|
||||||
"X-Frame-Options": "DENY",
|
"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 {
|
for header, expected := range headers {
|
||||||
|
|
|
@ -111,7 +111,7 @@ func sanitizeAttributes(baseURL, tagName string, attributes []html.Attribute) ([
|
||||||
} else {
|
} else {
|
||||||
continue
|
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
|
value = attribute.Val
|
||||||
} else {
|
} else {
|
||||||
value, err = url.AbsoluteURL(baseURL, value)
|
value, err = url.AbsoluteURL(baseURL, value)
|
||||||
|
@ -480,3 +480,24 @@ func isValidWidthOrDensityDescriptor(value string) bool {
|
||||||
_, err := strconv.ParseFloat(value[0:len(value)-1], 32)
|
_, err := strconv.ParseFloat(value[0:len(value)-1], 32)
|
||||||
return err == nil
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,16 @@ func TestValidInput(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestImgWithTextDataURL(t *testing.T) {
|
||||||
|
input := `<img src="data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==" alt="Example">`
|
||||||
|
expected := ``
|
||||||
|
output := Sanitize("http://example.org/", input)
|
||||||
|
|
||||||
|
if output != expected {
|
||||||
|
t.Errorf(`Wrong output: %s`, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestImgWithDataURL(t *testing.T) {
|
func TestImgWithDataURL(t *testing.T) {
|
||||||
input := `<img src="data:image/gif;base64,test" alt="Example">`
|
input := `<img src="data:image/gif;base64,test" alt="Example">`
|
||||||
expected := `<img src="data:image/gif;base64,test" alt="Example" loading="lazy">`
|
expected := `<img src="data:image/gif;base64,test" alt="Example" loading="lazy">`
|
||||||
|
|
Loading…
Reference in a new issue