rails-html-sanitizer 1.3.0 → 1.4.3
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.
Potentially problematic release.
This version of rails-html-sanitizer might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +43 -0
- data/README.md +3 -1
- data/lib/rails/html/sanitizer/version.rb +1 -1
- data/lib/rails/html/sanitizer.rb +18 -1
- data/lib/rails/html/scrubbers.rb +1 -1
- data/test/sanitizer_test.rb +118 -19
- data/test/scrubbers_test.rb +16 -0
- metadata +9 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2f00d9f256478eb753c8d211c3b25efa4204bbdbc9c5abf0415413c811a2e404
|
4
|
+
data.tar.gz: 65d3871aa798dfbbfb1138b666d475b590e347cdb66614d6d39b72ad3531c742
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e6e31eaa72b1a2e8356aae50600ac784f85a80828cbc49ce8061384ecd3f21a1d8eaee69845dc08537c5102728c3cc41a72cb3ed8b9789c4921038398afa61e2
|
7
|
+
data.tar.gz: 6b14a49842eaf4c3e0fbae5acd28fdf32a5deb6cd42f769aada848226847180c4d3a67a9dcbc439e1a4855699b0ea694cb4c7b6ee173391ac841bd334ae44b6f
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,46 @@
|
|
1
|
+
## 1.4.3 / 2022-06-09
|
2
|
+
|
3
|
+
* Address a possible XSS vulnerability with certain configurations of Rails::Html::Sanitizer.
|
4
|
+
|
5
|
+
Prevent the combination of `select` and `style` as allowed tags in SafeListSanitizer.
|
6
|
+
|
7
|
+
Fixes CVE-2022-32209
|
8
|
+
|
9
|
+
*Mike Dalessio*
|
10
|
+
|
11
|
+
|
12
|
+
## 1.4.2 / 2021-08-23
|
13
|
+
|
14
|
+
* Slightly improve performance.
|
15
|
+
|
16
|
+
Assuming elements are more common than comments, make one less method call per node.
|
17
|
+
|
18
|
+
*Mike Dalessio*
|
19
|
+
|
20
|
+
## 1.4.1 / 2021-08-18
|
21
|
+
|
22
|
+
* Fix regression in v1.4.0 that did not pass comment nodes to the scrubber.
|
23
|
+
|
24
|
+
Some scrubbers will want to override the default behavior and allow comments, but v1.4.0 only
|
25
|
+
passed through elements to the scrubber's `keep_node?` method.
|
26
|
+
|
27
|
+
This change once again allows the scrubber to make the decision on comment nodes, but still skips
|
28
|
+
other non-elements like processing instructions (see #115).
|
29
|
+
|
30
|
+
*Mike Dalessio*
|
31
|
+
|
32
|
+
## 1.4.0 / 2021-08-18
|
33
|
+
|
34
|
+
* Processing Instructions are no longer allowed by Rails::Html::PermitScrubber
|
35
|
+
|
36
|
+
Previously, a PI with a name (or "target") matching an allowed tag name was not scrubbed. There
|
37
|
+
are no known security issues associated with these PIs, but similar to comments it's preferred to
|
38
|
+
omit these nodes when possible from sanitized output.
|
39
|
+
|
40
|
+
Fixes #115.
|
41
|
+
|
42
|
+
*Mike Dalessio*
|
43
|
+
|
1
44
|
## 1.3.0
|
2
45
|
|
3
46
|
* 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
|
data/lib/rails/html/sanitizer.rb
CHANGED
@@ -141,8 +141,25 @@ 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
|
+
|
144
157
|
def allowed_tags(options)
|
145
|
-
options[:tags]
|
158
|
+
if options[:tags]
|
159
|
+
remove_safelist_tag_combinations(options[:tags])
|
160
|
+
else
|
161
|
+
self.class.allowed_tags
|
162
|
+
end
|
146
163
|
end
|
147
164
|
|
148
165
|
def allowed_attributes(options)
|
data/lib/rails/html/scrubbers.rb
CHANGED
data/test/sanitizer_test.rb
CHANGED
@@ -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
|
-
|
16
|
-
assert_equal '<script>alert("XSS");</script>', sanitizer.sanitize('<script><script></script>alert("XSS");<script><</script>/</script><script>script></script>', tags: %w(em))
|
17
|
+
assert_equal '<script>alert("XSS");</script>', 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
|
-
|
21
|
-
assert_equal '<script>alert("XSS");</script>', sanitizer.sanitize('<style><script></style>alert("XSS");<style><</style>/</style><style>script></style>', tags: %w(em))
|
21
|
+
assert_equal '<script>alert("XSS");</script>', 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
|
-
|
57
|
+
expected = libxml_2_9_14_recovery? ? %{<" 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
|
-
|
79
|
+
input = "This is <-- not\n a comment here."
|
80
|
+
expected = libxml_2_9_14_recovery? ? %{This is <-- 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
|
-
|
85
|
+
input = "This has a <![CDATA[<section>]]> here."
|
86
|
+
expected = libxml_2_9_14_recovery? ? %{This has a <![CDATA[]]> here.} : %{This has a ]]> here.}
|
87
|
+
assert_equal(expected, full_sanitize(input))
|
83
88
|
end
|
84
89
|
|
85
90
|
def test_strip_unclosed_cdata
|
86
|
-
|
91
|
+
input = "This has an unclosed <![CDATA[<section>]] here..."
|
92
|
+
expected = libxml_2_9_14_recovery? ? %{This has an unclosed <![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 "
|
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 "
|
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
|
-
|
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
|
-
|
417
|
-
|
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
|
-
|
460
|
+
input = "<![CDATA[<span>section</span>]]>"
|
461
|
+
expected = libxml_2_9_14_recovery? ? %{<![CDATA[<span>section</span>]]>} : %{section]]>}
|
462
|
+
assert_sanitized(input, expected)
|
436
463
|
end
|
437
464
|
|
438
465
|
def test_should_sanitize_unterminated_cdata_section
|
439
|
-
|
466
|
+
input = "<![CDATA[<span>neverending..."
|
467
|
+
expected = libxml_2_9_14_recovery? ? %{<![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
|
-
|
519
|
+
acceptable_results = [
|
520
|
+
# nokogiri w/vendored+patched libxml2
|
521
|
+
%{<a href="examp<!--%22%20unsafeattr=foo()>-->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
|
-
|
535
|
+
acceptable_results = [
|
536
|
+
# nokogiri w/vendored+patched libxml2
|
537
|
+
%{<a src="examp<!--%22%20unsafeattr=foo()>-->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
|
-
|
551
|
+
acceptable_results = [
|
552
|
+
# nokogiri w/vendored+patched libxml2
|
553
|
+
%{<a name="examp<!--%22%20unsafeattr=foo()>-->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,40 @@ class SanitizersTest < Minitest::Test
|
|
517
564
|
|
518
565
|
text = safe_list_sanitize(html, attributes: ['action'])
|
519
566
|
|
520
|
-
|
567
|
+
acceptable_results = [
|
568
|
+
# nokogiri w/vendored+patched libxml2
|
569
|
+
%{<a action="examp<!--%22%20unsafeattr=foo()>-->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
|
+
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
|
+
|
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)
|
599
|
+
end
|
600
|
+
refute_includes(sanitized, "style")
|
521
601
|
end
|
522
602
|
|
523
603
|
protected
|
@@ -565,4 +645,23 @@ protected
|
|
565
645
|
ensure
|
566
646
|
Rails::Html::SafeListSanitizer.allowed_attributes = old_attributes
|
567
647
|
end
|
648
|
+
|
649
|
+
# note that this is used for testing CSS hex encoding: \\[0-9a-f]{1,6}
|
650
|
+
def convert_to_css_hex(string, escape_parens=false)
|
651
|
+
string.chars.map do |c|
|
652
|
+
if !escape_parens && (c == "(" || c == ")")
|
653
|
+
c
|
654
|
+
else
|
655
|
+
format('\00%02X', c.ord)
|
656
|
+
end
|
657
|
+
end.join
|
658
|
+
end
|
659
|
+
|
660
|
+
def libxml_2_9_14_recovery?
|
661
|
+
Nokogiri.method(:uses_libxml?).arity == -1 && Nokogiri.uses_libxml?(">= 2.9.14")
|
662
|
+
end
|
663
|
+
|
664
|
+
def html5_mode?
|
665
|
+
::Loofah.respond_to?(:html5_mode?) && ::Loofah.html5_mode?
|
666
|
+
end
|
568
667
|
end
|
data/test/scrubbers_test.rb
CHANGED
@@ -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
|
4
|
+
version: 1.4.3
|
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:
|
12
|
+
date: 2022-06-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: loofah
|
@@ -101,7 +101,11 @@ files:
|
|
101
101
|
homepage: https://github.com/rails/rails-html-sanitizer
|
102
102
|
licenses:
|
103
103
|
- MIT
|
104
|
-
metadata:
|
104
|
+
metadata:
|
105
|
+
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
|
105
109
|
post_install_message:
|
106
110
|
rdoc_options: []
|
107
111
|
require_paths:
|
@@ -117,10 +121,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
121
|
- !ruby/object:Gem::Version
|
118
122
|
version: '0'
|
119
123
|
requirements: []
|
120
|
-
rubygems_version: 3.
|
124
|
+
rubygems_version: 3.3.5
|
121
125
|
signing_key:
|
122
126
|
specification_version: 4
|
123
127
|
summary: This gem is responsible to sanitize HTML fragments in Rails applications.
|
124
128
|
test_files:
|
125
|
-
- test/scrubbers_test.rb
|
126
129
|
- test/sanitizer_test.rb
|
130
|
+
- test/scrubbers_test.rb
|