diff --git a/lib/sanitize/css.rb b/lib/sanitize/css.rb index c570a26..2741e76 100644 --- a/lib/sanitize/css.rb +++ b/lib/sanitize/css.rb @@ -272,6 +272,10 @@ def property!(prop) return nil unless valid_url?(child) end + if name == 'image-set' || name == 'image' + return nil unless valid_image?(child) + end + combined_value << name return nil if name == 'expression' || combined_value == 'expression' end @@ -345,4 +349,27 @@ def valid_url?(node) false end + # Returns `true` if the given node (which is an `image` or `image-set` function) contains only strings + # using an allowlisted protocol. + def valid_image?(node) + return false unless node[:node] == :function + return false unless node.key?(:name) && ['image', 'image-set'].include?(node[:name].downcase) + return false unless Array === node[:value] + + node[:value].each do |token| + return false unless Hash === token + + case token[:node] + when :string + if token[:value] =~ Sanitize::REGEX_PROTOCOL + return false unless @config[:protocols].include?($1.downcase) + else + return false unless @config[:protocols].include?(:relative) + end + else + next + end + end + end + end; end diff --git a/test/test_sanitize_css.rb b/test/test_sanitize_css.rb index 185b650..46df688 100644 --- a/test/test_sanitize_css.rb +++ b/test/test_sanitize_css.rb @@ -29,6 +29,12 @@ "background: url('ht\\tp://example.com/http.jpg')", "background: url(https://example.com/https.jpg)", "background: url('https://example.com/https.jpg')", + "background: image-set('relative.jpg' 1x, 'relative-2x.jpg' 2x)", + "background: image-set('https://example.com/https.jpg' 1x, 'https://example.com/https-2x.jpg' 2x)", + "background: image-set('https://example.com/https.jpg' type('image/jpeg'), 'https://example.com/https.avif' type('image/avif'))", + "background: image('relative.jpg');", + "background: image('https://example.com/https.jpg');", + "background: image(rtl 'https://example.com/https.jpg');" ].each do |css| _(@default.properties(css)).must_equal '' _(@relaxed.properties(css)).must_equal css