sanitize 7.0.0 → 7.0.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.
@@ -1,537 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "common"
4
-
5
- describe "Sanitize::Transformers::CleanElement" do
6
- make_my_diffs_pretty!
7
- parallelize_me!
8
-
9
- strings = {
10
- basic: {
11
- html: '<b>Lo<!-- comment -->rem</b> <a href="pants" title="foo" style="text-decoration: underline;">ipsum</a> <a href="http://foo.com/"><strong>dolor</strong></a> sit<br/>amet <style>.foo { color: #fff; }</style> <script>alert("hello world");</script>',
12
- default: "Lorem ipsum dolor sit amet ",
13
- restricted: "<b>Lorem</b> ipsum <strong>dolor</strong> sit amet ",
14
- basic: '<b>Lorem</b> <a href="pants" rel="nofollow">ipsum</a> <a href="http://foo.com/" rel="nofollow"><strong>dolor</strong></a> sit<br>amet ',
15
- relaxed: '<b>Lorem</b> <a href="pants" title="foo" style="text-decoration: underline;">ipsum</a> <a href="http://foo.com/"><strong>dolor</strong></a> sit<br>amet <style>.foo { color: #fff; }</style> '
16
- },
17
-
18
- malformed: {
19
- html: 'Lo<!-- comment -->rem</b> <a href=pants title="foo>ipsum <a href="http://foo.com/"><strong>dolor</a></strong> sit<br/>amet <script>alert("hello world");',
20
- default: "Lorem dolor sit amet ",
21
- restricted: "Lorem <strong>dolor</strong> sit amet ",
22
- basic: 'Lorem <a href="pants" rel="nofollow"><strong>dolor</strong></a> sit<br>amet ',
23
- relaxed: 'Lorem <a href="pants" title="foo>ipsum <a href="><strong>dolor</strong></a> sit<br>amet '
24
- },
25
-
26
- unclosed: {
27
- html: "<p>a</p><blockquote>b",
28
- default: " a b ",
29
- restricted: " a b ",
30
- basic: "<p>a</p><blockquote>b</blockquote>",
31
- relaxed: "<p>a</p><blockquote>b</blockquote>"
32
- },
33
-
34
- malicious: {
35
- html: '<b>Lo<!-- comment -->rem</b> <a href="javascript:pants" title="foo">ipsum</a> <a href="http://foo.com/"><strong>dolor</strong></a> sit<br/>amet <<foo>script>alert("hello world");</script>',
36
- default: 'Lorem ipsum dolor sit amet &lt;script&gt;alert("hello world");',
37
- restricted: '<b>Lorem</b> ipsum <strong>dolor</strong> sit amet &lt;script&gt;alert("hello world");',
38
- basic: '<b>Lorem</b> <a rel="nofollow">ipsum</a> <a href="http://foo.com/" rel="nofollow"><strong>dolor</strong></a> sit<br>amet &lt;script&gt;alert("hello world");',
39
- relaxed: '<b>Lorem</b> <a title="foo">ipsum</a> <a href="http://foo.com/"><strong>dolor</strong></a> sit<br>amet &lt;script&gt;alert("hello world");'
40
- }
41
- }
42
-
43
- protocols = {
44
- "protocol-based JS injection: simple, no spaces" => {
45
- html: '<a href="javascript:alert(\'XSS\');">foo</a>',
46
- default: "foo",
47
- restricted: "foo",
48
- basic: '<a rel="nofollow">foo</a>',
49
- relaxed: "<a>foo</a>"
50
- },
51
-
52
- "protocol-based JS injection: simple, spaces before" => {
53
- html: '<a href="javascript :alert(\'XSS\');">foo</a>',
54
- default: "foo",
55
- restricted: "foo",
56
- basic: '<a rel="nofollow">foo</a>',
57
- relaxed: "<a>foo</a>"
58
- },
59
-
60
- "protocol-based JS injection: simple, spaces after" => {
61
- html: '<a href="javascript: alert(\'XSS\');">foo</a>',
62
- default: "foo",
63
- restricted: "foo",
64
- basic: '<a rel="nofollow">foo</a>',
65
- relaxed: "<a>foo</a>"
66
- },
67
-
68
- "protocol-based JS injection: simple, spaces before and after" => {
69
- html: '<a href="javascript : alert(\'XSS\');">foo</a>',
70
- default: "foo",
71
- restricted: "foo",
72
- basic: '<a rel="nofollow">foo</a>',
73
- relaxed: "<a>foo</a>"
74
- },
75
-
76
- "protocol-based JS injection: preceding colon" => {
77
- html: '<a href=":javascript:alert(\'XSS\');">foo</a>',
78
- default: "foo",
79
- restricted: "foo",
80
- basic: '<a rel="nofollow">foo</a>',
81
- relaxed: "<a>foo</a>"
82
- },
83
-
84
- "protocol-based JS injection: UTF-8 encoding" => {
85
- html: '<a href="javascript&#58;">foo</a>',
86
- default: "foo",
87
- restricted: "foo",
88
- basic: '<a rel="nofollow">foo</a>',
89
- relaxed: "<a>foo</a>"
90
- },
91
-
92
- "protocol-based JS injection: long UTF-8 encoding" => {
93
- html: '<a href="javascript&#0058;">foo</a>',
94
- default: "foo",
95
- restricted: "foo",
96
- basic: '<a rel="nofollow">foo</a>',
97
- relaxed: "<a>foo</a>"
98
- },
99
-
100
- "protocol-based JS injection: long UTF-8 encoding without semicolons" => {
101
- html: "<a href=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>foo</a>",
102
- default: "foo",
103
- restricted: "foo",
104
- basic: '<a rel="nofollow">foo</a>',
105
- relaxed: "<a>foo</a>"
106
- },
107
-
108
- "protocol-based JS injection: hex encoding" => {
109
- html: '<a href="javascript&#x3A;">foo</a>',
110
- default: "foo",
111
- restricted: "foo",
112
- basic: '<a rel="nofollow">foo</a>',
113
- relaxed: "<a>foo</a>"
114
- },
115
-
116
- "protocol-based JS injection: long hex encoding" => {
117
- html: '<a href="javascript&#x003A;">foo</a>',
118
- default: "foo",
119
- restricted: "foo",
120
- basic: '<a rel="nofollow">foo</a>',
121
- relaxed: "<a>foo</a>"
122
- },
123
-
124
- "protocol-based JS injection: hex encoding without semicolons" => {
125
- html: "<a href=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>foo</a>",
126
- default: "foo",
127
- restricted: "foo",
128
- basic: '<a rel="nofollow">foo</a>',
129
- relaxed: "<a>foo</a>"
130
- },
131
-
132
- "protocol-based JS injection: null char" => {
133
- html: "<img src=java\0script:alert(\"XSS\")>",
134
- default: "",
135
- restricted: "",
136
- basic: "",
137
- relaxed: "<img>"
138
- },
139
-
140
- "protocol-based JS injection: invalid URL char" => {
141
- html: '<img src=java\script:alert("XSS")>',
142
- default: "",
143
- restricted: "",
144
- basic: "",
145
- relaxed: "<img>"
146
- },
147
-
148
- "protocol-based JS injection: spaces and entities" => {
149
- html: '<img src=" &#14; javascript:alert(\'XSS\');">',
150
- default: "",
151
- restricted: "",
152
- basic: "",
153
- relaxed: "<img>"
154
- },
155
-
156
- "protocol whitespace" => {
157
- html: '<a href=" http://example.com/"></a>',
158
- default: "",
159
- restricted: "",
160
- basic: '<a href="http://example.com/" rel="nofollow"></a>',
161
- relaxed: '<a href="http://example.com/"></a>'
162
- }
163
- }
164
-
165
- describe "Default config" do
166
- it "should remove non-allowlisted elements, leaving safe contents behind" do
167
- _(Sanitize.fragment('foo <b>bar</b> <strong><a href="#a">baz</a></strong> quux'))
168
- .must_equal "foo bar baz quux"
169
-
170
- _(Sanitize.fragment('<script>alert("<xss>");</script>'))
171
- .must_equal ""
172
-
173
- _(Sanitize.fragment('<<script>script>alert("<xss>");</<script>>'))
174
- .must_equal "&lt;"
175
-
176
- _(Sanitize.fragment('< script <>> alert("<xss>");</script>'))
177
- .must_equal '&lt; script &lt;&gt;&gt; alert("");'
178
- end
179
-
180
- it "should surround the contents of :whitespace_elements with space characters when removing the element" do
181
- _(Sanitize.fragment("foo<div>bar</div>baz"))
182
- .must_equal "foo bar baz"
183
-
184
- _(Sanitize.fragment("foo<br>bar<br>baz"))
185
- .must_equal "foo bar baz"
186
-
187
- _(Sanitize.fragment("foo<hr>bar<hr>baz"))
188
- .must_equal "foo bar baz"
189
- end
190
-
191
- it "should not choke on several instances of the same element in a row" do
192
- _(Sanitize.fragment('<img src="http://www.google.com/intl/en_ALL/images/logo.gif"><img src="http://www.google.com/intl/en_ALL/images/logo.gif"><img src="http://www.google.com/intl/en_ALL/images/logo.gif"><img src="http://www.google.com/intl/en_ALL/images/logo.gif">'))
193
- .must_equal ""
194
- end
195
-
196
- it "should not preserve the content of removed `iframe` elements" do
197
- _(Sanitize.fragment("<iframe>hello! <script>alert(0)</script></iframe>"))
198
- .must_equal ""
199
- end
200
-
201
- it "should not preserve the content of removed `math` elements" do
202
- _(Sanitize.fragment("<math>hello! <script>alert(0)</script></math>"))
203
- .must_equal ""
204
- end
205
-
206
- it "should not preserve the content of removed `noembed` elements" do
207
- _(Sanitize.fragment("<noembed>hello! <script>alert(0)</script></noembed>"))
208
- .must_equal ""
209
- end
210
-
211
- it "should not preserve the content of removed `noframes` elements" do
212
- _(Sanitize.fragment("<noframes>hello! <script>alert(0)</script></noframes>"))
213
- .must_equal ""
214
- end
215
-
216
- it "should not preserve the content of removed `noscript` elements" do
217
- _(Sanitize.fragment("<noscript>hello! <script>alert(0)</script></noscript>"))
218
- .must_equal ""
219
- end
220
-
221
- it "should not preserve the content of removed `plaintext` elements" do
222
- _(Sanitize.fragment("<plaintext>hello! <script>alert(0)</script>"))
223
- .must_equal ""
224
- end
225
-
226
- it "should not preserve the content of removed `script` elements" do
227
- _(Sanitize.fragment("<script>hello! <script>alert(0)</script></script>"))
228
- .must_equal ""
229
- end
230
-
231
- it "should not preserve the content of removed `style` elements" do
232
- _(Sanitize.fragment("<style>hello! <script>alert(0)</script></style>"))
233
- .must_equal ""
234
- end
235
-
236
- it "should not preserve the content of removed `svg` elements" do
237
- _(Sanitize.fragment("<svg>hello! <script>alert(0)</script></svg>"))
238
- .must_equal ""
239
- end
240
-
241
- it "should not preserve the content of removed `xmp` elements" do
242
- _(Sanitize.fragment("<xmp>hello! <script>alert(0)</script></xmp>"))
243
- .must_equal ""
244
- end
245
-
246
- strings.each do |name, data|
247
- it "should clean #{name} HTML" do
248
- _(Sanitize.fragment(data[:html])).must_equal(data[:default])
249
- end
250
- end
251
-
252
- protocols.each do |name, data|
253
- it "should not allow #{name}" do
254
- _(Sanitize.fragment(data[:html])).must_equal(data[:default])
255
- end
256
- end
257
- end
258
-
259
- describe "Restricted config" do
260
- before do
261
- @s = Sanitize.new(Sanitize::Config::RESTRICTED)
262
- end
263
-
264
- strings.each do |name, data|
265
- it "should clean #{name} HTML" do
266
- _(@s.fragment(data[:html])).must_equal(data[:restricted])
267
- end
268
- end
269
-
270
- protocols.each do |name, data|
271
- it "should not allow #{name}" do
272
- _(@s.fragment(data[:html])).must_equal(data[:restricted])
273
- end
274
- end
275
- end
276
-
277
- describe "Basic config" do
278
- before do
279
- @s = Sanitize.new(Sanitize::Config::BASIC)
280
- end
281
-
282
- it "should not choke on valueless attributes" do
283
- _(@s.fragment("foo <a href>foo</a> bar"))
284
- .must_equal 'foo <a href="" rel="nofollow">foo</a> bar'
285
- end
286
-
287
- it "should downcase attribute names" do
288
- _(@s.fragment('<a HREF="javascript:alert(\'foo\')">bar</a>'))
289
- .must_equal '<a rel="nofollow">bar</a>'
290
- end
291
-
292
- strings.each do |name, data|
293
- it "should clean #{name} HTML" do
294
- _(@s.fragment(data[:html])).must_equal(data[:basic])
295
- end
296
- end
297
-
298
- protocols.each do |name, data|
299
- it "should not allow #{name}" do
300
- _(@s.fragment(data[:html])).must_equal(data[:basic])
301
- end
302
- end
303
- end
304
-
305
- describe "Relaxed config" do
306
- before do
307
- @s = Sanitize.new(Sanitize::Config::RELAXED)
308
- end
309
-
310
- it "should encode special chars in attribute values" do
311
- _(@s.fragment('<a href="http://example.com" title="<b>&eacute;xamples</b> & things">foo</a>'))
312
- .must_equal '<a href="http://example.com" title="<b>éxamples</b> &amp; things">foo</a>'
313
- end
314
-
315
- strings.each do |name, data|
316
- it "should clean #{name} HTML" do
317
- _(@s.fragment(data[:html])).must_equal(data[:relaxed])
318
- end
319
- end
320
-
321
- protocols.each do |name, data|
322
- it "should not allow #{name}" do
323
- _(@s.fragment(data[:html])).must_equal(data[:relaxed])
324
- end
325
- end
326
- end
327
-
328
- describe "Custom configs" do
329
- it "should allow attributes on all elements if allowlisted under :all" do
330
- input = '<p class="foo">bar</p>'
331
-
332
- _(Sanitize.fragment(input)).must_equal " bar "
333
-
334
- _(Sanitize.fragment(input, {
335
- elements: ["p"],
336
- attributes: {all: ["class"]}
337
- })).must_equal input
338
-
339
- _(Sanitize.fragment(input, {
340
- elements: ["p"],
341
- attributes: {"div" => ["class"]}
342
- })).must_equal "<p>bar</p>"
343
-
344
- _(Sanitize.fragment(input, {
345
- elements: ["p"],
346
- attributes: {"p" => ["title"], :all => ["class"]}
347
- })).must_equal input
348
- end
349
-
350
- it "should not allow relative URLs when relative URLs aren't allowlisted" do
351
- input = '<a href="/foo/bar">Link</a>'
352
-
353
- _(Sanitize.fragment(input,
354
- elements: ["a"],
355
- attributes: {"a" => ["href"]},
356
- protocols: {"a" => {"href" => ["http"]}})).must_equal "<a>Link</a>"
357
- end
358
-
359
- it "should allow relative URLs containing colons when the colon is not in the first path segment" do
360
- input = '<a href="/wiki/Special:Random">Random Page</a>'
361
-
362
- _(Sanitize.fragment(input, {
363
- elements: ["a"],
364
- attributes: {"a" => ["href"]},
365
- protocols: {"a" => {"href" => [:relative]}}
366
- })).must_equal input
367
- end
368
-
369
- it "should allow relative URLs containing colons when the colon is part of an anchor" do
370
- input = '<a href="#fn:1">Footnote 1</a>'
371
-
372
- _(Sanitize.fragment(input, {
373
- elements: ["a"],
374
- attributes: {"a" => ["href"]},
375
- protocols: {"a" => {"href" => [:relative]}}
376
- })).must_equal input
377
-
378
- input = '<a href="somepage#fn:1">Footnote 1</a>'
379
-
380
- _(Sanitize.fragment(input, {
381
- elements: ["a"],
382
- attributes: {"a" => ["href"]},
383
- protocols: {"a" => {"href" => [:relative]}}
384
- })).must_equal input
385
- end
386
-
387
- it "should remove the contents of filtered nodes when :remove_contents is true" do
388
- _(Sanitize.fragment("foo bar <div>baz<span>quux</span></div>",
389
- remove_contents: true)).must_equal "foo bar "
390
- end
391
-
392
- it "should remove the contents of specified nodes when :remove_contents is an Array or Set of element names as strings" do
393
- _(Sanitize.fragment('foo bar <div>baz<span>quux</span> <b>hi</b><script>alert("hello!");</script></div>',
394
- remove_contents: ["script", "span"])).must_equal "foo bar baz hi "
395
-
396
- _(Sanitize.fragment('foo bar <div>baz<span>quux</span> <b>hi</b><script>alert("hello!");</script></div>',
397
- remove_contents: Set.new(["script", "span"]))).must_equal "foo bar baz hi "
398
- end
399
-
400
- it "should remove the contents of specified nodes when :remove_contents is an Array or Set of element names as symbols" do
401
- _(Sanitize.fragment('foo bar <div>baz<span>quux</span> <b>hi</b><script>alert("hello!");</script></div>',
402
- remove_contents: [:script, :span])).must_equal "foo bar baz hi "
403
-
404
- _(Sanitize.fragment('foo bar <div>baz<span>quux</span> <b>hi</b><script>alert("hello!");</script></div>',
405
- remove_contents: Set.new([:script, :span]))).must_equal "foo bar baz hi "
406
- end
407
-
408
- it "should remove the contents of allowlisted iframes" do
409
- _(Sanitize.fragment("<iframe>hi <script>hello</script></iframe>",
410
- elements: ["iframe"])).must_equal "<iframe></iframe>"
411
- end
412
-
413
- it "should not allow arbitrary HTML5 data attributes by default" do
414
- _(Sanitize.fragment('<b data-foo="bar"></b>',
415
- elements: ["b"])).must_equal "<b></b>"
416
-
417
- _(Sanitize.fragment('<b class="foo" data-foo="bar"></b>',
418
- attributes: {"b" => ["class"]},
419
- elements: ["b"])).must_equal '<b class="foo"></b>'
420
- end
421
-
422
- it "should allow arbitrary HTML5 data attributes when the :attributes config includes :data" do
423
- s = Sanitize.new(
424
- attributes: {"b" => [:data]},
425
- elements: ["b"]
426
- )
427
-
428
- _(s.fragment('<b data-foo="valid" data-bar="valid"></b>'))
429
- .must_equal '<b data-foo="valid" data-bar="valid"></b>'
430
-
431
- _(s.fragment('<b data-="invalid"></b>'))
432
- .must_equal "<b></b>"
433
-
434
- _(s.fragment('<b data-="invalid"></b>'))
435
- .must_equal "<b></b>"
436
-
437
- _(s.fragment('<b data-xml="invalid"></b>'))
438
- .must_equal "<b></b>"
439
-
440
- _(s.fragment('<b data-xmlfoo="invalid"></b>'))
441
- .must_equal "<b></b>"
442
-
443
- _(s.fragment('<b data-f:oo="valid"></b>'))
444
- .must_equal "<b></b>"
445
-
446
- _(s.fragment('<b data-f/oo="partial"></b>'))
447
- .must_equal '<b data-f=""></b>' # Nokogiri quirk; not ideal, but harmless
448
-
449
- _(s.fragment('<b data-éfoo="valid"></b>'))
450
- .must_equal "<b></b>" # Another annoying Nokogiri quirk.
451
- end
452
-
453
- it "should replace whitespace_elements with configured :before and :after values" do
454
- s = Sanitize.new(
455
- whitespace_elements: {
456
- "p" => {before: "\n", after: "\n"},
457
- "div" => {before: "\n", after: "\n"},
458
- "br" => {before: "\n", after: "\n"}
459
- }
460
- )
461
-
462
- _(s.fragment("<p>foo</p>")).must_equal "\nfoo\n"
463
- _(s.fragment("<p>foo</p><p>bar</p>")).must_equal "\nfoo\n\nbar\n"
464
- _(s.fragment("foo<div>bar</div>baz")).must_equal "foo\nbar\nbaz"
465
- _(s.fragment("foo<br>bar<br>baz")).must_equal "foo\nbar\nbaz"
466
- end
467
-
468
- it "should handle protocols correctly regardless of case" do
469
- input = '<a href="hTTpS://foo.com/">Text</a>'
470
-
471
- _(Sanitize.fragment(input, {
472
- elements: ["a"],
473
- attributes: {"a" => ["href"]},
474
- protocols: {"a" => {"href" => ["https"]}}
475
- })).must_equal input
476
-
477
- input = '<a href="mailto:someone@example.com?Subject=Hello">Text</a>'
478
-
479
- _(Sanitize.fragment(input, {
480
- elements: ["a"],
481
- attributes: {"a" => ["href"]},
482
- protocols: {"a" => {"href" => ["https"]}}
483
- })).must_equal "<a>Text</a>"
484
- end
485
-
486
- it "should sanitize protocols in data attributes even if data attributes are generically allowed" do
487
- input = '<a data-url="mailto:someone@example.com">Text</a>'
488
-
489
- _(Sanitize.fragment(input, {
490
- elements: ["a"],
491
- attributes: {"a" => [:data]},
492
- protocols: {"a" => {"data-url" => ["https"]}}
493
- })).must_equal "<a>Text</a>"
494
-
495
- _(Sanitize.fragment(input, {
496
- elements: ["a"],
497
- attributes: {"a" => [:data]},
498
- protocols: {"a" => {"data-url" => ["mailto"]}}
499
- })).must_equal input
500
- end
501
-
502
- it "should prevent `<meta>` tags from being used to set a non-UTF-8 charset" do
503
- _(Sanitize.document('<html><head><meta charset="utf-8"></head><body>Howdy!</body></html>',
504
- elements: %w[html head meta body],
505
- attributes: {"meta" => ["charset"]})).must_equal "<html><head><meta charset=\"utf-8\"></head><body>Howdy!</body></html>"
506
-
507
- _(Sanitize.document('<html><meta charset="utf-8">Howdy!</html>',
508
- elements: %w[html meta],
509
- attributes: {"meta" => ["charset"]})).must_equal "<html><meta charset=\"utf-8\">Howdy!</html>"
510
-
511
- _(Sanitize.document('<html><meta charset="us-ascii">Howdy!</html>',
512
- elements: %w[html meta],
513
- attributes: {"meta" => ["charset"]})).must_equal "<html><meta charset=\"utf-8\">Howdy!</html>"
514
-
515
- _(Sanitize.document('<html><meta http-equiv="content-type" content=" text/html; charset=us-ascii">Howdy!</html>',
516
- elements: %w[html meta],
517
- attributes: {"meta" => %w[content http-equiv]})).must_equal "<html><meta http-equiv=\"content-type\" content=\" text/html;charset=utf-8\">Howdy!</html>"
518
-
519
- _(Sanitize.document('<html><meta http-equiv="Content-Type" content="text/plain;charset = us-ascii">Howdy!</html>',
520
- elements: %w[html meta],
521
- attributes: {"meta" => %w[content http-equiv]})).must_equal "<html><meta http-equiv=\"Content-Type\" content=\"text/plain;charset=utf-8\">Howdy!</html>"
522
- end
523
-
524
- it "should not modify `<meta>` tags that already set a UTF-8 charset" do
525
- _(Sanitize.document('<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"></head><body>Howdy!</body></html>',
526
- elements: %w[html head meta body],
527
- attributes: {"meta" => %w[content http-equiv]})).must_equal "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"></head><body>Howdy!</body></html>"
528
- end
529
-
530
- it "always removes `<noscript>` elements even if `noscript` is in the allowlist" do
531
- assert_equal(
532
- "",
533
- Sanitize.fragment("<noscript>foo</noscript>", elements: ["noscript"])
534
- )
535
- end
536
- end
537
- end
data/test/test_config.rb DELETED
@@ -1,66 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "common"
4
-
5
- describe "Config" do
6
- make_my_diffs_pretty!
7
- parallelize_me!
8
-
9
- def verify_deeply_frozen(config)
10
- _(config).must_be :frozen?
11
-
12
- if Hash === config
13
- config.each_value { |v| verify_deeply_frozen(v) }
14
- elsif Set === config || Array === config
15
- config.each { |v| verify_deeply_frozen(v) }
16
- end
17
- end
18
-
19
- it "built-in configs should be deeply frozen" do
20
- verify_deeply_frozen Sanitize::Config::DEFAULT
21
- verify_deeply_frozen Sanitize::Config::BASIC
22
- verify_deeply_frozen Sanitize::Config::RELAXED
23
- verify_deeply_frozen Sanitize::Config::RESTRICTED
24
- end
25
-
26
- describe ".freeze_config" do
27
- it "should deeply freeze and return a configuration Hash" do
28
- a = {one: {one_one: [0, "1", :a], one_two: false, one_three: Set.new([:a, :b, :c])}}
29
- b = Sanitize::Config.freeze_config(a)
30
-
31
- _(b).must_be_same_as a
32
- verify_deeply_frozen a
33
- end
34
- end
35
-
36
- describe ".merge" do
37
- it "should deeply merge a configuration Hash" do
38
- # Freeze to ensure that we get an error if either Hash is modified.
39
- a = Sanitize::Config.freeze_config({one: {one_one: [0, "1", :a], one_two: false, one_three: Set.new([:a, :b, :c])}})
40
- b = Sanitize::Config.freeze_config({one: {one_two: true, one_three: 3}, two: 2})
41
-
42
- c = Sanitize::Config.merge(a, b)
43
-
44
- _(c).wont_be_same_as a
45
- _(c).wont_be_same_as b
46
-
47
- _(c).must_equal(
48
- one: {
49
- one_one: [0, "1", :a],
50
- one_two: true,
51
- one_three: 3
52
- },
53
-
54
- two: 2
55
- )
56
-
57
- _(c[:one]).wont_be_same_as a[:one]
58
- _(c[:one][:one_one]).wont_be_same_as a[:one][:one_one]
59
- end
60
-
61
- it "should raise an ArgumentError if either argument is not a Hash" do
62
- _(proc { Sanitize::Config.merge("foo", {}) }).must_raise ArgumentError
63
- _(proc { Sanitize::Config.merge({}, "foo") }).must_raise ArgumentError
64
- end
65
- end
66
- end
@@ -1,56 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "common"
4
-
5
- # Miscellaneous attempts to sneak maliciously crafted CSS past Sanitize. Some of
6
- # these are courtesy of (or inspired by) the OWASP XSS Filter Evasion Cheat
7
- # Sheet.
8
- #
9
- # https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
10
-
11
- describe "Malicious CSS" do
12
- make_my_diffs_pretty!
13
- parallelize_me!
14
-
15
- before do
16
- @s = Sanitize::CSS.new(Sanitize::Config::RELAXED)
17
- end
18
-
19
- it "should not be possible to inject an expression by munging it with a comment" do
20
- _(@s.properties(%[width:expr/*XSS*/ession(alert('XSS'))]))
21
- .must_equal ""
22
-
23
- _(@s.properties(%[width:ex/*XSS*//*/*/pression(alert("XSS"))]))
24
- .must_equal ""
25
- end
26
-
27
- it "should not be possible to inject an expression by munging it with a newline" do
28
- _(@s.properties(%[width:\nexpression(alert('XSS'));]))
29
- .must_equal ""
30
- end
31
-
32
- it "should not allow the javascript protocol" do
33
- _(@s.properties(%[background-image:url("javascript:alert('XSS')");]))
34
- .must_equal ""
35
-
36
- _(Sanitize.fragment(%[<div style="background-image: url(&#1;javascript:alert('XSS'))">],
37
- Sanitize::Config::RELAXED)).must_equal "<div></div>"
38
- end
39
-
40
- it "should not allow behaviors" do
41
- _(@s.properties(%[behavior: url(xss.htc);])).must_equal ""
42
- end
43
-
44
- describe "sanitization bypass via CSS at-rule in HTML <style> element" do
45
- before do
46
- @s = Sanitize.new(Sanitize::Config::RELAXED)
47
- end
48
-
49
- it "is not possible to prematurely end a <style> element" do
50
- assert_equal(
51
- %[<style>@media<\\/style><iframe srcdoc='<script>alert(document.domain)<\\/script>'>{}</style>],
52
- @s.fragment(%[<style>@media</sty/**/le><iframe srcdoc='<script>alert(document.domain)</script>'></style>])
53
- )
54
- end
55
- end
56
- end