sanitize 4.6.5 → 6.0.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sanitize might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/HISTORY.md +235 -16
- data/LICENSE +1 -1
- data/README.md +89 -76
- data/lib/sanitize/config/default.rb +15 -4
- data/lib/sanitize/config/relaxed.rb +1 -1
- data/lib/sanitize/css.rb +2 -2
- data/lib/sanitize/transformers/clean_comment.rb +1 -1
- data/lib/sanitize/transformers/clean_css.rb +3 -3
- data/lib/sanitize/transformers/clean_doctype.rb +1 -1
- data/lib/sanitize/transformers/clean_element.rb +105 -22
- data/lib/sanitize/version.rb +1 -1
- data/lib/sanitize.rb +53 -68
- data/test/common.rb +0 -31
- data/test/test_clean_comment.rb +16 -20
- data/test/test_clean_css.rb +6 -6
- data/test/test_clean_doctype.rb +22 -22
- data/test/test_clean_element.rb +200 -82
- data/test/test_config.rb +9 -9
- data/test/test_malicious_css.rb +7 -7
- data/test/test_malicious_html.rb +179 -32
- data/test/test_parser.rb +9 -38
- data/test/test_sanitize.rb +114 -29
- data/test/test_sanitize_css.rb +88 -61
- data/test/test_transformers.rb +52 -46
- metadata +17 -33
- data/test/test_unicode.rb +0 -95
data/test/test_clean_element.rb
CHANGED
@@ -8,25 +8,22 @@ describe 'Sanitize::Transformers::CleanElement' do
|
|
8
8
|
strings = {
|
9
9
|
:basic => {
|
10
10
|
: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>',
|
11
|
-
|
12
|
-
:
|
13
|
-
:
|
14
|
-
:
|
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> alert("hello world");'
|
11
|
+
:default => 'Lorem ipsum dolor sit amet ',
|
12
|
+
:restricted => '<b>Lorem</b> ipsum <strong>dolor</strong> sit amet ',
|
13
|
+
: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 ',
|
14
|
+
: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
15
|
},
|
17
16
|
|
18
17
|
:malformed => {
|
19
18
|
: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
|
-
|
21
|
-
:
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:relaxed => 'Lorem <a href="pants" title="foo>ipsum <a href="><strong>dolor</strong></a> sit<br>amet alert("hello world");',
|
19
|
+
:default => 'Lorem dolor sit amet ',
|
20
|
+
:restricted => 'Lorem <strong>dolor</strong> sit amet ',
|
21
|
+
:basic => 'Lorem <a href="pants" rel="nofollow"><strong>dolor</strong></a> sit<br>amet ',
|
22
|
+
:relaxed => 'Lorem <a href="pants" title="foo>ipsum <a href="><strong>dolor</strong></a> sit<br>amet ',
|
25
23
|
},
|
26
24
|
|
27
25
|
:unclosed => {
|
28
26
|
:html => '<p>a</p><blockquote>b',
|
29
|
-
|
30
27
|
:default => ' a b ',
|
31
28
|
:restricted => ' a b ',
|
32
29
|
:basic => '<p>a</p><blockquote>b</blockquote>',
|
@@ -35,7 +32,6 @@ describe 'Sanitize::Transformers::CleanElement' do
|
|
35
32
|
|
36
33
|
:malicious => {
|
37
34
|
: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>',
|
38
|
-
|
39
35
|
:default => 'Lorem ipsum dolor sit amet <script>alert("hello world");',
|
40
36
|
:restricted => '<b>Lorem</b> ipsum <strong>dolor</strong> sit amet <script>alert("hello world");',
|
41
37
|
:basic => '<b>Lorem</b> <a rel="nofollow">ipsum</a> <a href="http://foo.com/" rel="nofollow"><strong>dolor</strong></a> sit<br>amet <script>alert("hello world");',
|
@@ -166,45 +162,95 @@ describe 'Sanitize::Transformers::CleanElement' do
|
|
166
162
|
}
|
167
163
|
|
168
164
|
describe 'Default config' do
|
169
|
-
it 'should remove non-
|
170
|
-
Sanitize.fragment('foo <b>bar</b> <strong><a href="#a">baz</a></strong> quux')
|
165
|
+
it 'should remove non-allowlisted elements, leaving safe contents behind' do
|
166
|
+
_(Sanitize.fragment('foo <b>bar</b> <strong><a href="#a">baz</a></strong> quux'))
|
171
167
|
.must_equal 'foo bar baz quux'
|
172
168
|
|
173
|
-
Sanitize.fragment('<script>alert("<xss>");</script>')
|
174
|
-
.must_equal '
|
169
|
+
_(Sanitize.fragment('<script>alert("<xss>");</script>'))
|
170
|
+
.must_equal ''
|
175
171
|
|
176
|
-
Sanitize.fragment('<<script>script>alert("<xss>");</<script>>')
|
177
|
-
.must_equal '<
|
172
|
+
_(Sanitize.fragment('<<script>script>alert("<xss>");</<script>>'))
|
173
|
+
.must_equal '<'
|
178
174
|
|
179
|
-
Sanitize.fragment('< script <>> alert("<xss>");</script>')
|
175
|
+
_(Sanitize.fragment('< script <>> alert("<xss>");</script>'))
|
180
176
|
.must_equal '< script <>> alert("");'
|
181
177
|
end
|
182
178
|
|
183
179
|
it 'should surround the contents of :whitespace_elements with space characters when removing the element' do
|
184
|
-
Sanitize.fragment('foo<div>bar</div>baz')
|
180
|
+
_(Sanitize.fragment('foo<div>bar</div>baz'))
|
185
181
|
.must_equal 'foo bar baz'
|
186
182
|
|
187
|
-
Sanitize.fragment('foo<br>bar<br>baz')
|
183
|
+
_(Sanitize.fragment('foo<br>bar<br>baz'))
|
188
184
|
.must_equal 'foo bar baz'
|
189
185
|
|
190
|
-
Sanitize.fragment('foo<hr>bar<hr>baz')
|
186
|
+
_(Sanitize.fragment('foo<hr>bar<hr>baz'))
|
191
187
|
.must_equal 'foo bar baz'
|
192
188
|
end
|
193
189
|
|
194
190
|
it 'should not choke on several instances of the same element in a row' do
|
195
|
-
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">')
|
191
|
+
_(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">'))
|
192
|
+
.must_equal ''
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'should not preserve the content of removed `iframe` elements' do
|
196
|
+
_(Sanitize.fragment('<iframe>hello! <script>alert(0)</script></iframe>'))
|
197
|
+
.must_equal ''
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'should not preserve the content of removed `math` elements' do
|
201
|
+
_(Sanitize.fragment('<math>hello! <script>alert(0)</script></math>'))
|
202
|
+
.must_equal ''
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'should not preserve the content of removed `noembed` elements' do
|
206
|
+
_(Sanitize.fragment('<noembed>hello! <script>alert(0)</script></noembed>'))
|
207
|
+
.must_equal ''
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'should not preserve the content of removed `noframes` elements' do
|
211
|
+
_(Sanitize.fragment('<noframes>hello! <script>alert(0)</script></noframes>'))
|
212
|
+
.must_equal ''
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'should not preserve the content of removed `noscript` elements' do
|
216
|
+
_(Sanitize.fragment('<noscript>hello! <script>alert(0)</script></noscript>'))
|
217
|
+
.must_equal ''
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'should not preserve the content of removed `plaintext` elements' do
|
221
|
+
_(Sanitize.fragment('<plaintext>hello! <script>alert(0)</script>'))
|
222
|
+
.must_equal ''
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'should not preserve the content of removed `script` elements' do
|
226
|
+
_(Sanitize.fragment('<script>hello! <script>alert(0)</script></script>'))
|
227
|
+
.must_equal ''
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'should not preserve the content of removed `style` elements' do
|
231
|
+
_(Sanitize.fragment('<style>hello! <script>alert(0)</script></style>'))
|
232
|
+
.must_equal ''
|
233
|
+
end
|
234
|
+
|
235
|
+
it 'should not preserve the content of removed `svg` elements' do
|
236
|
+
_(Sanitize.fragment('<svg>hello! <script>alert(0)</script></svg>'))
|
237
|
+
.must_equal ''
|
238
|
+
end
|
239
|
+
|
240
|
+
it 'should not preserve the content of removed `xmp` elements' do
|
241
|
+
_(Sanitize.fragment('<xmp>hello! <script>alert(0)</script></xmp>'))
|
196
242
|
.must_equal ''
|
197
243
|
end
|
198
244
|
|
199
245
|
strings.each do |name, data|
|
200
246
|
it "should clean #{name} HTML" do
|
201
|
-
Sanitize.fragment(data[:html]).must_equal(data[:default])
|
247
|
+
_(Sanitize.fragment(data[:html])).must_equal(data[:default])
|
202
248
|
end
|
203
249
|
end
|
204
250
|
|
205
251
|
protocols.each do |name, data|
|
206
252
|
it "should not allow #{name}" do
|
207
|
-
Sanitize.fragment(data[:html]).must_equal(data[:default])
|
253
|
+
_(Sanitize.fragment(data[:html])).must_equal(data[:default])
|
208
254
|
end
|
209
255
|
end
|
210
256
|
end
|
@@ -216,13 +262,13 @@ describe 'Sanitize::Transformers::CleanElement' do
|
|
216
262
|
|
217
263
|
strings.each do |name, data|
|
218
264
|
it "should clean #{name} HTML" do
|
219
|
-
@s.fragment(data[:html]).must_equal(data[:restricted])
|
265
|
+
_(@s.fragment(data[:html])).must_equal(data[:restricted])
|
220
266
|
end
|
221
267
|
end
|
222
268
|
|
223
269
|
protocols.each do |name, data|
|
224
270
|
it "should not allow #{name}" do
|
225
|
-
@s.fragment(data[:html]).must_equal(data[:restricted])
|
271
|
+
_(@s.fragment(data[:html])).must_equal(data[:restricted])
|
226
272
|
end
|
227
273
|
end
|
228
274
|
end
|
@@ -233,24 +279,24 @@ describe 'Sanitize::Transformers::CleanElement' do
|
|
233
279
|
end
|
234
280
|
|
235
281
|
it 'should not choke on valueless attributes' do
|
236
|
-
@s.fragment('foo <a href>foo</a> bar')
|
237
|
-
.must_equal 'foo <a href rel="nofollow">foo</a> bar'
|
282
|
+
_(@s.fragment('foo <a href>foo</a> bar'))
|
283
|
+
.must_equal 'foo <a href="" rel="nofollow">foo</a> bar'
|
238
284
|
end
|
239
285
|
|
240
286
|
it 'should downcase attribute names' do
|
241
|
-
@s.fragment('<a HREF="javascript:alert(\'foo\')">bar</a>')
|
287
|
+
_(@s.fragment('<a HREF="javascript:alert(\'foo\')">bar</a>'))
|
242
288
|
.must_equal '<a rel="nofollow">bar</a>'
|
243
289
|
end
|
244
290
|
|
245
291
|
strings.each do |name, data|
|
246
292
|
it "should clean #{name} HTML" do
|
247
|
-
@s.fragment(data[:html]).must_equal(data[:basic])
|
293
|
+
_(@s.fragment(data[:html])).must_equal(data[:basic])
|
248
294
|
end
|
249
295
|
end
|
250
296
|
|
251
297
|
protocols.each do |name, data|
|
252
298
|
it "should not allow #{name}" do
|
253
|
-
@s.fragment(data[:html]).must_equal(data[:basic])
|
299
|
+
_(@s.fragment(data[:html])).must_equal(data[:basic])
|
254
300
|
end
|
255
301
|
end
|
256
302
|
end
|
@@ -261,110 +307,124 @@ describe 'Sanitize::Transformers::CleanElement' do
|
|
261
307
|
end
|
262
308
|
|
263
309
|
it 'should encode special chars in attribute values' do
|
264
|
-
@s.fragment('<a href="http://example.com" title="<b>éxamples</b> & things">foo</a>')
|
265
|
-
.must_equal '<a href="http://example.com" title="
|
310
|
+
_(@s.fragment('<a href="http://example.com" title="<b>éxamples</b> & things">foo</a>'))
|
311
|
+
.must_equal '<a href="http://example.com" title="<b>éxamples</b> & things">foo</a>'
|
266
312
|
end
|
267
313
|
|
268
314
|
strings.each do |name, data|
|
269
315
|
it "should clean #{name} HTML" do
|
270
|
-
@s.fragment(data[:html]).must_equal(data[:relaxed])
|
316
|
+
_(@s.fragment(data[:html])).must_equal(data[:relaxed])
|
271
317
|
end
|
272
318
|
end
|
273
319
|
|
274
320
|
protocols.each do |name, data|
|
275
321
|
it "should not allow #{name}" do
|
276
|
-
@s.fragment(data[:html]).must_equal(data[:relaxed])
|
322
|
+
_(@s.fragment(data[:html])).must_equal(data[:relaxed])
|
277
323
|
end
|
278
324
|
end
|
279
325
|
end
|
280
326
|
|
281
327
|
describe 'Custom configs' do
|
282
|
-
it 'should allow attributes on all elements if
|
328
|
+
it 'should allow attributes on all elements if allowlisted under :all' do
|
283
329
|
input = '<p class="foo">bar</p>'
|
284
330
|
|
285
|
-
Sanitize.fragment(input).must_equal ' bar '
|
331
|
+
_(Sanitize.fragment(input)).must_equal ' bar '
|
286
332
|
|
287
|
-
Sanitize.fragment(input, {
|
333
|
+
_(Sanitize.fragment(input, {
|
288
334
|
:elements => ['p'],
|
289
335
|
:attributes => {:all => ['class']}
|
290
|
-
}).must_equal input
|
336
|
+
})).must_equal input
|
291
337
|
|
292
|
-
Sanitize.fragment(input, {
|
338
|
+
_(Sanitize.fragment(input, {
|
293
339
|
:elements => ['p'],
|
294
340
|
:attributes => {'div' => ['class']}
|
295
|
-
}).must_equal '<p>bar</p>'
|
341
|
+
})).must_equal '<p>bar</p>'
|
296
342
|
|
297
|
-
Sanitize.fragment(input, {
|
343
|
+
_(Sanitize.fragment(input, {
|
298
344
|
:elements => ['p'],
|
299
345
|
:attributes => {'p' => ['title'], :all => ['class']}
|
300
|
-
}).must_equal input
|
346
|
+
})).must_equal input
|
301
347
|
end
|
302
348
|
|
303
|
-
it "should not allow relative URLs when relative URLs aren't
|
349
|
+
it "should not allow relative URLs when relative URLs aren't allowlisted" do
|
304
350
|
input = '<a href="/foo/bar">Link</a>'
|
305
351
|
|
306
|
-
Sanitize.fragment(input,
|
352
|
+
_(Sanitize.fragment(input,
|
307
353
|
:elements => ['a'],
|
308
354
|
:attributes => {'a' => ['href']},
|
309
355
|
:protocols => {'a' => {'href' => ['http']}}
|
310
|
-
).must_equal '<a>Link</a>'
|
356
|
+
)).must_equal '<a>Link</a>'
|
311
357
|
end
|
312
358
|
|
313
359
|
it 'should allow relative URLs containing colons when the colon is not in the first path segment' do
|
314
360
|
input = '<a href="/wiki/Special:Random">Random Page</a>'
|
315
361
|
|
316
|
-
Sanitize.fragment(input, {
|
362
|
+
_(Sanitize.fragment(input, {
|
317
363
|
:elements => ['a'],
|
318
364
|
:attributes => {'a' => ['href']},
|
319
365
|
:protocols => {'a' => {'href' => [:relative]}}
|
320
|
-
}).must_equal input
|
366
|
+
})).must_equal input
|
321
367
|
end
|
322
368
|
|
323
369
|
it 'should allow relative URLs containing colons when the colon is part of an anchor' do
|
324
370
|
input = '<a href="#fn:1">Footnote 1</a>'
|
325
371
|
|
326
|
-
Sanitize.fragment(input, {
|
372
|
+
_(Sanitize.fragment(input, {
|
327
373
|
:elements => ['a'],
|
328
374
|
:attributes => {'a' => ['href']},
|
329
375
|
:protocols => {'a' => {'href' => [:relative]}}
|
330
|
-
}).must_equal input
|
376
|
+
})).must_equal input
|
331
377
|
|
332
378
|
input = '<a href="somepage#fn:1">Footnote 1</a>'
|
333
379
|
|
334
|
-
Sanitize.fragment(input, {
|
380
|
+
_(Sanitize.fragment(input, {
|
335
381
|
:elements => ['a'],
|
336
382
|
:attributes => {'a' => ['href']},
|
337
383
|
:protocols => {'a' => {'href' => [:relative]}}
|
338
|
-
}).must_equal input
|
384
|
+
})).must_equal input
|
339
385
|
end
|
340
386
|
|
341
387
|
it 'should remove the contents of filtered nodes when :remove_contents is true' do
|
342
|
-
Sanitize.fragment('foo bar <div>baz<span>quux</span></div>',
|
388
|
+
_(Sanitize.fragment('foo bar <div>baz<span>quux</span></div>',
|
343
389
|
:remove_contents => true
|
344
|
-
).must_equal 'foo bar '
|
390
|
+
)).must_equal 'foo bar '
|
345
391
|
end
|
346
392
|
|
347
|
-
it 'should remove the contents of specified nodes when :remove_contents is an Array of element names as strings' do
|
348
|
-
Sanitize.fragment('foo bar <div>baz<span>quux</span><script>alert("hello!");</script></div>',
|
393
|
+
it 'should remove the contents of specified nodes when :remove_contents is an Array or Set of element names as strings' do
|
394
|
+
_(Sanitize.fragment('foo bar <div>baz<span>quux</span> <b>hi</b><script>alert("hello!");</script></div>',
|
349
395
|
:remove_contents => ['script', 'span']
|
350
|
-
).must_equal 'foo bar baz '
|
396
|
+
)).must_equal 'foo bar baz hi '
|
397
|
+
|
398
|
+
_(Sanitize.fragment('foo bar <div>baz<span>quux</span> <b>hi</b><script>alert("hello!");</script></div>',
|
399
|
+
:remove_contents => Set.new(['script', 'span'])
|
400
|
+
)).must_equal 'foo bar baz hi '
|
351
401
|
end
|
352
402
|
|
353
|
-
it 'should remove the contents of specified nodes when :remove_contents is an Array of element names as symbols' do
|
354
|
-
Sanitize.fragment('foo bar <div>baz<span>quux</span><script>alert("hello!");</script></div>',
|
403
|
+
it 'should remove the contents of specified nodes when :remove_contents is an Array or Set of element names as symbols' do
|
404
|
+
_(Sanitize.fragment('foo bar <div>baz<span>quux</span> <b>hi</b><script>alert("hello!");</script></div>',
|
355
405
|
:remove_contents => [:script, :span]
|
356
|
-
).must_equal 'foo bar baz '
|
406
|
+
)).must_equal 'foo bar baz hi '
|
407
|
+
|
408
|
+
_(Sanitize.fragment('foo bar <div>baz<span>quux</span> <b>hi</b><script>alert("hello!");</script></div>',
|
409
|
+
:remove_contents => Set.new([:script, :span])
|
410
|
+
)).must_equal 'foo bar baz hi '
|
411
|
+
end
|
412
|
+
|
413
|
+
it 'should remove the contents of allowlisted iframes' do
|
414
|
+
_(Sanitize.fragment('<iframe>hi <script>hello</script></iframe>',
|
415
|
+
:elements => ['iframe']
|
416
|
+
)).must_equal '<iframe></iframe>'
|
357
417
|
end
|
358
418
|
|
359
419
|
it 'should not allow arbitrary HTML5 data attributes by default' do
|
360
|
-
Sanitize.fragment('<b data-foo="bar"></b>',
|
420
|
+
_(Sanitize.fragment('<b data-foo="bar"></b>',
|
361
421
|
:elements => ['b']
|
362
|
-
).must_equal '<b></b>'
|
422
|
+
)).must_equal '<b></b>'
|
363
423
|
|
364
|
-
Sanitize.fragment('<b class="foo" data-foo="bar"></b>',
|
424
|
+
_(Sanitize.fragment('<b class="foo" data-foo="bar"></b>',
|
365
425
|
:attributes => {'b' => ['class']},
|
366
426
|
:elements => ['b']
|
367
|
-
).must_equal '<b class="foo"></b>'
|
427
|
+
)).must_equal '<b class="foo"></b>'
|
368
428
|
end
|
369
429
|
|
370
430
|
it 'should allow arbitrary HTML5 data attributes when the :attributes config includes :data' do
|
@@ -373,28 +433,28 @@ describe 'Sanitize::Transformers::CleanElement' do
|
|
373
433
|
:elements => ['b']
|
374
434
|
)
|
375
435
|
|
376
|
-
s.fragment('<b data-foo="valid" data-bar="valid"></b>')
|
436
|
+
_(s.fragment('<b data-foo="valid" data-bar="valid"></b>'))
|
377
437
|
.must_equal '<b data-foo="valid" data-bar="valid"></b>'
|
378
438
|
|
379
|
-
s.fragment('<b data-="invalid"></b>')
|
439
|
+
_(s.fragment('<b data-="invalid"></b>'))
|
380
440
|
.must_equal '<b></b>'
|
381
441
|
|
382
|
-
s.fragment('<b data-="invalid"></b>')
|
442
|
+
_(s.fragment('<b data-="invalid"></b>'))
|
383
443
|
.must_equal '<b></b>'
|
384
444
|
|
385
|
-
s.fragment('<b data-xml="invalid"></b>')
|
445
|
+
_(s.fragment('<b data-xml="invalid"></b>'))
|
386
446
|
.must_equal '<b></b>'
|
387
447
|
|
388
|
-
s.fragment('<b data-xmlfoo="invalid"></b>')
|
448
|
+
_(s.fragment('<b data-xmlfoo="invalid"></b>'))
|
389
449
|
.must_equal '<b></b>'
|
390
450
|
|
391
|
-
s.fragment('<b data-f:oo="valid"></b>')
|
451
|
+
_(s.fragment('<b data-f:oo="valid"></b>'))
|
392
452
|
.must_equal '<b></b>'
|
393
453
|
|
394
|
-
s.fragment('<b data-f/oo="partial"></b>')
|
454
|
+
_(s.fragment('<b data-f/oo="partial"></b>'))
|
395
455
|
.must_equal '<b data-f=""></b>' # Nokogiri quirk; not ideal, but harmless
|
396
456
|
|
397
|
-
s.fragment('<b data-éfoo="valid"></b>')
|
457
|
+
_(s.fragment('<b data-éfoo="valid"></b>'))
|
398
458
|
.must_equal '<b></b>' # Another annoying Nokogiri quirk.
|
399
459
|
end
|
400
460
|
|
@@ -407,28 +467,86 @@ describe 'Sanitize::Transformers::CleanElement' do
|
|
407
467
|
}
|
408
468
|
)
|
409
469
|
|
410
|
-
s.fragment('<p>foo</p>').must_equal "\nfoo\n"
|
411
|
-
s.fragment('<p>foo</p><p>bar</p>').must_equal "\nfoo\n\nbar\n"
|
412
|
-
s.fragment('foo<div>bar</div>baz').must_equal "foo\nbar\nbaz"
|
413
|
-
s.fragment('foo<br>bar<br>baz').must_equal "foo\nbar\nbaz"
|
470
|
+
_(s.fragment('<p>foo</p>')).must_equal "\nfoo\n"
|
471
|
+
_(s.fragment('<p>foo</p><p>bar</p>')).must_equal "\nfoo\n\nbar\n"
|
472
|
+
_(s.fragment('foo<div>bar</div>baz')).must_equal "foo\nbar\nbaz"
|
473
|
+
_(s.fragment('foo<br>bar<br>baz')).must_equal "foo\nbar\nbaz"
|
414
474
|
end
|
415
475
|
|
416
|
-
it '
|
476
|
+
it 'should handle protocols correctly regardless of case' do
|
417
477
|
input = '<a href="hTTpS://foo.com/">Text</a>'
|
418
478
|
|
419
|
-
Sanitize.fragment(input, {
|
479
|
+
_(Sanitize.fragment(input, {
|
420
480
|
:elements => ['a'],
|
421
481
|
:attributes => {'a' => ['href']},
|
422
482
|
:protocols => {'a' => {'href' => ['https']}}
|
423
|
-
}).must_equal input
|
483
|
+
})).must_equal input
|
424
484
|
|
425
485
|
input = '<a href="mailto:someone@example.com?Subject=Hello">Text</a>'
|
426
486
|
|
427
|
-
Sanitize.fragment(input, {
|
487
|
+
_(Sanitize.fragment(input, {
|
428
488
|
:elements => ['a'],
|
429
489
|
:attributes => {'a' => ['href']},
|
430
490
|
:protocols => {'a' => {'href' => ['https']}}
|
431
|
-
}).must_equal "<a>Text</a>"
|
491
|
+
})).must_equal "<a>Text</a>"
|
432
492
|
end
|
493
|
+
|
494
|
+
it 'should sanitize protocols in data attributes even if data attributes are generically allowed' do
|
495
|
+
input = '<a data-url="mailto:someone@example.com">Text</a>'
|
496
|
+
|
497
|
+
_(Sanitize.fragment(input, {
|
498
|
+
:elements => ['a'],
|
499
|
+
:attributes => {'a' => [:data]},
|
500
|
+
:protocols => {'a' => {'data-url' => ['https']}}
|
501
|
+
})).must_equal "<a>Text</a>"
|
502
|
+
|
503
|
+
_(Sanitize.fragment(input, {
|
504
|
+
:elements => ['a'],
|
505
|
+
:attributes => {'a' => [:data]},
|
506
|
+
:protocols => {'a' => {'data-url' => ['mailto']}}
|
507
|
+
})).must_equal input
|
508
|
+
end
|
509
|
+
|
510
|
+
it 'should prevent `<meta>` tags from being used to set a non-UTF-8 charset' do
|
511
|
+
_(Sanitize.document('<html><head><meta charset="utf-8"></head><body>Howdy!</body></html>',
|
512
|
+
:elements => %w[html head meta body],
|
513
|
+
:attributes => {'meta' => ['charset']}
|
514
|
+
)).must_equal "<html><head><meta charset=\"utf-8\"></head><body>Howdy!</body></html>"
|
515
|
+
|
516
|
+
_(Sanitize.document('<html><meta charset="utf-8">Howdy!</html>',
|
517
|
+
:elements => %w[html meta],
|
518
|
+
:attributes => {'meta' => ['charset']}
|
519
|
+
)).must_equal "<html><meta charset=\"utf-8\">Howdy!</html>"
|
520
|
+
|
521
|
+
_(Sanitize.document('<html><meta charset="us-ascii">Howdy!</html>',
|
522
|
+
:elements => %w[html meta],
|
523
|
+
:attributes => {'meta' => ['charset']}
|
524
|
+
)).must_equal "<html><meta charset=\"utf-8\">Howdy!</html>"
|
525
|
+
|
526
|
+
_(Sanitize.document('<html><meta http-equiv="content-type" content=" text/html; charset=us-ascii">Howdy!</html>',
|
527
|
+
:elements => %w[html meta],
|
528
|
+
:attributes => {'meta' => %w[content http-equiv]}
|
529
|
+
)).must_equal "<html><meta http-equiv=\"content-type\" content=\" text/html;charset=utf-8\">Howdy!</html>"
|
530
|
+
|
531
|
+
_(Sanitize.document('<html><meta http-equiv="Content-Type" content="text/plain;charset = us-ascii">Howdy!</html>',
|
532
|
+
:elements => %w[html meta],
|
533
|
+
:attributes => {'meta' => %w[content http-equiv]}
|
534
|
+
)).must_equal "<html><meta http-equiv=\"Content-Type\" content=\"text/plain;charset=utf-8\">Howdy!</html>"
|
535
|
+
end
|
536
|
+
|
537
|
+
it 'should not modify `<meta>` tags that already set a UTF-8 charset' do
|
538
|
+
_(Sanitize.document('<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"></head><body>Howdy!</body></html>',
|
539
|
+
:elements => %w[html head meta body],
|
540
|
+
:attributes => {'meta' => %w[content http-equiv]}
|
541
|
+
)).must_equal "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"></head><body>Howdy!</body></html>"
|
542
|
+
end
|
543
|
+
|
544
|
+
it 'always removes `<noscript>` elements even if `noscript` is in the allowlist' do
|
545
|
+
assert_equal(
|
546
|
+
'',
|
547
|
+
Sanitize.fragment('<noscript>foo</noscript>', elements: ['noscript'])
|
548
|
+
)
|
549
|
+
end
|
550
|
+
|
433
551
|
end
|
434
552
|
end
|
data/test/test_config.rb
CHANGED
@@ -6,7 +6,7 @@ describe 'Config' do
|
|
6
6
|
parallelize_me!
|
7
7
|
|
8
8
|
def verify_deeply_frozen(config)
|
9
|
-
config.must_be :frozen?
|
9
|
+
_(config).must_be :frozen?
|
10
10
|
|
11
11
|
if Hash === config
|
12
12
|
config.each_value {|v| verify_deeply_frozen(v) }
|
@@ -27,7 +27,7 @@ describe 'Config' do
|
|
27
27
|
a = {:one => {:one_one => [0, '1', :a], :one_two => false, :one_three => Set.new([:a, :b, :c])}}
|
28
28
|
b = Sanitize::Config.freeze_config(a)
|
29
29
|
|
30
|
-
b.must_be_same_as a
|
30
|
+
_(b).must_be_same_as a
|
31
31
|
verify_deeply_frozen a
|
32
32
|
end
|
33
33
|
end
|
@@ -40,10 +40,10 @@ describe 'Config' do
|
|
40
40
|
|
41
41
|
c = Sanitize::Config.merge(a, b)
|
42
42
|
|
43
|
-
c.wont_be_same_as a
|
44
|
-
c.wont_be_same_as b
|
43
|
+
_(c).wont_be_same_as a
|
44
|
+
_(c).wont_be_same_as b
|
45
45
|
|
46
|
-
c.must_equal(
|
46
|
+
_(c).must_equal(
|
47
47
|
:one => {
|
48
48
|
:one_one => [0, '1', :a],
|
49
49
|
:one_two => true,
|
@@ -53,13 +53,13 @@ describe 'Config' do
|
|
53
53
|
:two => 2
|
54
54
|
)
|
55
55
|
|
56
|
-
c[:one].wont_be_same_as a[:one]
|
57
|
-
c[:one][:one_one].wont_be_same_as a[:one][:one_one]
|
56
|
+
_(c[:one]).wont_be_same_as a[:one]
|
57
|
+
_(c[:one][:one_one]).wont_be_same_as a[:one][:one_one]
|
58
58
|
end
|
59
59
|
|
60
60
|
it 'should raise an ArgumentError if either argument is not a Hash' do
|
61
|
-
proc { Sanitize::Config.merge('foo', {}) }.must_raise ArgumentError
|
62
|
-
proc { Sanitize::Config.merge({}, 'foo') }.must_raise ArgumentError
|
61
|
+
_(proc { Sanitize::Config.merge('foo', {}) }).must_raise ArgumentError
|
62
|
+
_(proc { Sanitize::Config.merge({}, 'foo') }).must_raise ArgumentError
|
63
63
|
end
|
64
64
|
end
|
65
65
|
end
|
data/test/test_malicious_css.rb
CHANGED
@@ -16,27 +16,27 @@ describe 'Malicious CSS' do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'should not be possible to inject an expression by munging it with a comment' do
|
19
|
-
@s.properties(%[width:expr/*XSS*/ession(alert('XSS'))]).
|
19
|
+
_(@s.properties(%[width:expr/*XSS*/ession(alert('XSS'))])).
|
20
20
|
must_equal ''
|
21
21
|
|
22
|
-
@s.properties(%[width:ex/*XSS*//*/*/pression(alert("XSS"))]).
|
22
|
+
_(@s.properties(%[width:ex/*XSS*//*/*/pression(alert("XSS"))])).
|
23
23
|
must_equal ''
|
24
24
|
end
|
25
25
|
|
26
26
|
it 'should not be possible to inject an expression by munging it with a newline' do
|
27
|
-
@s.properties(%[width:\nexpression(alert('XSS'));]).
|
27
|
+
_(@s.properties(%[width:\nexpression(alert('XSS'));])).
|
28
28
|
must_equal ''
|
29
29
|
end
|
30
30
|
|
31
31
|
it 'should not allow the javascript protocol' do
|
32
|
-
@s.properties(%[background-image:url("javascript:alert('XSS')");]).
|
32
|
+
_(@s.properties(%[background-image:url("javascript:alert('XSS')");])).
|
33
33
|
must_equal ''
|
34
34
|
|
35
|
-
Sanitize.fragment(%[<div style="background-image: url(javascript:alert('XSS'))">],
|
36
|
-
Sanitize::Config::RELAXED).must_equal '<div></div>'
|
35
|
+
_(Sanitize.fragment(%[<div style="background-image: url(javascript:alert('XSS'))">],
|
36
|
+
Sanitize::Config::RELAXED)).must_equal '<div></div>'
|
37
37
|
end
|
38
38
|
|
39
39
|
it 'should not allow behaviors' do
|
40
|
-
@s.properties(%[behavior: url(xss.htc);]).must_equal ''
|
40
|
+
_(@s.properties(%[behavior: url(xss.htc);])).must_equal ''
|
41
41
|
end
|
42
42
|
end
|