rails-html-sanitizer 1.4.3 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f00d9f256478eb753c8d211c3b25efa4204bbdbc9c5abf0415413c811a2e404
4
- data.tar.gz: 65d3871aa798dfbbfb1138b666d475b590e347cdb66614d6d39b72ad3531c742
3
+ metadata.gz: 59897b4a0d7f69a21932ec1cb44e24ea0ba4c2cf79ef7101ef32e90f40ad766b
4
+ data.tar.gz: '063919ea5426a6938040672fefeabcaf82087a5ccd12ffc7457e34eb6984042b'
5
5
  SHA512:
6
- metadata.gz: e6e31eaa72b1a2e8356aae50600ac784f85a80828cbc49ce8061384ecd3f21a1d8eaee69845dc08537c5102728c3cc41a72cb3ed8b9789c4921038398afa61e2
7
- data.tar.gz: 6b14a49842eaf4c3e0fbae5acd28fdf32a5deb6cd42f769aada848226847180c4d3a67a9dcbc439e1a4855699b0ea694cb4c7b6ee173391ac841bd334ae44b6f
6
+ metadata.gz: 2b0c23a07bc8acb3c1a039266cf053ad9044670a96620365b3ed722eb9a602def1bebe8de40697a2b12deba61cf224461ae8a4dc93749fa8c9675cda4cd216dd
7
+ data.tar.gz: 5dce2af04dd887e08773a975cc67d93987b15330d023cc68a6cc51322ed73b60681309b25feab1a2c54b0e062696af1dd78c83cdb609e8d179b6ef95419573b3
data/CHANGELOG.md CHANGED
@@ -1,3 +1,47 @@
1
+ ## 1.5.0 / 2023-01-20
2
+
3
+ * `SafeListSanitizer`, `PermitScrubber`, and `TargetScrubber` now all support pruning of unsafe tags.
4
+
5
+ By default, unsafe tags are still stripped, but this behavior can be changed to prune the element
6
+ and its children from the document by passing `prune: true` to any of these classes' constructors.
7
+
8
+ *seyerian*
9
+
10
+ ## 1.4.4 / 2022-12-13
11
+
12
+ * Address inefficient regular expression complexity with certain configurations of Rails::Html::Sanitizer.
13
+
14
+ Fixes CVE-2022-23517. See
15
+ [GHSA-5x79-w82f-gw8w](https://github.com/rails/rails-html-sanitizer/security/advisories/GHSA-5x79-w82f-gw8w)
16
+ for more information.
17
+
18
+ *Mike Dalessio*
19
+
20
+ * Address improper sanitization of data URIs.
21
+
22
+ Fixes CVE-2022-23518 and #135. See
23
+ [GHSA-mcvf-2q2m-x72m](https://github.com/rails/rails-html-sanitizer/security/advisories/GHSA-mcvf-2q2m-x72m)
24
+ for more information.
25
+
26
+ *Mike Dalessio*
27
+
28
+ * Address possible XSS vulnerability with certain configurations of Rails::Html::Sanitizer.
29
+
30
+ Fixes CVE-2022-23520. See
31
+ [GHSA-rrfc-7g8p-99q8](https://github.com/rails/rails-html-sanitizer/security/advisories/GHSA-rrfc-7g8p-99q8)
32
+ for more information.
33
+
34
+ *Mike Dalessio*
35
+
36
+ * Address possible XSS vulnerability with certain configurations of Rails::Html::Sanitizer.
37
+
38
+ Fixes CVE-2022-23519. See
39
+ [GHSA-9h9g-93gc-623h](https://github.com/rails/rails-html-sanitizer/security/advisories/GHSA-9h9g-93gc-623h)
40
+ for more information.
41
+
42
+ *Mike Dalessio*
43
+
44
+
1
45
  ## 1.4.3 / 2022-06-09
2
46
 
3
47
  * Address a possible XSS vulnerability with certain configurations of Rails::Html::Sanitizer.
@@ -8,7 +52,6 @@
8
52
 
9
53
  *Mike Dalessio*
10
54
 
11
-
12
55
  ## 1.4.2 / 2021-08-23
13
56
 
14
57
  * Slightly improve performance.
data/README.md CHANGED
@@ -21,6 +21,35 @@ Or install it yourself as:
21
21
 
22
22
  ## Usage
23
23
 
24
+ ### A note on HTML entities
25
+
26
+ __Rails::HTML sanitizers are intended to be used by the view layer, at page-render time. They are *not* intended to sanitize persisted strings that will sanitized *again* at page-render time.__
27
+
28
+ Proper HTML sanitization will replace some characters with HTML entities. For example, `<` will be replaced with `&lt;` to ensure that the markup is well-formed.
29
+
30
+ This is important to keep in mind because __HTML entities will render improperly if they are sanitized twice.__
31
+
32
+
33
+ #### A concrete example showing the problem that can arise
34
+
35
+ Imagine the user is asked to enter their employer's name, which will appear on their public profile page. Then imagine they enter `JPMorgan Chase & Co.`.
36
+
37
+ If you sanitize this before persisting it in the database, the stored string will be `JPMorgan Chase &amp; Co.`
38
+
39
+ When the page is rendered, if this string is sanitized a second time by the view layer, the HTML will contain `JPMorgan Chase &amp;amp; Co.` which will render as "JPMorgan Chase &amp;amp; Co.".
40
+
41
+ Another problem that can arise is rendering the sanitized string in a non-HTML context (for example, if it ends up being part of an SMS message). In this case, it may contain inappropriate HTML entities.
42
+
43
+
44
+ #### Suggested alternatives
45
+
46
+ You might simply choose to persist the untrusted string as-is (the raw input), and then ensure that the string will be properly sanitized by the view layer.
47
+
48
+ That raw string, if rendered in an non-HTML context (like SMS), must also be sanitized by a method appropriate for that context. You may wish to look into using [Loofah](https://github.com/flavorjones/loofah) or [Sanitize](https://github.com/rgrove/sanitize) to customize how this sanitization works, including omitting HTML entities in the final string.
49
+
50
+ If you really want to sanitize the string that's stored in your database, you may wish to look into [Loofah::ActiveRecord](https://github.com/flavorjones/loofah-activerecord) rather than use the Rails::HTML sanitizers.
51
+
52
+
24
53
  ### Sanitizers
25
54
 
26
55
  All sanitizers respond to `sanitize`.
@@ -57,6 +86,9 @@ safe_list_sanitizer.sanitize(@article.body, scrubber: ArticleScrubber.new)
57
86
 
58
87
  # safe list sanitizer can also sanitize css
59
88
  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)
60
92
  ```
61
93
 
62
94
  ### Scrubbers
@@ -78,6 +110,24 @@ html_fragment.scrub!(scrubber)
78
110
  html_fragment.to_s # => "<a></a>"
79
111
  ```
80
112
 
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
+
81
131
  #### `Rails::Html::TargetScrubber`
82
132
 
83
133
  Where `PermitScrubber` picks out tags and attributes to permit in sanitization,
@@ -95,6 +145,23 @@ html_fragment.scrub!(scrubber)
95
145
  html_fragment.to_s # => "<a></a>"
96
146
  ```
97
147
 
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
+ ```
98
165
  #### Custom Scrubbers
99
166
 
100
167
  You can also create custom scrubbers in your application if you want to.
@@ -138,5 +205,11 @@ Rails Html Sanitizers is work of many contributors. You're encouraged to submit
138
205
 
139
206
  See [CONTRIBUTING](CONTRIBUTING.md).
140
207
 
208
+ ### Security reports
209
+
210
+ Trying to report a possible security vulnerability in this project? Please
211
+ check out our [security policy](https://rubyonrails.org/security) for
212
+ guidelines about how to proceed.
213
+
141
214
  ## License
142
215
  Rails Html Sanitizers is released under the [MIT License](MIT-LICENSE).
@@ -1,7 +1,7 @@
1
1
  module Rails
2
2
  module Html
3
3
  class Sanitizer
4
- VERSION = "1.4.3"
4
+ VERSION = "1.5.0"
5
5
  end
6
6
  end
7
7
  end
@@ -110,8 +110,8 @@ module Rails
110
110
  acronym a img blockquote del ins))
111
111
  self.allowed_attributes = Set.new(%w(href src width height alt cite datetime title class name xml:lang abbr))
112
112
 
113
- def initialize
114
- @permit_scrubber = PermitScrubber.new
113
+ def initialize(prune: false)
114
+ @permit_scrubber = PermitScrubber.new(prune: prune)
115
115
  end
116
116
 
117
117
  def sanitize(html, options = {})
@@ -141,25 +141,8 @@ module Rails
141
141
 
142
142
  private
143
143
 
144
- def loofah_using_html5?
145
- # future-proofing, see https://github.com/flavorjones/loofah/pull/239
146
- Loofah.respond_to?(:html5_mode?) && Loofah.html5_mode?
147
- end
148
-
149
- def remove_safelist_tag_combinations(tags)
150
- if !loofah_using_html5? && tags.include?("select") && tags.include?("style")
151
- warn("WARNING: #{self.class}: removing 'style' from safelist, should not be combined with 'select'")
152
- tags.delete("style")
153
- end
154
- tags
155
- end
156
-
157
144
  def allowed_tags(options)
158
- if options[:tags]
159
- remove_safelist_tag_combinations(options[:tags])
160
- else
161
- self.class.allowed_tags
162
- end
145
+ options[:tags] || self.class.allowed_tags
163
146
  end
164
147
 
165
148
  def allowed_attributes(options)
@@ -45,10 +45,11 @@ module Rails
45
45
  # See the documentation for +Nokogiri::XML::Node+ to understand what's possible
46
46
  # with nodes: https://nokogiri.org/rdoc/Nokogiri/XML/Node.html
47
47
  class PermitScrubber < Loofah::Scrubber
48
- attr_reader :tags, :attributes
48
+ attr_reader :tags, :attributes, :prune
49
49
 
50
- def initialize
51
- @direction = :bottom_up
50
+ def initialize(prune: false)
51
+ @prune = prune
52
+ @direction = @prune ? :top_down : :bottom_up
52
53
  @tags, @attributes = nil, nil
53
54
  end
54
55
 
@@ -61,9 +62,9 @@ module Rails
61
62
  end
62
63
 
63
64
  def scrub(node)
64
- if node.cdata?
65
- text = node.document.create_text_node node.text
66
- node.replace text
65
+ if Loofah::HTML5::Scrub.cdata_needs_escaping?(node)
66
+ replacement = Loofah::HTML5::Scrub.cdata_escape(node)
67
+ node.replace(replacement)
67
68
  return CONTINUE
68
69
  end
69
70
  return CONTINUE if skip_node?(node)
@@ -98,7 +99,7 @@ module Rails
98
99
  end
99
100
 
100
101
  def scrub_node(node)
101
- node.before(node.children) # strip
102
+ node.before(node.children) unless prune # strip
102
103
  node.remove
103
104
  end
104
105
 
@@ -139,15 +140,13 @@ module Rails
139
140
  end
140
141
 
141
142
  if Loofah::HTML5::SafeList::ATTR_VAL_IS_URI.include?(attr_name)
142
- # this block lifted nearly verbatim from HTML5 sanitization
143
- val_unescaped = CGI.unescapeHTML(attr_node.value).gsub(Loofah::HTML5::Scrub::CONTROL_CHARACTERS,'').downcase
144
- if val_unescaped =~ /^[a-z0-9][-+.a-z0-9]*:/ && ! Loofah::HTML5::SafeList::ALLOWED_PROTOCOLS.include?(val_unescaped.split(Loofah::HTML5::SafeList::PROTOCOL_SEPARATOR)[0])
145
- attr_node.remove
146
- end
143
+ return if Loofah::HTML5::Scrub.scrub_uri_attribute(attr_node)
147
144
  end
145
+
148
146
  if Loofah::HTML5::SafeList::SVG_ATTR_VAL_ALLOWS_REF.include?(attr_name)
149
- attr_node.value = attr_node.value.gsub(/url\s*\(\s*[^#\s][^)]+?\)/m, ' ') if attr_node.value
147
+ Loofah::HTML5::Scrub.scrub_attribute_that_allows_local_ref(attr_node)
150
148
  end
149
+
151
150
  if Loofah::HTML5::SafeList::SVG_ALLOW_LOCAL_HREF.include?(node.name) && attr_name == 'xlink:href' && attr_node.value =~ /^\s*[^#\s].*/m
152
151
  attr_node.remove
153
152
  end
@@ -54,7 +54,7 @@ class SanitizersTest < Minitest::Test
54
54
 
55
55
  def test_strip_tags_with_quote
56
56
  input = '<" <img src="trollface.gif" onload="alert(1)"> hi'
57
- expected = libxml_2_9_14_recovery? ? %{&lt;" hi} : %{ hi}
57
+ expected = libxml_2_9_14_recovery_lt? ? %{&lt;" hi} : %{ hi}
58
58
  assert_equal(expected, full_sanitize(input))
59
59
  end
60
60
 
@@ -77,19 +77,19 @@ class SanitizersTest < Minitest::Test
77
77
 
78
78
  def test_remove_unclosed_tags
79
79
  input = "This is <-- not\n a comment here."
80
- expected = libxml_2_9_14_recovery? ? %{This is &lt;-- not\n a comment here.} : %{This is }
80
+ expected = libxml_2_9_14_recovery_lt? ? %{This is &lt;-- not\n a comment here.} : %{This is }
81
81
  assert_equal(expected, full_sanitize(input))
82
82
  end
83
83
 
84
84
  def test_strip_cdata
85
85
  input = "This has a <![CDATA[<section>]]> here."
86
- expected = libxml_2_9_14_recovery? ? %{This has a &lt;![CDATA[]]&gt; here.} : %{This has a ]]&gt; here.}
86
+ expected = libxml_2_9_14_recovery_lt_bang? ? %{This has a &lt;![CDATA[]]&gt; here.} : %{This has a ]]&gt; here.}
87
87
  assert_equal(expected, full_sanitize(input))
88
88
  end
89
89
 
90
90
  def test_strip_unclosed_cdata
91
91
  input = "This has an unclosed <![CDATA[<section>]] here..."
92
- expected = libxml_2_9_14_recovery? ? %{This has an unclosed &lt;![CDATA[]] here...} : %{This has an unclosed ]] here...}
92
+ expected = libxml_2_9_14_recovery_lt_bang? ? %{This has an unclosed &lt;![CDATA[]] here...} : %{This has an unclosed ]] here...}
93
93
  assert_equal(expected, full_sanitize(input))
94
94
  end
95
95
 
@@ -256,6 +256,12 @@ class SanitizersTest < Minitest::Test
256
256
  end
257
257
  end
258
258
 
259
+ def test_should_allow_prune
260
+ sanitizer = Rails::Html::SafeListSanitizer.new(prune: true)
261
+ text = '<u>leave me <b>now</b></u>'
262
+ assert_equal "<u>leave me </u>", sanitizer.sanitize(text, tags: %w(u))
263
+ end
264
+
259
265
  def test_should_allow_custom_tags
260
266
  text = "<u>foo</u>"
261
267
  assert_equal text, safe_list_sanitize(text, tags: %w(u))
@@ -458,13 +464,13 @@ class SanitizersTest < Minitest::Test
458
464
 
459
465
  def test_should_sanitize_cdata_section
460
466
  input = "<![CDATA[<span>section</span>]]>"
461
- expected = libxml_2_9_14_recovery? ? %{&lt;![CDATA[<span>section</span>]]&gt;} : %{section]]&gt;}
467
+ expected = libxml_2_9_14_recovery_lt_bang? ? %{&lt;![CDATA[<span>section</span>]]&gt;} : %{section]]&gt;}
462
468
  assert_sanitized(input, expected)
463
469
  end
464
470
 
465
471
  def test_should_sanitize_unterminated_cdata_section
466
472
  input = "<![CDATA[<span>neverending..."
467
- expected = libxml_2_9_14_recovery? ? %{&lt;![CDATA[<span>neverending...</span>} : %{neverending...}
473
+ expected = libxml_2_9_14_recovery_lt_bang? ? %{&lt;![CDATA[<span>neverending...</span>} : %{neverending...}
468
474
  assert_sanitized(input, expected)
469
475
  end
470
476
 
@@ -581,23 +587,124 @@ class SanitizersTest < Minitest::Test
581
587
  assert_equal("<div>text</div><b>text</b>", safe_list_sanitize("<div>text</div><!-- comment --><b>text</b>"))
582
588
  end
583
589
 
584
- def test_disallow_the_dangerous_safelist_combination_of_select_and_style
585
- input = "<select><style><script>alert(1)</script></style></select>"
586
- tags = ["select", "style"]
587
- warning = /WARNING: Rails::Html::SafeListSanitizer: removing 'style' from safelist/
588
- sanitized = nil
589
- invocation = Proc.new { sanitized = safe_list_sanitize(input, tags: tags) }
590
+ %w[text/plain text/css image/png image/gif image/jpeg].each do |mediatype|
591
+ define_method "test_mediatype_#{mediatype}_allowed" do
592
+ input = %Q(<img src="data:#{mediatype};base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">)
593
+ expected = input
594
+ actual = safe_list_sanitize(input)
595
+ assert_equal(expected, actual)
590
596
 
591
- if html5_mode?
592
- # if Loofah is using an HTML5 parser,
593
- # then "style" should be removed by the parser as an invalid child of "select"
594
- assert_silent(&invocation)
595
- else
596
- # if Loofah is using an HTML4 parser,
597
- # then SafeListSanitizer should remove "style" from the safelist
598
- assert_output(nil, warning, &invocation)
597
+ input = %Q(<img src="DATA:#{mediatype};base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">)
598
+ expected = input
599
+ actual = safe_list_sanitize(input)
600
+ assert_equal(expected, actual)
599
601
  end
600
- refute_includes(sanitized, "style")
602
+ end
603
+
604
+ def test_mediatype_text_html_disallowed
605
+ input = %q(<img src="data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">)
606
+ expected = %q(<img>)
607
+ actual = safe_list_sanitize(input)
608
+ assert_equal(expected, actual)
609
+
610
+ input = %q(<img src="DATA:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">)
611
+ expected = %q(<img>)
612
+ actual = safe_list_sanitize(input)
613
+ assert_equal(expected, actual)
614
+ end
615
+
616
+ def test_mediatype_image_svg_xml_disallowed
617
+ input = %q(<img src="data:image/svg+xml;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">)
618
+ expected = %q(<img>)
619
+ actual = safe_list_sanitize(input)
620
+ assert_equal(expected, actual)
621
+
622
+ input = %q(<img src="DATA:image/svg+xml;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">)
623
+ expected = %q(<img>)
624
+ actual = safe_list_sanitize(input)
625
+ assert_equal(expected, actual)
626
+ end
627
+
628
+ def test_mediatype_other_disallowed
629
+ input = %q(<a href="data:foo;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">foo</a>)
630
+ expected = %q(<a>foo</a>)
631
+ actual = safe_list_sanitize(input)
632
+ assert_equal(expected, actual)
633
+
634
+ input = %q(<a href="DATA:foo;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">foo</a>)
635
+ expected = %q(<a>foo</a>)
636
+ actual = safe_list_sanitize(input)
637
+ assert_equal(expected, actual)
638
+ end
639
+
640
+ def test_scrubbing_svg_attr_values_that_allow_ref
641
+ input = %Q(<div fill="yellow url(http://bad.com/) #fff">hey</div>)
642
+ expected = %Q(<div fill="yellow #fff">hey</div>)
643
+ actual = scope_allowed_attributes %w(fill) do
644
+ safe_list_sanitize(input)
645
+ end
646
+
647
+ assert_equal(expected, actual)
648
+ end
649
+
650
+ def test_style_with_css_payload
651
+ input, tags = "<style>div > span { background: \"red\"; }</style>", ["style"]
652
+ expected = "<style>div &gt; span { background: \"red\"; }</style>"
653
+ actual = safe_list_sanitize(input, tags: tags)
654
+
655
+ assert_equal(expected, actual)
656
+ end
657
+
658
+ def test_combination_of_select_and_style_with_css_payload
659
+ input, tags = "<select><style>div > span { background: \"red\"; }</style></select>", ["select", "style"]
660
+ expected = "<select><style>div &gt; span { background: \"red\"; }</style></select>"
661
+ actual = safe_list_sanitize(input, tags: tags)
662
+
663
+ assert_equal(expected, actual)
664
+ end
665
+
666
+ def test_combination_of_select_and_style_with_script_payload
667
+ input, tags = "<select><style><script>alert(1)</script></style></select>", ["select", "style"]
668
+ expected = "<select><style>&lt;script&gt;alert(1)&lt;/script&gt;</style></select>"
669
+ actual = safe_list_sanitize(input, tags: tags)
670
+
671
+ assert_equal(expected, actual)
672
+ end
673
+
674
+ def test_combination_of_svg_and_style_with_script_payload
675
+ input, tags = "<svg><style><script>alert(1)</script></style></svg>", ["svg", "style"]
676
+ expected = "<svg><style>&lt;script&gt;alert(1)&lt;/script&gt;</style></svg>"
677
+ actual = safe_list_sanitize(input, tags: tags)
678
+
679
+ assert_equal(expected, actual)
680
+ end
681
+
682
+ def test_combination_of_math_and_style_with_img_payload
683
+ input, tags = "<math><style><img src=x onerror=alert(1)></style></math>", ["math", "style"]
684
+ expected = "<math><style>&lt;img src=x onerror=alert(1)&gt;</style></math>"
685
+ actual = safe_list_sanitize(input, tags: tags)
686
+
687
+ assert_equal(expected, actual)
688
+
689
+ input, tags = "<math><style><img src=x onerror=alert(1)></style></math>", ["math", "style", "img"]
690
+ expected = "<math><style>&lt;img src=x onerror=alert(1)&gt;</style></math>"
691
+ actual = safe_list_sanitize(input, tags: tags)
692
+
693
+ assert_equal(expected, actual)
694
+ end
695
+
696
+ def test_combination_of_svg_and_style_with_img_payload
697
+ input, tags = "<svg><style><img src=x onerror=alert(1)></style></svg>", ["svg", "style"]
698
+ expected = "<svg><style>&lt;img src=x onerror=alert(1)&gt;</style></svg>"
699
+ actual = safe_list_sanitize(input, tags: tags)
700
+
701
+ assert_equal(expected, actual)
702
+
703
+ input, tags = "<svg><style><img src=x onerror=alert(1)></style></svg>", ["svg", "style", "img"]
704
+ expected = "<svg><style>&lt;img src=x onerror=alert(1)&gt;</style></svg>"
705
+ actual = safe_list_sanitize(input, tags: tags)
706
+
707
+ assert_equal(expected, actual)
601
708
  end
602
709
 
603
710
  protected
@@ -657,11 +764,14 @@ protected
657
764
  end.join
658
765
  end
659
766
 
660
- def libxml_2_9_14_recovery?
767
+ def libxml_2_9_14_recovery_lt?
768
+ # changed in 2.9.14, see https://github.com/sparklemotion/nokogiri/releases/tag/v1.13.5
661
769
  Nokogiri.method(:uses_libxml?).arity == -1 && Nokogiri.uses_libxml?(">= 2.9.14")
662
770
  end
663
771
 
664
- def html5_mode?
665
- ::Loofah.respond_to?(:html5_mode?) && ::Loofah.html5_mode?
772
+ def libxml_2_9_14_recovery_lt_bang?
773
+ # changed in 2.9.14, see https://github.com/sparklemotion/nokogiri/releases/tag/v1.13.5
774
+ # then reverted in 2.10.0, see https://gitlab.gnome.org/GNOME/libxml2/-/issues/380
775
+ Nokogiri.method(:uses_libxml?).arity == -1 && Nokogiri.uses_libxml?("= 2.9.14")
666
776
  end
667
777
  end
@@ -66,6 +66,13 @@ class PermitScrubberTest < ScrubberTest
66
66
  assert_scrubbed html, '<tag>leave me now</tag>'
67
67
  end
68
68
 
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
+
69
76
  def test_leaves_comments_when_supplied_as_tag
70
77
  @scrubber.tags = %w(div comment)
71
78
  assert_scrubbed('<div>one</div><!-- two --><span>three</span>',
@@ -157,6 +164,13 @@ class TargetScrubberTest < ScrubberTest
157
164
  html = '<tag remove="" other=""></tag><a remove="" other=""></a>'
158
165
  assert_scrubbed html, '<a other=""></a>'
159
166
  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
160
174
  end
161
175
 
162
176
  class TextOnlyScrubberTest < ScrubberTest
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-html-sanitizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.3
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rafael Mendonça França
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-06-09 00:00:00.000000000 Z
12
+ date: 2023-01-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: loofah
@@ -17,14 +17,20 @@ dependencies:
17
17
  requirements:
18
18
  - - "~>"
19
19
  - !ruby/object:Gem::Version
20
- version: '2.3'
20
+ version: '2.19'
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.19.1
21
24
  type: :runtime
22
25
  prerelease: false
23
26
  version_requirements: !ruby/object:Gem::Requirement
24
27
  requirements:
25
28
  - - "~>"
26
29
  - !ruby/object:Gem::Version
27
- version: '2.3'
30
+ version: '2.19'
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.19.1
28
34
  - !ruby/object:Gem::Dependency
29
35
  name: bundler
30
36
  requirement: !ruby/object:Gem::Requirement
@@ -103,9 +109,9 @@ licenses:
103
109
  - MIT
104
110
  metadata:
105
111
  bug_tracker_uri: https://github.com/rails/rails-html-sanitizer/issues
106
- changelog_uri: https://github.com/rails/rails-html-sanitizer/blob/v1.4.3/CHANGELOG.md
107
- documentation_uri: https://www.rubydoc.info/gems/rails-html-sanitizer/1.4.3
108
- source_code_uri: https://github.com/rails/rails-html-sanitizer/tree/v1.4.3
112
+ changelog_uri: https://github.com/rails/rails-html-sanitizer/blob/v1.5.0/CHANGELOG.md
113
+ documentation_uri: https://www.rubydoc.info/gems/rails-html-sanitizer/1.5.0
114
+ source_code_uri: https://github.com/rails/rails-html-sanitizer/tree/v1.5.0
109
115
  post_install_message:
110
116
  rdoc_options: []
111
117
  require_paths:
@@ -121,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
127
  - !ruby/object:Gem::Version
122
128
  version: '0'
123
129
  requirements: []
124
- rubygems_version: 3.3.5
130
+ rubygems_version: 3.4.2
125
131
  signing_key:
126
132
  specification_version: 4
127
133
  summary: This gem is responsible to sanitize HTML fragments in Rails applications.