Skip to content

Commit b19975f

Browse files
seyerianflavorjones
authored andcommitted
Add prune flag to PermitScrubber and SafeListSanitizer
By default PermitScrubber leaves behind inner content when scrubbing a node. Setting the `prune` flag will remove the node *and* its content. Note that this feature also works with TargetScrubber.
1 parent 593a820 commit b19975f

File tree

5 files changed

+65
-6
lines changed

5 files changed

+65
-6
lines changed

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ safe_list_sanitizer.sanitize(@article.body, scrubber: ArticleScrubber.new)
8686

8787
# safe list sanitizer can also sanitize css
8888
safe_list_sanitizer.sanitize_css('background-color: #000;')
89+
90+
# fully prune nodes from the tree instead of stripping tags and leaving inner content
91+
safe_list_sanitizer = Rails::Html::SafeListSanitizer.new(prune: true)
8992
```
9093

9194
### Scrubbers
@@ -107,6 +110,24 @@ html_fragment.scrub!(scrubber)
107110
html_fragment.to_s # => "<a></a>"
108111
```
109112

113+
By default, inner content is left, but it can be removed as well.
114+
115+
```ruby
116+
scrubber = Rails::Html::PermitScrubber.new
117+
scrubber.tags = ['a']
118+
119+
html_fragment = Loofah.fragment('<a><span>text</span></a>')
120+
html_fragment.scrub!(scrubber)
121+
html_fragment.to_s # => "<a>text</a>"
122+
123+
scrubber = Rails::Html::PermitScrubber.new(prune: true)
124+
scrubber.tags = ['a']
125+
126+
html_fragment = Loofah.fragment('<a><span>text</span></a>')
127+
html_fragment.scrub!(scrubber)
128+
html_fragment.to_s # => "<a></a>"
129+
```
130+
110131
#### `Rails::Html::TargetScrubber`
111132

112133
Where `PermitScrubber` picks out tags and attributes to permit in sanitization,
@@ -124,6 +145,23 @@ html_fragment.scrub!(scrubber)
124145
html_fragment.to_s # => "<a></a>"
125146
```
126147

148+
Similarly to `PermitScrubber`, nodes can be fully pruned.
149+
150+
```ruby
151+
scrubber = Rails::Html::TargetScrubber.new
152+
scrubber.tags = ['span']
153+
154+
html_fragment = Loofah.fragment('<a><span>text</span></a>')
155+
html_fragment.scrub!(scrubber)
156+
html_fragment.to_s # => "<a>text</a>"
157+
158+
scrubber = Rails::Html::TargetScrubber.new(prune: true)
159+
scrubber.tags = ['span']
160+
161+
html_fragment = Loofah.fragment('<a><span>text</span></a>')
162+
html_fragment.scrub!(scrubber)
163+
html_fragment.to_s # => "<a></a>"
164+
```
127165
#### Custom Scrubbers
128166

129167
You can also create custom scrubbers in your application if you want to.

lib/rails/html/sanitizer.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ class << self
110110
acronym a img blockquote del ins))
111111
self.allowed_attributes = Set.new(%w(href src width height alt cite datetime title class name xml:lang abbr))
112112

113-
def initialize
114-
@permit_scrubber = PermitScrubber.new
113+
def initialize(prune: false)
114+
@permit_scrubber = PermitScrubber.new(prune: prune)
115115
end
116116

117117
def sanitize(html, options = {})

lib/rails/html/scrubbers.rb

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,11 @@ 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_reader :tags, :attributes
48+
attr_reader :tags, :attributes, :prune
4949

50-
def initialize
51-
@direction = :bottom_up
50+
def initialize(prune: false)
51+
@prune = prune
52+
@direction = @prune ? :top_down : :bottom_up
5253
@tags, @attributes = nil, nil
5354
end
5455

@@ -98,7 +99,7 @@ def keep_node?(node)
9899
end
99100

100101
def scrub_node(node)
101-
node.before(node.children) # strip
102+
node.before(node.children) unless prune # strip
102103
node.remove
103104
end
104105

test/sanitizer_test.rb

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

261+
def test_should_allow_prune
262+
sanitizer = Rails::Html::SafeListSanitizer.new(prune: true)
263+
text = '<u>leave me <b>now</b></u>'
264+
assert_equal "<u>leave me </u>", sanitizer.sanitize(text, tags: %w(u))
265+
end
266+
261267
def test_should_allow_custom_tags
262268
text = "<u>foo</u>"
263269
assert_equal text, safe_list_sanitize(text, tags: %w(u))

test/scrubbers_test.rb

Lines changed: 14 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+
@scrubber = Rails::Html::PermitScrubber.new(prune: true)
71+
@scrubber.tags = %w(tag)
72+
html = '<tag>leave me <span>now</span></tag>'
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>',
@@ -157,6 +164,13 @@ def test_targeting_tags_and_attributes_removes_only_them
157164
html = '<tag remove="" other=""></tag><a remove="" other=""></a>'
158165
assert_scrubbed html, '<a other=""></a>'
159166
end
167+
168+
def test_prunes_tags
169+
@scrubber = Rails::Html::TargetScrubber.new(prune: true)
170+
@scrubber.tags = %w(span)
171+
html = '<tag>leave me <span>now</span></tag>'
172+
assert_scrubbed html, '<tag>leave me </tag>'
173+
end
160174
end
161175

162176
class TextOnlyScrubberTest < ScrubberTest

0 commit comments

Comments
 (0)