Skip to content

Commit 0aea20d

Browse files
committed
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 dd44073 commit 0aea20d

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
@@ -249,6 +249,20 @@ def test_custom_attributes_overrides_allowed_attributes
249249
end
250250
end
251251

252+
def test_setting_default_prune_affects_sanitization
253+
scope_prune true do |sanitizer|
254+
input = '<u>leave me <b>now</b></u>'
255+
assert_equal '<u>leave me </u>', sanitizer.sanitize(input, tags: %w(u))
256+
end
257+
end
258+
259+
def test_custom_prune_overrides_default_prune
260+
scope_prune true do |sanitizer|
261+
input = '<u>leave me <b>now</b></u>'
262+
assert_equal '<u>leave me now</u>', sanitizer.sanitize(input, tags: %w(u), prune: false)
263+
end
264+
end
265+
252266
def test_should_allow_custom_tags
253267
text = "<u>foo</u>"
254268
assert_equal text, safe_list_sanitize(text, tags: %w(u))
@@ -616,6 +630,14 @@ def scope_allowed_attributes(attributes)
616630
Rails::Html::SafeListSanitizer.allowed_attributes = old_attributes
617631
end
618632

633+
def scope_prune(prune)
634+
old_prune = Rails::Html::SafeListSanitizer.prune
635+
Rails::Html::SafeListSanitizer.prune = prune
636+
yield Rails::Html::SafeListSanitizer.new
637+
ensure
638+
Rails::Html::SafeListSanitizer.prune = old_prune
639+
end
640+
619641
# note that this is used for testing CSS hex encoding: \\[0-9a-f]{1,6}
620642
def convert_to_css_hex(string, escape_parens=false)
621643
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)