rails-html-sanitizer 1.6.0.rc2 → 1.6.1

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: 3ce8562c96b3e842ebf50227e682c3fa948ebf8474786f100dbf78adff7f98d0
4
- data.tar.gz: 7ca7beb76be35dea0dd926819212445e885a2b205ca0b2e45628f58d734a1a9f
3
+ metadata.gz: ad86488f25943fb1d0050513a08581231534a8552e4de66329270845ff650b12
4
+ data.tar.gz: ca70b518cc54e0ec2e224a5c3dbd82d567e39fd2d2ce3538e308f4d8846234b4
5
5
  SHA512:
6
- metadata.gz: 30d9b9288698da75f713811e8b507edda0645eb4a485b0466847a4b8246aa854cd12e4dae238e61b09e371c950ee8516595b39207d4b10323bf81cf74b0a5114
7
- data.tar.gz: 81698f017c423bac3434e7129b70c4ecba80b27a4f8c6d86294d548aeeb9238d98cd65c15f06bcf62c6ee104e8b3beca782c8e9b338302590c33b65ab9ed8121
6
+ metadata.gz: f6e2db01e0cd52d3bdcae7ceb90a081998111e84f19f4908d73a9b229a0ec87edfc10f05772b057d2c3e6c9cd08df267f82070f16015d9953d3edc85002dcafd
7
+ data.tar.gz: 746475ff0522b512e28f7bfd83a7e54be7445a29abcd9b1c8aa16c7b6c39b6f97f5e553ac228b3bad98af8b5f828c4fb6c311f75ddd4b286045ba9a353f0fd8a
data/CHANGELOG.md CHANGED
@@ -1,4 +1,55 @@
1
- ## 1.6.0.rc2 / 2023-05-24
1
+ ## 1.6.1 / 2024-12-02
2
+
3
+ This is a performance and security release which addresses several possible XSS vulnerabilities.
4
+
5
+ * The dependency on Nokogiri is updated to v1.15.7 or >=1.16.8.
6
+
7
+ This change addresses CVE-2024-53985 (GHSA-w8gc-x259-rc7x).
8
+
9
+ *Mike Dalessio*
10
+
11
+ * Disallowed tags will be pruned when they appear in foreign content (i.e. SVG or MathML content),
12
+ regardless of the `prune:` option value. Previously, disallowed tags were "stripped" unless the
13
+ gem was configured with the `prune: true` option.
14
+
15
+ The CVEs addressed by this change are:
16
+
17
+ - CVE-2024-53986 (GHSA-638j-pmjw-jq48)
18
+ - CVE-2024-53987 (GHSA-2x5m-9ch4-qgrr)
19
+
20
+ *Mike Dalessio*
21
+
22
+ * The tags "noscript", "mglyph", and "malignmark" will not be allowed, even if explicitly added to
23
+ the allowlist. If applications try to allow any of these tags, a warning is emitted and the tags
24
+ are removed from the allow-list.
25
+
26
+ The CVEs addressed by this change are:
27
+
28
+ - CVE-2024-53988 (GHSA-cfjx-w229-hgx5)
29
+ - CVE-2024-53989 (GHSA-rxv5-gxqc-xx8g)
30
+
31
+ Please note that we _may_ restore support for allowing "noscript" in a future release. We do not
32
+ expect to ever allow "mglyph" or "malignmark", though, especially since browser support is minimal
33
+ for these tags.
34
+
35
+ *Mike Dalessio*
36
+
37
+ * Improve performance by eliminating needless operations on attributes that are being removed. #188
38
+
39
+ *Mike Dalessio*
40
+
41
+
42
+ ## 1.6.0 / 2023-05-26
43
+
44
+ * Dependencies have been updated:
45
+
46
+ - Loofah `~>2.21` and Nokogiri `~>1.14` for HTML5 parser support
47
+ - As a result, required Ruby version is now `>= 2.7.0`
48
+
49
+ Security updates will continue to be made on the `1.5.x` release branch as long as Rails 6.1
50
+ (which supports Ruby 2.5) is still in security support.
51
+
52
+ *Mike Dalessio*
2
53
 
3
54
  * HTML5 standards-compliant sanitizers are now available on platforms supported by
4
55
  Nokogiri::HTML5. These are available as:
data/README.md CHANGED
@@ -7,51 +7,6 @@ Rails HTML Sanitizer is only intended to be used with Rails applications. If you
7
7
 
8
8
  ## Usage
9
9
 
10
- ### A note on HTML entities
11
-
12
- __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 be sanitized *again* at page-render time.__
13
-
14
- Proper HTML sanitization will replace some characters with HTML entities. For example, text containing a `<` character will be updated to contain `&lt;` to ensure that the markup is well-formed.
15
-
16
- This is important to keep in mind because __HTML entities will render improperly if they are sanitized twice.__
17
-
18
-
19
- #### A concrete example showing the problem that can arise
20
-
21
- 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.`.
22
-
23
- If you sanitize this before persisting it in the database, the stored string will be `JPMorgan Chase &amp; Co.`
24
-
25
- 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.".
26
-
27
- 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.
28
-
29
-
30
- #### Suggested alternatives
31
-
32
- 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.
33
-
34
- 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.
35
-
36
- 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.
37
-
38
-
39
- ### A note on module names
40
-
41
- In versions < 1.6, the only module defined by this library was `Rails::Html`. Starting in 1.6, we define three additional modules:
42
-
43
- - `Rails::HTML` for general functionality (replacing `Rails::Html`)
44
- - `Rails::HTML4` containing sanitizers that parse content as HTML4
45
- - `Rails::HTML5` containing sanitizers that parse content as HTML5 (if supported)
46
-
47
- The following aliases are maintained for backwards compatibility:
48
-
49
- - `Rails::Html` points to `Rails::HTML`
50
- - `Rails::HTML::FullSanitizer` points to `Rails::HTML4::FullSanitizer`
51
- - `Rails::HTML::LinkSanitizer` points to `Rails::HTML4::LinkSanitizer`
52
- - `Rails::HTML::SafeListSanitizer` points to `Rails::HTML4::SafeListSanitizer`
53
-
54
-
55
10
  ### Sanitizers
56
11
 
57
12
  All sanitizers respond to `sanitize`, and are available in variants that use either HTML4 or HTML5 parsing, under the `Rails::HTML4` and `Rails::HTML5` namespaces, respectively.
@@ -75,10 +30,6 @@ full_sanitizer.sanitize("<b>Bold</b> no more! <a href='more.html'>See more here
75
30
  # => Bold no more! See more here...
76
31
  ```
77
32
 
78
- HTML5 version:
79
-
80
-
81
-
82
33
  #### LinkSanitizer
83
34
 
84
35
  ```ruby
@@ -219,6 +170,51 @@ Using the `CommentScrubber` from above, you can use this in a Rails view like so
219
170
  <%= sanitize @comment, scrubber: CommentScrubber.new %>
220
171
  ```
221
172
 
173
+ ### A note on HTML entities
174
+
175
+ __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 be sanitized *again* at page-render time.__
176
+
177
+ Proper HTML sanitization will replace some characters with HTML entities. For example, text containing a `<` character will be updated to contain `&lt;` to ensure that the markup is well-formed.
178
+
179
+ This is important to keep in mind because __HTML entities will render improperly if they are sanitized twice.__
180
+
181
+
182
+ #### A concrete example showing the problem that can arise
183
+
184
+ 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.`.
185
+
186
+ If you sanitize this before persisting it in the database, the stored string will be `JPMorgan Chase &amp; Co.`
187
+
188
+ 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.".
189
+
190
+ 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.
191
+
192
+
193
+ #### Suggested alternatives
194
+
195
+ 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.
196
+
197
+ 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.
198
+
199
+ 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.
200
+
201
+
202
+ ### A note on module names
203
+
204
+ In versions < 1.6, the only module defined by this library was `Rails::Html`. Starting in 1.6, we define three additional modules:
205
+
206
+ - `Rails::HTML` for general functionality (replacing `Rails::Html`)
207
+ - `Rails::HTML4` containing sanitizers that parse content as HTML4
208
+ - `Rails::HTML5` containing sanitizers that parse content as HTML5 (if supported)
209
+
210
+ The following aliases are maintained for backwards compatibility:
211
+
212
+ - `Rails::Html` points to `Rails::HTML`
213
+ - `Rails::HTML::FullSanitizer` points to `Rails::HTML4::FullSanitizer`
214
+ - `Rails::HTML::LinkSanitizer` points to `Rails::HTML4::LinkSanitizer`
215
+ - `Rails::HTML::SafeListSanitizer` points to `Rails::HTML4::SafeListSanitizer`
216
+
217
+
222
218
  ## Installation
223
219
 
224
220
  Add this line to your application's Gemfile:
@@ -234,6 +230,15 @@ Or install it yourself as:
234
230
  $ gem install rails-html-sanitizer
235
231
 
236
232
 
233
+ ## Support matrix
234
+
235
+ | branch | ruby support | actively maintained | security support |
236
+ |--------|--------------|---------------------|----------------------------------------|
237
+ | 1.6.x | >= 2.7 | yes | yes |
238
+ | 1.5.x | >= 2.5 | no | while Rails 6.1 is in security support |
239
+ | 1.4.x | >= 1.8.7 | no | no |
240
+
241
+
237
242
  ## Read more
238
243
 
239
244
  Loofah is what underlies the sanitizers and scrubbers of rails-html-sanitizer.
@@ -3,7 +3,7 @@
3
3
  module Rails
4
4
  module HTML
5
5
  class Sanitizer
6
- VERSION = "1.6.0.rc2"
6
+ VERSION = "1.6.1"
7
7
  end
8
8
  end
9
9
  end
@@ -106,6 +106,7 @@ module Rails
106
106
  "ins",
107
107
  "kbd",
108
108
  "li",
109
+ "mark",
109
110
  "ol",
110
111
  "p",
111
112
  "pre",
@@ -72,10 +72,11 @@ module Rails
72
72
  return CONTINUE if skip_node?(node)
73
73
 
74
74
  unless (node.element? || node.comment?) && keep_node?(node)
75
- return STOP if scrub_node(node) == STOP
75
+ return STOP unless scrub_node(node) == CONTINUE
76
76
  end
77
77
 
78
78
  scrub_attributes(node)
79
+ CONTINUE
79
80
  end
80
81
 
81
82
  protected
@@ -100,15 +101,22 @@ module Rails
100
101
  end
101
102
 
102
103
  def scrub_node(node)
103
- node.before(node.children) unless prune # strip
104
+ # If a node has a namespace, then it's a tag in either a `math` or `svg` foreign context,
105
+ # and we should always prune it to avoid namespace confusion and mutation XSS vectors.
106
+ unless prune || node.namespace
107
+ node.before(node.children)
108
+ end
104
109
  node.remove
105
110
  end
106
111
 
107
112
  def scrub_attributes(node)
108
113
  if @attributes
109
114
  node.attribute_nodes.each do |attr|
110
- attr.remove if scrub_attribute?(attr.name)
111
- scrub_attribute(node, attr)
115
+ if scrub_attribute?(attr.name)
116
+ attr.remove
117
+ else
118
+ scrub_attribute(node, attr)
119
+ end
112
120
  end
113
121
 
114
122
  scrub_css_attribute(node)
@@ -130,6 +138,24 @@ module Rails
130
138
  if var && !var.is_a?(Enumerable)
131
139
  raise ArgumentError, "You should pass :#{name} as an Enumerable"
132
140
  end
141
+
142
+ if var && name == :tags
143
+ if var.include?("mglyph")
144
+ warn("WARNING: 'mglyph' tags cannot be allowed by the PermitScrubber and will be scrubbed")
145
+ var.delete("mglyph")
146
+ end
147
+
148
+ if var.include?("malignmark")
149
+ warn("WARNING: 'malignmark' tags cannot be allowed by the PermitScrubber and will be scrubbed")
150
+ var.delete("malignmark")
151
+ end
152
+
153
+ if var.include?("noscript")
154
+ warn("WARNING: 'noscript' tags cannot be allowed by the PermitScrubber and will be scrubbed")
155
+ var.delete("noscript")
156
+ end
157
+ end
158
+
133
159
  var
134
160
  end
135
161
 
@@ -140,9 +166,7 @@ module Rails
140
166
  attr_node.node_name
141
167
  end
142
168
 
143
- if Loofah::HTML5::SafeList::ATTR_VAL_IS_URI.include?(attr_name)
144
- return if Loofah::HTML5::Scrub.scrub_uri_attribute(attr_node)
145
- end
169
+ return if Loofah::HTML5::SafeList::ATTR_VAL_IS_URI.include?(attr_name) && Loofah::HTML5::Scrub.scrub_uri_attribute(attr_node)
146
170
 
147
171
  if Loofah::HTML5::SafeList::SVG_ATTR_VAL_ALLOWS_REF.include?(attr_name)
148
172
  Loofah::HTML5::Scrub.scrub_attribute_that_allows_local_ref(attr_node)
@@ -728,8 +728,6 @@ module SanitizerTests
728
728
  end
729
729
 
730
730
  def test_uri_escaping_of_href_attr_in_a_tag_in_safe_list_sanitizer
731
- skip if RUBY_VERSION < "2.3"
732
-
733
731
  html = %{<a href='examp<!--" unsafeattr=foo()>-->le.com'>test</a>}
734
732
 
735
733
  text = safe_list_sanitize(html)
@@ -747,8 +745,6 @@ module SanitizerTests
747
745
  end
748
746
 
749
747
  def test_uri_escaping_of_src_attr_in_a_tag_in_safe_list_sanitizer
750
- skip if RUBY_VERSION < "2.3"
751
-
752
748
  html = %{<a src='examp<!--" unsafeattr=foo()>-->le.com'>test</a>}
753
749
 
754
750
  text = safe_list_sanitize(html)
@@ -766,8 +762,6 @@ module SanitizerTests
766
762
  end
767
763
 
768
764
  def test_uri_escaping_of_name_attr_in_a_tag_in_safe_list_sanitizer
769
- skip if RUBY_VERSION < "2.3"
770
-
771
765
  html = %{<a name='examp<!--" unsafeattr=foo()>-->le.com'>test</a>}
772
766
 
773
767
  text = safe_list_sanitize(html)
@@ -785,8 +779,6 @@ module SanitizerTests
785
779
  end
786
780
 
787
781
  def test_uri_escaping_of_name_action_in_a_tag_in_safe_list_sanitizer
788
- skip if RUBY_VERSION < "2.3"
789
-
790
782
  html = %{<a action='examp<!--" unsafeattr=foo()>-->le.com'>test</a>}
791
783
 
792
784
  text = safe_list_sanitize(html, attributes: ["action"])
@@ -926,7 +918,7 @@ module SanitizerTests
926
918
  # libxml2
927
919
  "<svg><style>&lt;script&gt;alert(1)&lt;/script&gt;</style></svg>",
928
920
  # libgumbo
929
- "<svg><style>alert(1)</style></svg>"
921
+ "<svg><style></style></svg>",
930
922
  ]
931
923
 
932
924
  assert_includes(acceptable_results, actual)
@@ -984,6 +976,76 @@ module SanitizerTests
984
976
  assert_includes(acceptable_results, actual)
985
977
  end
986
978
 
979
+ def test_combination_of_svg_and_style_with_escaped_img_payload
980
+ # https://hackerone.com/reports/2503220
981
+ input, tags = "<svg><style>&lt;img src onerror=alert(1)>", ["svg", "style"]
982
+ actual = safe_list_sanitize(input, tags: tags)
983
+ acceptable_results = [
984
+ # libxml2
985
+ "<svg><style>&amp;lt;img src onerror=alert(1)&gt;</style></svg>",
986
+ # libgumbo
987
+ "<svg><style>&lt;img src onerror=alert(1)&gt;</style></svg>",
988
+ ]
989
+
990
+ assert_includes(acceptable_results, actual)
991
+ end
992
+
993
+ def test_combination_of_math_and_style_with_escaped_img_payload
994
+ # https://hackerone.com/reports/2503220
995
+ input, tags = "<math><style>&lt;img src onerror=alert(1)>", ["math", "style"]
996
+ actual = safe_list_sanitize(input, tags: tags)
997
+ acceptable_results = [
998
+ # libxml2
999
+ "<math><style>&amp;lt;img src onerror=alert(1)&gt;</style></math>",
1000
+ # libgumbo
1001
+ "<math><style>&lt;img src onerror=alert(1)&gt;</style></math>",
1002
+ ]
1003
+
1004
+ assert_includes(acceptable_results, actual)
1005
+ end
1006
+
1007
+ def test_combination_of_style_and_disallowed_svg_with_script_payload
1008
+ # https://hackerone.com/reports/2519936
1009
+ input, tags = "<svg><style><style class='</style><script>alert(1)</script>'>", ["style"]
1010
+ actual = safe_list_sanitize(input, tags: tags)
1011
+ acceptable_results = [
1012
+ # libxml2
1013
+ "<style>&lt;style class='</style>alert(1)'&gt;",
1014
+ # libgumbo
1015
+ "",
1016
+ ]
1017
+
1018
+ assert_includes(acceptable_results, actual)
1019
+ end
1020
+
1021
+ def test_combination_of_style_and_disallowed_math_with_script_payload
1022
+ # https://hackerone.com/reports/2519936
1023
+ input, tags = "<math><style><style class='</style><script>alert(1)</script>'>", ["style"]
1024
+ actual = safe_list_sanitize(input, tags: tags)
1025
+ acceptable_results = [
1026
+ # libxml2
1027
+ "<style>&lt;style class='</style>alert(1)'&gt;",
1028
+ # libgumbo
1029
+ "",
1030
+ ]
1031
+
1032
+ assert_includes(acceptable_results, actual)
1033
+ end
1034
+
1035
+ def test_math_with_disallowed_mtext_and_img_payload
1036
+ # https://hackerone.com/reports/2519941
1037
+ input, tags = "<math><mtext><table><mglyph><style><img src=: onerror=alert(1)>", ["math", "style"]
1038
+ actual = safe_list_sanitize(input, tags: tags)
1039
+ acceptable_results = [
1040
+ # libxml2
1041
+ "<math><style>&lt;img src=: onerror=alert(1)&gt;</style></math>",
1042
+ # libgumbo
1043
+ "<math></math>",
1044
+ ]
1045
+
1046
+ assert_includes(acceptable_results, actual)
1047
+ end
1048
+
987
1049
  def test_should_sanitize_illegal_style_properties
988
1050
  raw = %(display:block; position:absolute; left:0; top:0; width:100%; height:100%; z-index:1; background-color:black; background-image:url(http://www.ragingplatypus.com/i/cam-full.jpg); background-x:center; background-y:center; background-repeat:repeat;)
989
1051
  expected = %(display:block;width:100%;height:100%;background-color:black;background-x:center;background-y:center;)
@@ -1034,6 +1096,64 @@ module SanitizerTests
1034
1096
  assert_equal "", sanitize_css(raw)
1035
1097
  end
1036
1098
 
1099
+ def test_should_prune_mglyph
1100
+ # https://hackerone.com/reports/2519936
1101
+ input = "<math><mtext><table><mglyph><style><img src=: onerror=alert(1)>"
1102
+ tags = %w(math mtext table mglyph style)
1103
+
1104
+ actual = nil
1105
+ assert_output(nil, /WARNING: 'mglyph' tags cannot be allowed by the PermitScrubber/) do
1106
+ actual = safe_list_sanitize(input, tags: tags)
1107
+ end
1108
+
1109
+ acceptable_results = [
1110
+ # libxml2
1111
+ "<math><mtext><table><style>&lt;img src=: onerror=alert(1)&gt;</style></table></mtext></math>",
1112
+ # libgumbo
1113
+ "<math><mtext><style><img src=: onerror=alert(1)></style><table></table></mtext></math>",
1114
+ ]
1115
+
1116
+ assert_includes(acceptable_results, actual)
1117
+ end
1118
+
1119
+ def test_should_prune_malignmark
1120
+ # https://hackerone.com/reports/2519936
1121
+ input = "<math><mtext><table><malignmark><style><img src=: onerror=alert(1)>"
1122
+ tags = %w(math mtext table malignmark style)
1123
+
1124
+ actual = nil
1125
+ assert_output(nil, /WARNING: 'malignmark' tags cannot be allowed by the PermitScrubber/) do
1126
+ actual = safe_list_sanitize(input, tags: tags)
1127
+ end
1128
+
1129
+ acceptable_results = [
1130
+ # libxml2
1131
+ "<math><mtext><table><style>&lt;img src=: onerror=alert(1)&gt;</style></table></mtext></math>",
1132
+ # libgumbo
1133
+ "<math><mtext><style><img src=: onerror=alert(1)></style><table></table></mtext></math>",
1134
+ ]
1135
+
1136
+ assert_includes(acceptable_results, actual)
1137
+ end
1138
+
1139
+ def test_should_prune_noscript
1140
+ # https://hackerone.com/reports/2509647
1141
+ input, tags = "<div><noscript><p id='</noscript><script>alert(1)</script>'></noscript>", ["p", "div", "noscript"]
1142
+ actual = nil
1143
+ assert_output(nil, /WARNING: 'noscript' tags cannot be allowed by the PermitScrubber/) do
1144
+ actual = safe_list_sanitize(input, tags: tags, attributes: %w(id))
1145
+ end
1146
+
1147
+ acceptable_results = [
1148
+ # libxml2
1149
+ "<div><p id=\"&lt;/noscript&gt;&lt;script&gt;alert(1)&lt;/script&gt;\"></p></div>",
1150
+ # libgumbo
1151
+ "<div><p id=\"</noscript><script>alert(1)</script>\"></p></div>",
1152
+ ]
1153
+
1154
+ assert_includes(acceptable_results, actual)
1155
+ end
1156
+
1037
1157
  protected
1038
1158
  def safe_list_sanitize(input, options = {})
1039
1159
  module_under_test::SafeListSanitizer.new.sanitize(input, options)
@@ -1083,5 +1203,84 @@ module SanitizerTests
1083
1203
  class HTML5SafeListSanitizerTest < Minitest::Test
1084
1204
  @module_under_test = Rails::HTML5
1085
1205
  include SafeListSanitizerTest
1206
+
1207
+ def test_should_not_be_vulnerable_to_nokogiri_foreign_style_serialization_bug
1208
+ # https://hackerone.com/reports/2503220
1209
+ input = "<svg><style>&lt;img src onerror=alert(1)>"
1210
+ result = Rails::HTML5::SafeListSanitizer.new.sanitize(input, tags: ["svg", "style"])
1211
+ browser = Nokogiri::HTML5::Document.parse(result)
1212
+ xss = browser.at_xpath("//img/@onerror")
1213
+
1214
+ assert_nil(xss)
1215
+ end
1216
+
1217
+ def test_should_not_be_vulnerable_to_ns_confusion_2519936
1218
+ # https://hackerone.com/reports/2519936
1219
+ input = "<math><style><style class='</style><script>alert(1)</script>'>"
1220
+ result = Rails::HTML5::SafeListSanitizer.new.sanitize(input, tags: ["style"])
1221
+ browser = Nokogiri::HTML5::Document.parse(result)
1222
+ xss = browser.at_xpath("//script")
1223
+
1224
+ assert_nil(xss)
1225
+ end
1226
+
1227
+ def test_should_not_be_vulnerable_to_ns_confusion_2519941
1228
+ # https://hackerone.com/reports/2519941
1229
+ input = "<math><mtext><table><mglyph><style><img src=: onerror=alert(1)>"
1230
+ result = Rails::HTML5::SafeListSanitizer.new.sanitize(input, tags: %w(math style))
1231
+ browser = Nokogiri::HTML5::Document.parse(result)
1232
+ xss = browser.at_xpath("//img/@onerror")
1233
+
1234
+ assert_nil(xss)
1235
+ end
1236
+
1237
+ def test_should_not_be_vulnerable_to_mglyph_namespace_confusion
1238
+ # https://hackerone.com/reports/2519936
1239
+ input = "<math><mtext><table><mglyph><style><img src=: onerror=alert(1)>"
1240
+ tags = %w(math mtext table mglyph style)
1241
+
1242
+ result = nil
1243
+ assert_output(nil, /WARNING/) do
1244
+ result = safe_list_sanitize(input, tags: tags)
1245
+ end
1246
+
1247
+ browser = Nokogiri::HTML5::Document.parse(result)
1248
+ xss = browser.at_xpath("//img/@onerror")
1249
+
1250
+ assert_nil(xss)
1251
+ end
1252
+
1253
+ def test_should_not_be_vulnerable_to_malignmark_namespace_confusion
1254
+ # https://hackerone.com/reports/2519936
1255
+ input = "<math><mtext><table><malignmark><style><img src=: onerror=alert(1)>"
1256
+ tags = %w(math mtext table malignmark style)
1257
+
1258
+ result = nil
1259
+ assert_output(nil, /WARNING/) do
1260
+ result = safe_list_sanitize(input, tags: tags)
1261
+ end
1262
+
1263
+ browser = Nokogiri::HTML5::Document.parse(result)
1264
+ xss = browser.at_xpath("//img/@onerror")
1265
+
1266
+ assert_nil(xss)
1267
+ end
1268
+
1269
+ def test_should_not_be_vulnerable_to_noscript_attacks
1270
+ # https://hackerone.com/reports/2509647
1271
+ skip("browser assertion requires parse_noscript_content_as_text") unless Nokogiri::VERSION >= "1.17"
1272
+
1273
+ input = '<noscript><p id="</noscript><script>alert(1)</script>"></noscript>'
1274
+
1275
+ result = nil
1276
+ assert_output(nil, /WARNING/) do
1277
+ result = Rails::HTML5::SafeListSanitizer.new.sanitize(input, tags: %w(p div noscript), attributes: %w(id class style))
1278
+ end
1279
+
1280
+ browser = Nokogiri::HTML5::Document.parse(result, parse_noscript_content_as_text: true)
1281
+ xss = browser.at_xpath("//script")
1282
+
1283
+ assert_nil(xss)
1284
+ end
1086
1285
  end if loofah_html5_support?
1087
1286
  end
@@ -121,6 +121,30 @@ class PermitScrubberTest < ScrubberTest
121
121
  assert_scrubbed html, '<tag></tag><tag cooler=""></tag>'
122
122
  end
123
123
 
124
+ def test_does_not_allow_safelisted_mglyph
125
+ # https://hackerone.com/reports/2519936
126
+ assert_output(nil, /WARNING: 'mglyph' tags cannot be allowed by the PermitScrubber/) do
127
+ @scrubber.tags = ["div", "mglyph", "span"]
128
+ end
129
+ assert_equal(["div", "span"], @scrubber.tags)
130
+ end
131
+
132
+ def test_does_not_allow_safelisted_malignmark
133
+ # https://hackerone.com/reports/2519936
134
+ assert_output(nil, /WARNING: 'malignmark' tags cannot be allowed by the PermitScrubber/) do
135
+ @scrubber.tags = ["div", "malignmark", "span"]
136
+ end
137
+ assert_equal(["div", "span"], @scrubber.tags)
138
+ end
139
+
140
+ def test_does_not_allow_safelisted_noscript
141
+ # https://hackerone.com/reports/2509647
142
+ assert_output(nil, /WARNING: 'noscript' tags cannot be allowed by the PermitScrubber/) do
143
+ @scrubber.tags = ["div", "noscript", "span"]
144
+ end
145
+ assert_equal(["div", "span"], @scrubber.tags)
146
+ end
147
+
124
148
  def test_leaves_text
125
149
  assert_scrubbed("some text")
126
150
  end
@@ -207,11 +231,65 @@ class ReturningStopFromScrubNodeTest < ScrubberTest
207
231
  end
208
232
  end
209
233
 
210
- def setup
211
- @scrubber = ScrubStopper.new
234
+ class ScrubContinuer < Rails::HTML::PermitScrubber
235
+ def scrub_node(node)
236
+ Loofah::Scrubber::CONTINUE
237
+ end
212
238
  end
213
239
 
214
240
  def test_returns_stop_from_scrub_if_scrub_node_does
241
+ @scrubber = ScrubStopper.new
215
242
  assert_scrub_stopped "<script>remove me</script>"
216
243
  end
244
+
245
+ def test_returns_continue_from_scrub_if_scrub_node_does
246
+ @scrubber = ScrubContinuer.new
247
+ assert_node_skipped "<script>keep me</script>"
248
+ end
249
+ end
250
+
251
+ class PermitScrubberMinimalOperationsTest < ScrubberTest
252
+ class TestPermitScrubber < Rails::HTML::PermitScrubber
253
+ def initialize
254
+ @scrub_attribute_args = []
255
+ @scrub_attributes_args = []
256
+
257
+ super
258
+
259
+ self.tags = ["div"]
260
+ self.attributes = ["class"]
261
+ end
262
+
263
+ def scrub_attributes(node)
264
+ @scrub_attributes_args << node.name
265
+
266
+ super
267
+ end
268
+
269
+ def scrub_attribute(node, attr)
270
+ @scrub_attribute_args << [node.name, attr.name]
271
+
272
+ super
273
+ end
274
+ end
275
+
276
+ def test_does_not_scrub_removed_attributes
277
+ @scrubber = TestPermitScrubber.new
278
+
279
+ input = "<div class='foo' href='bar'></div>"
280
+ frag = scrub_fragment(input)
281
+ assert_equal("<div class=\"foo\"></div>", frag)
282
+
283
+ assert_equal([["div", "class"]], @scrubber.instance_variable_get(:@scrub_attribute_args))
284
+ end
285
+
286
+ def test_does_not_scrub_attributes_of_a_removed_node
287
+ @scrubber = TestPermitScrubber.new
288
+
289
+ input = "<div class='foo' href='bar'><svg xlink:href='asdf'><set></set></svg></div>"
290
+ frag = scrub_fragment(input)
291
+ assert_equal("<div class=\"foo\"></div>", frag)
292
+
293
+ assert_equal(["div"], @scrubber.instance_variable_get(:@scrub_attributes_args))
294
+ end
217
295
  end
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-html-sanitizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.0.rc2
4
+ version: 1.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rafael Mendonça França
8
8
  - Kasper Timm Hansen
9
9
  - Mike Dalessio
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2023-05-24 00:00:00.000000000 Z
13
+ date: 2024-12-02 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: loofah
@@ -30,16 +30,70 @@ dependencies:
30
30
  name: nokogiri
31
31
  requirement: !ruby/object:Gem::Requirement
32
32
  requirements:
33
- - - "~>"
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 1.15.7
36
+ - - "!="
37
+ - !ruby/object:Gem::Version
38
+ version: 1.16.0
39
+ - - "!="
40
+ - !ruby/object:Gem::Version
41
+ version: 1.16.0.rc1
42
+ - - "!="
43
+ - !ruby/object:Gem::Version
44
+ version: 1.16.1
45
+ - - "!="
46
+ - !ruby/object:Gem::Version
47
+ version: 1.16.2
48
+ - - "!="
34
49
  - !ruby/object:Gem::Version
35
- version: '1.14'
50
+ version: 1.16.3
51
+ - - "!="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.16.4
54
+ - - "!="
55
+ - !ruby/object:Gem::Version
56
+ version: 1.16.5
57
+ - - "!="
58
+ - !ruby/object:Gem::Version
59
+ version: 1.16.6
60
+ - - "!="
61
+ - !ruby/object:Gem::Version
62
+ version: 1.16.7
36
63
  type: :runtime
37
64
  prerelease: false
38
65
  version_requirements: !ruby/object:Gem::Requirement
39
66
  requirements:
40
- - - "~>"
67
+ - - ">="
41
68
  - !ruby/object:Gem::Version
42
- version: '1.14'
69
+ version: 1.15.7
70
+ - - "!="
71
+ - !ruby/object:Gem::Version
72
+ version: 1.16.0
73
+ - - "!="
74
+ - !ruby/object:Gem::Version
75
+ version: 1.16.0.rc1
76
+ - - "!="
77
+ - !ruby/object:Gem::Version
78
+ version: 1.16.1
79
+ - - "!="
80
+ - !ruby/object:Gem::Version
81
+ version: 1.16.2
82
+ - - "!="
83
+ - !ruby/object:Gem::Version
84
+ version: 1.16.3
85
+ - - "!="
86
+ - !ruby/object:Gem::Version
87
+ version: 1.16.4
88
+ - - "!="
89
+ - !ruby/object:Gem::Version
90
+ version: 1.16.5
91
+ - - "!="
92
+ - !ruby/object:Gem::Version
93
+ version: 1.16.6
94
+ - - "!="
95
+ - !ruby/object:Gem::Version
96
+ version: 1.16.7
43
97
  description: HTML sanitization for Rails applications
44
98
  email:
45
99
  - rafaelmfranca@gmail.com
@@ -64,10 +118,10 @@ licenses:
64
118
  - MIT
65
119
  metadata:
66
120
  bug_tracker_uri: https://github.com/rails/rails-html-sanitizer/issues
67
- changelog_uri: https://github.com/rails/rails-html-sanitizer/blob/v1.6.0.rc2/CHANGELOG.md
68
- documentation_uri: https://www.rubydoc.info/gems/rails-html-sanitizer/1.6.0.rc2
69
- source_code_uri: https://github.com/rails/rails-html-sanitizer/tree/v1.6.0.rc2
70
- post_install_message:
121
+ changelog_uri: https://github.com/rails/rails-html-sanitizer/blob/v1.6.1/CHANGELOG.md
122
+ documentation_uri: https://www.rubydoc.info/gems/rails-html-sanitizer/1.6.1
123
+ source_code_uri: https://github.com/rails/rails-html-sanitizer/tree/v1.6.1
124
+ post_install_message:
71
125
  rdoc_options: []
72
126
  require_paths:
73
127
  - lib
@@ -78,12 +132,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
78
132
  version: 2.7.0
79
133
  required_rubygems_version: !ruby/object:Gem::Requirement
80
134
  requirements:
81
- - - ">"
135
+ - - ">="
82
136
  - !ruby/object:Gem::Version
83
- version: 1.3.1
137
+ version: '0'
84
138
  requirements: []
85
- rubygems_version: 3.4.10
86
- signing_key:
139
+ rubygems_version: 3.5.22
140
+ signing_key:
87
141
  specification_version: 4
88
142
  summary: This gem is responsible to sanitize HTML fragments in Rails applications.
89
143
  test_files: