rails-html-sanitizer 1.3.0 → 1.4.4

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: 8eba1aac52c80be280f186c5d378150709b7d4cd2a5d5b2367e6d2c036648d52
4
- data.tar.gz: 96408eae2efee778a704f7caf246b64868a63bfdbbb81905b294bcca731a9289
3
+ metadata.gz: a74021096590326ee357971bec71d2c4507a95cdaf05c8e21d383ce18fee18d3
4
+ data.tar.gz: faad0d5f268dad601b633b03912e353fcc2d760fceb253d9cde2064b010b997a
5
5
  SHA512:
6
- metadata.gz: c4209cebc841299143a466143f4b776461fc1cc8bba112dc603e86835b68ee44a800566f64224b27f5a45d164d0b004049b228dc405c3de59068800ec7a5d564
7
- data.tar.gz: c899472b8dffe9f9fd4d15ae4739f07a775d74b9ed14143beb688bb546b6a82ec469add036747b81aff33510e6e241379e21458cb39d9b2a8e797824066e24e5
6
+ metadata.gz: e7f01438708076a283326c78b052ba954a42de4134d8d1d7e7c336c82ecd04c661f75dad3a0f9b1ffebe278f76ef229c98a3f2568801f82d94c94a50f399a2ef
7
+ data.tar.gz: 4f44c0e92eb9e565611772ba28d426025621c0517c4217004c3409192991a17498dd38165a6c55561a5347d2fcdf34f51b24101ad6de525604e35785e89efbc0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,81 @@
1
+ ## 1.4.4 / 2022-12-13
2
+
3
+ * Address inefficient regular expression complexity with certain configurations of Rails::Html::Sanitizer.
4
+
5
+ Fixes CVE-2022-23517. See
6
+ [GHSA-5x79-w82f-gw8w](https://github.com/rails/rails-html-sanitizer/security/advisories/GHSA-5x79-w82f-gw8w)
7
+ for more information.
8
+
9
+ *Mike Dalessio*
10
+
11
+ * Address improper sanitization of data URIs.
12
+
13
+ Fixes CVE-2022-23518 and #135. See
14
+ [GHSA-mcvf-2q2m-x72m](https://github.com/rails/rails-html-sanitizer/security/advisories/GHSA-mcvf-2q2m-x72m)
15
+ for more information.
16
+
17
+ *Mike Dalessio*
18
+
19
+ * Address possible XSS vulnerability with certain configurations of Rails::Html::Sanitizer.
20
+
21
+ Fixes CVE-2022-23520. See
22
+ [GHSA-rrfc-7g8p-99q8](https://github.com/rails/rails-html-sanitizer/security/advisories/GHSA-rrfc-7g8p-99q8)
23
+ for more information.
24
+
25
+ *Mike Dalessio*
26
+
27
+ * Address possible XSS vulnerability with certain configurations of Rails::Html::Sanitizer.
28
+
29
+ Fixes CVE-2022-23519. See
30
+ [GHSA-9h9g-93gc-623h](https://github.com/rails/rails-html-sanitizer/security/advisories/GHSA-9h9g-93gc-623h)
31
+ for more information.
32
+
33
+ *Mike Dalessio*
34
+
35
+
36
+ ## 1.4.3 / 2022-06-09
37
+
38
+ * Address a possible XSS vulnerability with certain configurations of Rails::Html::Sanitizer.
39
+
40
+ Prevent the combination of `select` and `style` as allowed tags in SafeListSanitizer.
41
+
42
+ Fixes CVE-2022-32209
43
+
44
+ *Mike Dalessio*
45
+
46
+
47
+ ## 1.4.2 / 2021-08-23
48
+
49
+ * Slightly improve performance.
50
+
51
+ Assuming elements are more common than comments, make one less method call per node.
52
+
53
+ *Mike Dalessio*
54
+
55
+ ## 1.4.1 / 2021-08-18
56
+
57
+ * Fix regression in v1.4.0 that did not pass comment nodes to the scrubber.
58
+
59
+ Some scrubbers will want to override the default behavior and allow comments, but v1.4.0 only
60
+ passed through elements to the scrubber's `keep_node?` method.
61
+
62
+ This change once again allows the scrubber to make the decision on comment nodes, but still skips
63
+ other non-elements like processing instructions (see #115).
64
+
65
+ *Mike Dalessio*
66
+
67
+ ## 1.4.0 / 2021-08-18
68
+
69
+ * Processing Instructions are no longer allowed by Rails::Html::PermitScrubber
70
+
71
+ Previously, a PI with a name (or "target") matching an allowed tag name was not scrubbed. There
72
+ are no known security issues associated with these PIs, but similar to comments it's preferred to
73
+ omit these nodes when possible from sanitized output.
74
+
75
+ Fixes #115.
76
+
77
+ *Mike Dalessio*
78
+
1
79
  ## 1.3.0
2
80
 
3
81
  * Address deprecations in Loofah 2.3.0.
data/README.md CHANGED
@@ -81,8 +81,10 @@ html_fragment.to_s # => "<a></a>"
81
81
  #### `Rails::Html::TargetScrubber`
82
82
 
83
83
  Where `PermitScrubber` picks out tags and attributes to permit in sanitization,
84
- `Rails::Html::TargetScrubber` targets them for removal.
84
+ `Rails::Html::TargetScrubber` targets them for removal. See https://github.com/flavorjones/loofah/blob/main/lib/loofah/html5/safelist.rb for the tag list.
85
85
 
86
+ **Note:** by default, it will scrub anything that is not part of the permitted tags from
87
+ loofah `HTML5::Scrub.allowed_element?`.
86
88
 
87
89
  ```ruby
88
90
  scrubber = Rails::Html::TargetScrubber.new
@@ -1,7 +1,7 @@
1
1
  module Rails
2
2
  module Html
3
3
  class Sanitizer
4
- VERSION = "1.3.0"
4
+ VERSION = "1.4.4"
5
5
  end
6
6
  end
7
7
  end
@@ -61,14 +61,14 @@ module Rails
61
61
  end
62
62
 
63
63
  def scrub(node)
64
- if node.cdata?
65
- text = node.document.create_text_node node.text
66
- node.replace text
64
+ if Loofah::HTML5::Scrub.cdata_needs_escaping?(node)
65
+ replacement = Loofah::HTML5::Scrub.cdata_escape(node)
66
+ node.replace(replacement)
67
67
  return CONTINUE
68
68
  end
69
69
  return CONTINUE if skip_node?(node)
70
70
 
71
- unless keep_node?(node)
71
+ unless (node.element? || node.comment?) && keep_node?(node)
72
72
  return STOP if scrub_node(node) == STOP
73
73
  end
74
74
 
@@ -139,15 +139,13 @@ module Rails
139
139
  end
140
140
 
141
141
  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
142
+ return if Loofah::HTML5::Scrub.scrub_uri_attribute(attr_node)
147
143
  end
144
+
148
145
  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
146
+ Loofah::HTML5::Scrub.scrub_attribute_that_allows_local_ref(attr_node)
150
147
  end
148
+
151
149
  if Loofah::HTML5::SafeList::SVG_ALLOW_LOCAL_HREF.include?(node.name) && attr_name == 'xlink:href' && attr_node.value =~ /^\s*[^#\s].*/m
152
150
  attr_node.remove
153
151
  end
@@ -2,6 +2,8 @@ require "minitest/autorun"
2
2
  require "rails-html-sanitizer"
3
3
  require "rails/dom/testing/assertions/dom_assertions"
4
4
 
5
+ puts Nokogiri::VERSION_INFO
6
+
5
7
  class SanitizersTest < Minitest::Test
6
8
  include Rails::Dom::Testing::Assertions::DomAssertions
7
9
 
@@ -12,13 +14,11 @@ class SanitizersTest < Minitest::Test
12
14
  end
13
15
 
14
16
  def test_sanitize_nested_script
15
- sanitizer = Rails::Html::SafeListSanitizer.new
16
- assert_equal '&lt;script&gt;alert("XSS");&lt;/script&gt;', sanitizer.sanitize('<script><script></script>alert("XSS");<script><</script>/</script><script>script></script>', tags: %w(em))
17
+ assert_equal '&lt;script&gt;alert("XSS");&lt;/script&gt;', safe_list_sanitize('<script><script></script>alert("XSS");<script><</script>/</script><script>script></script>', tags: %w(em))
17
18
  end
18
19
 
19
20
  def test_sanitize_nested_script_in_style
20
- sanitizer = Rails::Html::SafeListSanitizer.new
21
- assert_equal '&lt;script&gt;alert("XSS");&lt;/script&gt;', sanitizer.sanitize('<style><script></style>alert("XSS");<style><</style>/</style><style>script></style>', tags: %w(em))
21
+ assert_equal '&lt;script&gt;alert("XSS");&lt;/script&gt;', safe_list_sanitize('<style><script></style>alert("XSS");<style><</style>/</style><style>script></style>', tags: %w(em))
22
22
  end
23
23
 
24
24
  class XpathRemovalTestSanitizer < Rails::Html::Sanitizer
@@ -54,7 +54,8 @@ 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
- assert_equal ' hi', full_sanitize(input)
57
+ expected = libxml_2_9_14_recovery_lt? ? %{&lt;" hi} : %{ hi}
58
+ assert_equal(expected, full_sanitize(input))
58
59
  end
59
60
 
60
61
  def test_strip_invalid_html
@@ -75,15 +76,21 @@ class SanitizersTest < Minitest::Test
75
76
  end
76
77
 
77
78
  def test_remove_unclosed_tags
78
- assert_equal "This is ", full_sanitize("This is <-- not\n a comment here.")
79
+ input = "This is <-- not\n a comment here."
80
+ expected = libxml_2_9_14_recovery_lt? ? %{This is &lt;-- not\n a comment here.} : %{This is }
81
+ assert_equal(expected, full_sanitize(input))
79
82
  end
80
83
 
81
84
  def test_strip_cdata
82
- assert_equal "This has a ]]&gt; here.", full_sanitize("This has a <![CDATA[<section>]]> here.")
85
+ input = "This has a <![CDATA[<section>]]> here."
86
+ expected = libxml_2_9_14_recovery_lt_bang? ? %{This has a &lt;![CDATA[]]&gt; here.} : %{This has a ]]&gt; here.}
87
+ assert_equal(expected, full_sanitize(input))
83
88
  end
84
89
 
85
90
  def test_strip_unclosed_cdata
86
- assert_equal "This has an unclosed ]] here...", full_sanitize("This has an unclosed <![CDATA[<section>]] here...")
91
+ input = "This has an unclosed <![CDATA[<section>]] here..."
92
+ expected = libxml_2_9_14_recovery_lt_bang? ? %{This has an unclosed &lt;![CDATA[]] here...} : %{This has an unclosed ]] here...}
93
+ assert_equal(expected, full_sanitize(input))
87
94
  end
88
95
 
89
96
  def test_strip_blank_string
@@ -93,7 +100,7 @@ class SanitizersTest < Minitest::Test
93
100
  end
94
101
 
95
102
  def test_strip_tags_with_plaintext
96
- assert_equal "Dont touch me", full_sanitize("Dont touch me")
103
+ assert_equal "Don't touch me", full_sanitize("Don't touch me")
97
104
  end
98
105
 
99
106
  def test_strip_tags_with_tags
@@ -135,7 +142,7 @@ class SanitizersTest < Minitest::Test
135
142
  end
136
143
 
137
144
  def test_strip_links_with_plaintext
138
- assert_equal "Dont touch me", link_sanitize("Dont touch me")
145
+ assert_equal "Don't touch me", link_sanitize("Don't touch me")
139
146
  end
140
147
 
141
148
  def test_strip_links_with_line_feed_and_uppercase_tag
@@ -271,7 +278,8 @@ class SanitizersTest < Minitest::Test
271
278
 
272
279
  def test_scrub_style_if_style_attribute_option_is_passed
273
280
  input = '<p style="color: #000; background-image: url(http://www.ragingplatypus.com/i/cam-full.jpg);"></p>'
274
- assert_equal '<p style="color: #000;"></p>', safe_list_sanitize(input, attributes: %w(style))
281
+ actual = safe_list_sanitize(input, attributes: %w(style))
282
+ assert_includes(['<p style="color: #000;"></p>', '<p style="color:#000;"></p>'], actual)
275
283
  end
276
284
 
277
285
  def test_should_raise_argument_error_if_tags_is_not_enumerable
@@ -413,8 +421,25 @@ class SanitizersTest < Minitest::Test
413
421
  end
414
422
 
415
423
  def test_should_sanitize_div_background_image_unicode_encoded
416
- raw = %(background-image:\0075\0072\006C\0028'\006a\0061\0076\0061\0073\0063\0072\0069\0070\0074\003a\0061\006c\0065\0072\0074\0028.1027\0058.1053\0053\0027\0029'\0029)
417
- assert_equal '', sanitize_css(raw)
424
+ [
425
+ convert_to_css_hex("url(javascript:alert(1))", false),
426
+ convert_to_css_hex("url(javascript:alert(1))", true),
427
+ convert_to_css_hex("url(https://example.com)", false),
428
+ convert_to_css_hex("url(https://example.com)", true),
429
+ ].each do |propval|
430
+ raw = "background-image:" + propval
431
+ assert_empty(sanitize_css(raw))
432
+ end
433
+ end
434
+
435
+ def test_should_allow_div_background_image_unicode_encoded_safe_functions
436
+ [
437
+ convert_to_css_hex("rgb(255,0,0)", false),
438
+ convert_to_css_hex("rgb(255,0,0)", true),
439
+ ].each do |propval|
440
+ raw = "background-image:" + propval
441
+ assert_includes(sanitize_css(raw), "background-image")
442
+ end
418
443
  end
419
444
 
420
445
  def test_should_sanitize_div_style_expression
@@ -432,11 +457,15 @@ class SanitizersTest < Minitest::Test
432
457
  end
433
458
 
434
459
  def test_should_sanitize_cdata_section
435
- assert_sanitized "<![CDATA[<span>section</span>]]>", "section]]&gt;"
460
+ input = "<![CDATA[<span>section</span>]]>"
461
+ expected = libxml_2_9_14_recovery_lt_bang? ? %{&lt;![CDATA[<span>section</span>]]&gt;} : %{section]]&gt;}
462
+ assert_sanitized(input, expected)
436
463
  end
437
464
 
438
465
  def test_should_sanitize_unterminated_cdata_section
439
- assert_sanitized "<![CDATA[<span>neverending...", "neverending..."
466
+ input = "<![CDATA[<span>neverending..."
467
+ expected = libxml_2_9_14_recovery_lt_bang? ? %{&lt;![CDATA[<span>neverending...</span>} : %{neverending...}
468
+ assert_sanitized(input, expected)
440
469
  end
441
470
 
442
471
  def test_should_not_mangle_urls_with_ampersand
@@ -487,7 +516,13 @@ class SanitizersTest < Minitest::Test
487
516
 
488
517
  text = safe_list_sanitize(html)
489
518
 
490
- assert_equal %{<a href=\"examp&lt;!--%22%20unsafeattr=foo()&gt;--&gt;le.com\">test</a>}, text
519
+ acceptable_results = [
520
+ # nokogiri w/vendored+patched libxml2
521
+ %{<a href="examp&lt;!--%22%20unsafeattr=foo()&gt;--&gt;le.com">test</a>},
522
+ # nokogiri w/ system libxml2
523
+ %{<a href="examp<!--%22%20unsafeattr=foo()>-->le.com">test</a>},
524
+ ]
525
+ assert_includes(acceptable_results, text)
491
526
  end
492
527
 
493
528
  def test_uri_escaping_of_src_attr_in_a_tag_in_safe_list_sanitizer
@@ -497,7 +532,13 @@ class SanitizersTest < Minitest::Test
497
532
 
498
533
  text = safe_list_sanitize(html)
499
534
 
500
- assert_equal %{<a src=\"examp&lt;!--%22%20unsafeattr=foo()&gt;--&gt;le.com\">test</a>}, text
535
+ acceptable_results = [
536
+ # nokogiri w/vendored+patched libxml2
537
+ %{<a src="examp&lt;!--%22%20unsafeattr=foo()&gt;--&gt;le.com">test</a>},
538
+ # nokogiri w/system libxml2
539
+ %{<a src="examp<!--%22%20unsafeattr=foo()>-->le.com">test</a>},
540
+ ]
541
+ assert_includes(acceptable_results, text)
501
542
  end
502
543
 
503
544
  def test_uri_escaping_of_name_attr_in_a_tag_in_safe_list_sanitizer
@@ -507,7 +548,13 @@ class SanitizersTest < Minitest::Test
507
548
 
508
549
  text = safe_list_sanitize(html)
509
550
 
510
- assert_equal %{<a name=\"examp&lt;!--%22%20unsafeattr=foo()&gt;--&gt;le.com\">test</a>}, text
551
+ acceptable_results = [
552
+ # nokogiri w/vendored+patched libxml2
553
+ %{<a name="examp&lt;!--%22%20unsafeattr=foo()&gt;--&gt;le.com">test</a>},
554
+ # nokogiri w/system libxml2
555
+ %{<a name="examp<!--%22%20unsafeattr=foo()>-->le.com">test</a>},
556
+ ]
557
+ assert_includes(acceptable_results, text)
511
558
  end
512
559
 
513
560
  def test_uri_escaping_of_name_action_in_a_tag_in_safe_list_sanitizer
@@ -517,7 +564,141 @@ class SanitizersTest < Minitest::Test
517
564
 
518
565
  text = safe_list_sanitize(html, attributes: ['action'])
519
566
 
520
- assert_equal %{<a action=\"examp&lt;!--%22%20unsafeattr=foo()&gt;--&gt;le.com\">test</a>}, text
567
+ acceptable_results = [
568
+ # nokogiri w/vendored+patched libxml2
569
+ %{<a action="examp&lt;!--%22%20unsafeattr=foo()&gt;--&gt;le.com">test</a>},
570
+ # nokogiri w/system libxml2
571
+ %{<a action="examp<!--%22%20unsafeattr=foo()>-->le.com">test</a>},
572
+ ]
573
+ assert_includes(acceptable_results, text)
574
+ end
575
+
576
+ def test_exclude_node_type_processing_instructions
577
+ assert_equal("<div>text</div><b>text</b>", safe_list_sanitize("<div>text</div><?div content><b>text</b>"))
578
+ end
579
+
580
+ def test_exclude_node_type_comment
581
+ assert_equal("<div>text</div><b>text</b>", safe_list_sanitize("<div>text</div><!-- comment --><b>text</b>"))
582
+ end
583
+
584
+ %w[text/plain text/css image/png image/gif image/jpeg].each do |mediatype|
585
+ define_method "test_mediatype_#{mediatype}_allowed" do
586
+ input = %Q(<img src="data:#{mediatype};base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">)
587
+ expected = input
588
+ actual = safe_list_sanitize(input)
589
+ assert_equal(expected, actual)
590
+
591
+ input = %Q(<img src="DATA:#{mediatype};base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">)
592
+ expected = input
593
+ actual = safe_list_sanitize(input)
594
+ assert_equal(expected, actual)
595
+ end
596
+ end
597
+
598
+ def test_mediatype_text_html_disallowed
599
+ input = %q(<img src="data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">)
600
+ expected = %q(<img>)
601
+ actual = safe_list_sanitize(input)
602
+ assert_equal(expected, actual)
603
+
604
+ input = %q(<img src="DATA:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">)
605
+ expected = %q(<img>)
606
+ actual = safe_list_sanitize(input)
607
+ assert_equal(expected, actual)
608
+ end
609
+
610
+ def test_mediatype_image_svg_xml_disallowed
611
+ input = %q(<img src="data:image/svg+xml;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">)
612
+ expected = %q(<img>)
613
+ actual = safe_list_sanitize(input)
614
+ assert_equal(expected, actual)
615
+
616
+ input = %q(<img src="DATA:image/svg+xml;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">)
617
+ expected = %q(<img>)
618
+ actual = safe_list_sanitize(input)
619
+ assert_equal(expected, actual)
620
+ end
621
+
622
+ def test_mediatype_other_disallowed
623
+ input = %q(<a href="data:foo;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">foo</a>)
624
+ expected = %q(<a>foo</a>)
625
+ actual = safe_list_sanitize(input)
626
+ assert_equal(expected, actual)
627
+
628
+ input = %q(<a href="DATA:foo;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">foo</a>)
629
+ expected = %q(<a>foo</a>)
630
+ actual = safe_list_sanitize(input)
631
+ assert_equal(expected, actual)
632
+ end
633
+
634
+ def test_scrubbing_svg_attr_values_that_allow_ref
635
+ input = %Q(<div fill="yellow url(http://bad.com/) #fff">hey</div>)
636
+ expected = %Q(<div fill="yellow #fff">hey</div>)
637
+ actual = scope_allowed_attributes %w(fill) do
638
+ safe_list_sanitize(input)
639
+ end
640
+
641
+ assert_equal(expected, actual)
642
+ end
643
+
644
+ def test_style_with_css_payload
645
+ input, tags = "<style>div > span { background: \"red\"; }</style>", ["style"]
646
+ expected = "<style>div &gt; span { background: \"red\"; }</style>"
647
+ actual = safe_list_sanitize(input, tags: tags)
648
+
649
+ assert_equal(expected, actual)
650
+ end
651
+
652
+ def test_combination_of_select_and_style_with_css_payload
653
+ input, tags = "<select><style>div > span { background: \"red\"; }</style></select>", ["select", "style"]
654
+ expected = "<select><style>div &gt; span { background: \"red\"; }</style></select>"
655
+ actual = safe_list_sanitize(input, tags: tags)
656
+
657
+ assert_equal(expected, actual)
658
+ end
659
+
660
+ def test_combination_of_select_and_style_with_script_payload
661
+ input, tags = "<select><style><script>alert(1)</script></style></select>", ["select", "style"]
662
+ expected = "<select><style>&lt;script&gt;alert(1)&lt;/script&gt;</style></select>"
663
+ actual = safe_list_sanitize(input, tags: tags)
664
+
665
+ assert_equal(expected, actual)
666
+ end
667
+
668
+ def test_combination_of_svg_and_style_with_script_payload
669
+ input, tags = "<svg><style><script>alert(1)</script></style></svg>", ["svg", "style"]
670
+ expected = "<svg><style>&lt;script&gt;alert(1)&lt;/script&gt;</style></svg>"
671
+ actual = safe_list_sanitize(input, tags: tags)
672
+
673
+ assert_equal(expected, actual)
674
+ end
675
+
676
+ def test_combination_of_math_and_style_with_img_payload
677
+ input, tags = "<math><style><img src=x onerror=alert(1)></style></math>", ["math", "style"]
678
+ expected = "<math><style>&lt;img src=x onerror=alert(1)&gt;</style></math>"
679
+ actual = safe_list_sanitize(input, tags: tags)
680
+
681
+ assert_equal(expected, actual)
682
+
683
+ input, tags = "<math><style><img src=x onerror=alert(1)></style></math>", ["math", "style", "img"]
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
+ end
689
+
690
+ def test_combination_of_svg_and_style_with_img_payload
691
+ input, tags = "<svg><style><img src=x onerror=alert(1)></style></svg>", ["svg", "style"]
692
+ expected = "<svg><style>&lt;img src=x onerror=alert(1)&gt;</style></svg>"
693
+ actual = safe_list_sanitize(input, tags: tags)
694
+
695
+ assert_equal(expected, actual)
696
+
697
+ input, tags = "<svg><style><img src=x onerror=alert(1)></style></svg>", ["svg", "style", "img"]
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)
521
702
  end
522
703
 
523
704
  protected
@@ -565,4 +746,26 @@ protected
565
746
  ensure
566
747
  Rails::Html::SafeListSanitizer.allowed_attributes = old_attributes
567
748
  end
749
+
750
+ # note that this is used for testing CSS hex encoding: \\[0-9a-f]{1,6}
751
+ def convert_to_css_hex(string, escape_parens=false)
752
+ string.chars.map do |c|
753
+ if !escape_parens && (c == "(" || c == ")")
754
+ c
755
+ else
756
+ format('\00%02X', c.ord)
757
+ end
758
+ end.join
759
+ end
760
+
761
+ def libxml_2_9_14_recovery_lt?
762
+ # changed in 2.9.14, see https://github.com/sparklemotion/nokogiri/releases/tag/v1.13.5
763
+ Nokogiri.method(:uses_libxml?).arity == -1 && Nokogiri.uses_libxml?(">= 2.9.14")
764
+ end
765
+
766
+ def libxml_2_9_14_recovery_lt_bang?
767
+ # changed in 2.9.14, see https://github.com/sparklemotion/nokogiri/releases/tag/v1.13.5
768
+ # then reverted in 2.10.0, see https://gitlab.gnome.org/GNOME/libxml2/-/issues/380
769
+ Nokogiri.method(:uses_libxml?).arity == -1 && Nokogiri.uses_libxml?("= 2.9.14")
770
+ end
568
771
  end
@@ -41,6 +41,16 @@ class PermitScrubberTest < ScrubberTest
41
41
  assert_scrubbed '<tag>hello</tag>', 'hello'
42
42
  end
43
43
 
44
+ def test_default_scrub_removes_comments
45
+ assert_scrubbed('<div>one</div><!-- two --><span>three</span>',
46
+ '<div>one</div><span>three</span>')
47
+ end
48
+
49
+ def test_default_scrub_removes_processing_instructions
50
+ assert_scrubbed('<div>one</div><?div two><span>three</span>',
51
+ '<div>one</div><span>three</span>')
52
+ end
53
+
44
54
  def test_default_attributes_removal_behavior
45
55
  assert_scrubbed '<p cooler="hello">hello</p>', '<p>hello</p>'
46
56
  end
@@ -56,6 +66,12 @@ class PermitScrubberTest < ScrubberTest
56
66
  assert_scrubbed html, '<tag>leave me now</tag>'
57
67
  end
58
68
 
69
+ def test_leaves_comments_when_supplied_as_tag
70
+ @scrubber.tags = %w(div comment)
71
+ assert_scrubbed('<div>one</div><!-- two --><span>three</span>',
72
+ '<div>one</div><!-- two -->three')
73
+ end
74
+
59
75
  def test_leaves_only_supplied_tags_nested
60
76
  html = '<tag>leave <em>me <span>now</span></em></tag>'
61
77
  @scrubber.tags = %w(tag)
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.3.0
4
+ version: 1.4.4
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: 2019-10-06 00:00:00.000000000 Z
12
+ date: 2022-12-13 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
@@ -101,7 +107,11 @@ files:
101
107
  homepage: https://github.com/rails/rails-html-sanitizer
102
108
  licenses:
103
109
  - MIT
104
- metadata: {}
110
+ metadata:
111
+ bug_tracker_uri: https://github.com/rails/rails-html-sanitizer/issues
112
+ changelog_uri: https://github.com/rails/rails-html-sanitizer/blob/v1.4.4/CHANGELOG.md
113
+ documentation_uri: https://www.rubydoc.info/gems/rails-html-sanitizer/1.4.4
114
+ source_code_uri: https://github.com/rails/rails-html-sanitizer/tree/v1.4.4
105
115
  post_install_message:
106
116
  rdoc_options: []
107
117
  require_paths:
@@ -117,10 +127,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
117
127
  - !ruby/object:Gem::Version
118
128
  version: '0'
119
129
  requirements: []
120
- rubygems_version: 3.0.3
130
+ rubygems_version: 3.3.7
121
131
  signing_key:
122
132
  specification_version: 4
123
133
  summary: This gem is responsible to sanitize HTML fragments in Rails applications.
124
134
  test_files:
125
- - test/scrubbers_test.rb
126
135
  - test/sanitizer_test.rb
136
+ - test/scrubbers_test.rb