Skip to content

Commit 98195d4

Browse files
seyerianflavorjones
authored andcommitted
Add prune flag to PermitScrubber
By default PermitScrubber leaves behind inner content when scrubbing a node. Setting the `prune` flag will remove the node *and* its content.
1 parent 593a820 commit 98195d4

File tree

4 files changed

+40
-2
lines changed

4 files changed

+40
-2
lines changed

lib/rails/html/sanitizer.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,13 @@ class SafeListSanitizer < Sanitizer
104104
class << self
105105
attr_accessor :allowed_tags
106106
attr_accessor :allowed_attributes
107+
attr_accessor :prune
107108
end
108109
self.allowed_tags = Set.new(%w(strong em b i p code pre tt samp kbd var sub
109110
sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dl dt dd abbr
110111
acronym a img blockquote del ins))
111112
self.allowed_attributes = Set.new(%w(href src width height alt cite datetime title class name xml:lang abbr))
113+
self.prune = false
112114

113115
def initialize
114116
@permit_scrubber = PermitScrubber.new
@@ -123,9 +125,10 @@ def sanitize(html, options = {})
123125
if scrubber = options[:scrubber]
124126
# No duck typing, Loofah ensures subclass of Loofah::Scrubber
125127
loofah_fragment.scrub!(scrubber)
126-
elsif allowed_tags(options) || allowed_attributes(options)
128+
elsif allowed_tags(options) || allowed_attributes(options) || prune(options)
127129
@permit_scrubber.tags = allowed_tags(options)
128130
@permit_scrubber.attributes = allowed_attributes(options)
131+
@permit_scrubber.prune = prune(options)
129132
loofah_fragment.scrub!(@permit_scrubber)
130133
else
131134
remove_xpaths(loofah_fragment, XPATHS_TO_REMOVE)
@@ -148,6 +151,10 @@ def allowed_tags(options)
148151
def allowed_attributes(options)
149152
options[:attributes] || self.class.allowed_attributes
150153
end
154+
155+
def prune(options)
156+
options.key?(:prune) ? options[:prune] : self.class.prune
157+
end
151158
end
152159

153160
WhiteListSanitizer = SafeListSanitizer

lib/rails/html/scrubbers.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,12 @@ module Html
4545
# See the documentation for +Nokogiri::XML::Node+ to understand what's possible
4646
# with nodes: https://nokogiri.org/rdoc/Nokogiri/XML/Node.html
4747
class PermitScrubber < Loofah::Scrubber
48+
attr_accessor :prune
4849
attr_reader :tags, :attributes
4950

5051
def initialize
5152
@direction = :bottom_up
53+
@prune = false
5254
@tags, @attributes = nil, nil
5355
end
5456

@@ -98,7 +100,7 @@ def keep_node?(node)
98100
end
99101

100102
def scrub_node(node)
101-
node.before(node.children) # strip
103+
node.before(node.children) unless prune # strip
102104
node.remove
103105
end
104106

test/sanitizer_test.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,20 @@ def test_custom_attributes_overrides_allowed_attributes
258258
end
259259
end
260260

261+
def test_setting_default_prune_affects_sanitization
262+
scope_prune true do |sanitizer|
263+
input = '<u>leave me <b>now</b></u>'
264+
assert_equal '<u>leave me </u>', sanitizer.sanitize(input, tags: %w(u))
265+
end
266+
end
267+
268+
def test_custom_prune_overrides_default_prune
269+
scope_prune true do |sanitizer|
270+
input = '<u>leave me <b>now</b></u>'
271+
assert_equal '<u>leave me now</u>', sanitizer.sanitize(input, tags: %w(u), prune: false)
272+
end
273+
end
274+
261275
def test_should_allow_custom_tags
262276
text = "<u>foo</u>"
263277
assert_equal text, safe_list_sanitize(text, tags: %w(u))
@@ -629,6 +643,14 @@ def scope_allowed_attributes(attributes)
629643
Rails::Html::SafeListSanitizer.allowed_attributes = old_attributes
630644
end
631645

646+
def scope_prune(prune)
647+
old_prune = Rails::Html::SafeListSanitizer.prune
648+
Rails::Html::SafeListSanitizer.prune = prune
649+
yield Rails::Html::SafeListSanitizer.new
650+
ensure
651+
Rails::Html::SafeListSanitizer.prune = old_prune
652+
end
653+
632654
# note that this is used for testing CSS hex encoding: \\[0-9a-f]{1,6}
633655
def convert_to_css_hex(string, escape_parens=false)
634656
string.chars.map do |c|

test/scrubbers_test.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ def test_leaves_only_supplied_tags
6666
assert_scrubbed html, '<tag>leave me now</tag>'
6767
end
6868

69+
def test_prunes_tags
70+
html = '<tag>leave me <span>now</span></tag>'
71+
@scrubber.tags = %w(tag)
72+
@scrubber.prune = true
73+
assert_scrubbed html, '<tag>leave me </tag>'
74+
end
75+
6976
def test_leaves_comments_when_supplied_as_tag
7077
@scrubber.tags = %w(div comment)
7178
assert_scrubbed('<div>one</div><!-- two --><span>three</span>',

0 commit comments

Comments
 (0)