diff --git a/CHANGELOG.md b/CHANGELOG.md index d6399fb..593b4e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## next / unreleased + +* `SafeListSanitizer`, `PermitScrubber`, and `TargetScrubber` now all support pruning of unsafe tags. + + By default, unsafe tags are still stripped, but this behavior can be changed to prune the element + and its children from the document by passing `prune: true` to any of these classes' constructors. + + *seyrian* + ## 1.4.2 / 2021-08-23 * Slightly improve performance. diff --git a/README.md b/README.md index af42d58..b6d368c 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,9 @@ safe_list_sanitizer.sanitize(@article.body, scrubber: ArticleScrubber.new) # safe list sanitizer can also sanitize css safe_list_sanitizer.sanitize_css('background-color: #000;') + +# fully prune nodes from the tree instead of stripping tags and leaving inner content +safe_list_sanitizer = Rails::Html::SafeListSanitizer.new(prune: true) ``` ### Scrubbers @@ -107,6 +110,24 @@ html_fragment.scrub!(scrubber) html_fragment.to_s # => "" ``` +By default, inner content is left, but it can be removed as well. + +```ruby +scrubber = Rails::Html::PermitScrubber.new +scrubber.tags = ['a'] + +html_fragment = Loofah.fragment('text') +html_fragment.scrub!(scrubber) +html_fragment.to_s # => "text" + +scrubber = Rails::Html::PermitScrubber.new(prune: true) +scrubber.tags = ['a'] + +html_fragment = Loofah.fragment('text') +html_fragment.scrub!(scrubber) +html_fragment.to_s # => "" +``` + #### `Rails::Html::TargetScrubber` Where `PermitScrubber` picks out tags and attributes to permit in sanitization, @@ -124,6 +145,23 @@ html_fragment.scrub!(scrubber) html_fragment.to_s # => "" ``` +Similarly to `PermitScrubber`, nodes can be fully pruned. + +```ruby +scrubber = Rails::Html::TargetScrubber.new +scrubber.tags = ['span'] + +html_fragment = Loofah.fragment('text') +html_fragment.scrub!(scrubber) +html_fragment.to_s # => "text" + +scrubber = Rails::Html::TargetScrubber.new(prune: true) +scrubber.tags = ['span'] + +html_fragment = Loofah.fragment('text') +html_fragment.scrub!(scrubber) +html_fragment.to_s # => "" +``` #### Custom Scrubbers You can also create custom scrubbers in your application if you want to. diff --git a/lib/rails/html/sanitizer.rb b/lib/rails/html/sanitizer.rb index 5633ca1..ffd6764 100644 --- a/lib/rails/html/sanitizer.rb +++ b/lib/rails/html/sanitizer.rb @@ -110,8 +110,8 @@ class << self acronym a img blockquote del ins)) self.allowed_attributes = Set.new(%w(href src width height alt cite datetime title class name xml:lang abbr)) - def initialize - @permit_scrubber = PermitScrubber.new + def initialize(prune: false) + @permit_scrubber = PermitScrubber.new(prune: prune) end def sanitize(html, options = {}) diff --git a/lib/rails/html/scrubbers.rb b/lib/rails/html/scrubbers.rb index 09cfe95..13b6d6f 100644 --- a/lib/rails/html/scrubbers.rb +++ b/lib/rails/html/scrubbers.rb @@ -45,10 +45,11 @@ module Html # See the documentation for +Nokogiri::XML::Node+ to understand what's possible # with nodes: https://nokogiri.org/rdoc/Nokogiri/XML/Node.html class PermitScrubber < Loofah::Scrubber - attr_reader :tags, :attributes + attr_reader :tags, :attributes, :prune - def initialize - @direction = :bottom_up + def initialize(prune: false) + @prune = prune + @direction = @prune ? :top_down : :bottom_up @tags, @attributes = nil, nil end @@ -98,7 +99,7 @@ def keep_node?(node) end def scrub_node(node) - node.before(node.children) # strip + node.before(node.children) unless prune # strip node.remove end diff --git a/test/sanitizer_test.rb b/test/sanitizer_test.rb index 8b0b7ab..23f41e1 100644 --- a/test/sanitizer_test.rb +++ b/test/sanitizer_test.rb @@ -258,6 +258,12 @@ def test_custom_attributes_overrides_allowed_attributes end end + def test_should_allow_prune + sanitizer = Rails::Html::SafeListSanitizer.new(prune: true) + text = 'leave me now' + assert_equal "leave me ", sanitizer.sanitize(text, tags: %w(u)) + end + def test_should_allow_custom_tags text = "foo" assert_equal text, safe_list_sanitize(text, tags: %w(u)) diff --git a/test/scrubbers_test.rb b/test/scrubbers_test.rb index a825404..a86536e 100644 --- a/test/scrubbers_test.rb +++ b/test/scrubbers_test.rb @@ -66,6 +66,13 @@ def test_leaves_only_supplied_tags assert_scrubbed html, 'leave me now' end + def test_prunes_tags + @scrubber = Rails::Html::PermitScrubber.new(prune: true) + @scrubber.tags = %w(tag) + html = 'leave me now' + assert_scrubbed html, 'leave me ' + end + def test_leaves_comments_when_supplied_as_tag @scrubber.tags = %w(div comment) assert_scrubbed('
one
three', @@ -157,6 +164,13 @@ def test_targeting_tags_and_attributes_removes_only_them html = '' assert_scrubbed html, '' end + + def test_prunes_tags + @scrubber = Rails::Html::TargetScrubber.new(prune: true) + @scrubber.tags = %w(span) + html = 'leave me now' + assert_scrubbed html, 'leave me ' + end end class TextOnlyScrubberTest < ScrubberTest