diff --git a/internal/reader/sanitizer/sanitizer.go b/internal/reader/sanitizer/sanitizer.go index 28b9d94b..fab21fa9 100644 --- a/internal/reader/sanitizer/sanitizer.go +++ b/internal/reader/sanitizer/sanitizer.go @@ -46,7 +46,7 @@ var ( "h6": {"id"}, "hr": {}, "iframe": {"width", "height", "frameborder", "src", "allowfullscreen"}, - "img": {"alt", "title", "src", "srcset", "sizes", "width", "height"}, + "img": {"alt", "title", "src", "srcset", "sizes", "width", "height", "fetchpriority", "decoding"}, "ins": {}, "kbd": {}, "li": {"id"}, @@ -234,6 +234,18 @@ func sanitizeAttributes(baseURL, tagName string, attributes []html.Attribute, sa continue } + if tagName == "img" && attribute.Key == "fetchpriority" { + if !isValidFetchPriorityValue(value) { + continue + } + } + + if tagName == "img" && attribute.Key == "decoding" { + if !isValidDecodingValue(value) { + continue + } + } + if (tagName == "img" || tagName == "source") && attribute.Key == "srcset" { value = sanitizeSrcsetAttr(baseURL, value) } @@ -540,3 +552,13 @@ func getIntegerAttributeValue(name string, attributes []html.Attribute) int { } return 0 } + +func isValidFetchPriorityValue(value string) bool { + allowedValues := []string{"high", "low", "auto"} + return slices.Contains(allowedValues, value) +} + +func isValidDecodingValue(value string) bool { + allowedValues := []string{"sync", "async", "auto"} + return slices.Contains(allowedValues, value) +} diff --git a/internal/reader/sanitizer/sanitizer_test.go b/internal/reader/sanitizer/sanitizer_test.go index 2e4d6424..afe2e928 100644 --- a/internal/reader/sanitizer/sanitizer_test.go +++ b/internal/reader/sanitizer/sanitizer_test.go @@ -139,6 +139,100 @@ func TestImgWithSrcsetAndNoSrcAttribute(t *testing.T) { } } +func TestImgWithFetchPriorityAttribute(t *testing.T) { + cases := []struct { + input string + expected string + }{ + { + ``, + ``, + }, + { + ``, + ``, + }, + { + ``, + ``, + }, + } + + for _, tc := range cases { + output := SanitizeHTMLWithDefaultOptions("http://example.org/", tc.input) + if output != tc.expected { + t.Errorf(`Wrong output for input %q: expected %q, got %q`, tc.input, tc.expected, output) + } + } +} + +func TestImgWithInvalidFetchPriorityAttribute(t *testing.T) { + input := `` + expected := `` + output := SanitizeHTMLWithDefaultOptions("http://example.org/", input) + + if output != expected { + t.Errorf(`Wrong output: expected %q, got %q`, expected, output) + } +} + +func TestNonImgWithFetchPriorityAttribute(t *testing.T) { + input := `

Text

` + expected := `

Text

` + output := SanitizeHTMLWithDefaultOptions("http://example.org/", input) + + if output != expected { + t.Errorf(`Wrong output: expected %q, got %q`, expected, output) + } +} + +func TestImgWithDecodingAttribute(t *testing.T) { + cases := []struct { + input string + expected string + }{ + { + ``, + ``, + }, + { + ``, + ``, + }, + { + ``, + ``, + }, + } + + for _, tc := range cases { + output := SanitizeHTMLWithDefaultOptions("http://example.org/", tc.input) + if output != tc.expected { + t.Errorf(`Wrong output for input %q: expected %q, got %q`, tc.input, tc.expected, output) + } + } +} + +func TestImgWithInvalidDecodingAttribute(t *testing.T) { + input := `` + expected := `` + output := SanitizeHTMLWithDefaultOptions("http://example.org/", input) + + if output != expected { + t.Errorf(`Wrong output: expected %q, got %q`, expected, output) + } +} + +func TestNonImgWithDecodingAttribute(t *testing.T) { + input := `

Text

` + expected := `

Text

` + output := SanitizeHTMLWithDefaultOptions("http://example.org/", input) + + if output != expected { + t.Errorf(`Wrong output: expected %q, got %q`, expected, output) + } +} + func TestSourceWithSrcsetAndMedia(t *testing.T) { input := `` expected := ``