rails-html-sanitizer 1.4.3 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.