sanitize 6.1.3 → 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,433 +0,0 @@
1
- # encoding: utf-8
2
- require_relative 'common'
3
-
4
- describe 'Sanitize::CSS' do
5
- make_my_diffs_pretty!
6
- parallelize_me!
7
-
8
- describe 'instance methods' do
9
- before do
10
- @default = Sanitize::CSS.new
11
- @relaxed = Sanitize::CSS.new(Sanitize::Config::RELAXED[:css])
12
- @custom = Sanitize::CSS.new(:properties => %w[background color width])
13
- end
14
-
15
- describe '#properties' do
16
- it 'should sanitize CSS properties' do
17
- css = 'background: #fff; width: expression(alert("hi"));'
18
-
19
- _(@default.properties(css)).must_equal ' '
20
- _(@relaxed.properties(css)).must_equal 'background: #fff; '
21
- _(@custom.properties(css)).must_equal 'background: #fff; '
22
- end
23
-
24
- it 'should allow allowlisted URL protocols' do
25
- [
26
- "background: url(relative.jpg)",
27
- "background: url('relative.jpg')",
28
- "background: url(http://example.com/http.jpg)",
29
- "background: url('ht\\tp://example.com/http.jpg')",
30
- "background: url(https://example.com/https.jpg)",
31
- "background: url('https://example.com/https.jpg')",
32
- "background: image-set('relative.jpg' 1x, 'relative-2x.jpg' 2x)",
33
- "background: image-set('https://example.com/https.jpg' 1x, 'https://example.com/https-2x.jpg' 2x)",
34
- "background: image-set('https://example.com/https.jpg' type('image/jpeg'), 'https://example.com/https.avif' type('image/avif'))",
35
- "background: -webkit-image-set('relative.jpg' 1x, 'relative-2x.jpg' 2x)",
36
- "background: -webkit-image-set('https://example.com/https.jpg' 1x, 'https://example.com/https-2x.jpg' 2x)",
37
- "background: -webkit-image-set('https://example.com/https.jpg' type('image/jpeg'), 'https://example.com/https.avif' type('image/avif'))",
38
- "background: image('relative.jpg');",
39
- "background: image('https://example.com/https.jpg');",
40
- "background: image(rtl 'https://example.com/https.jpg');"
41
- ].each do |css|
42
- _(@default.properties(css)).must_equal ''
43
- _(@relaxed.properties(css)).must_equal css
44
- _(@custom.properties(css)).must_equal ''
45
- end
46
- end
47
-
48
- it 'should not allow non-allowlisted URL protocols' do
49
- [
50
- "background: url(javascript:alert(0))",
51
- "background: url(ja\\56 ascript:alert(0))",
52
- "background: url('javascript:foo')",
53
- "background: url('ja\\56 ascript:alert(0)')",
54
- "background: url('ja\\va\\script\\:alert(0)')",
55
- "background: url('javas\\\ncript:alert(0)')",
56
- "background: url('java\\0script:foo')"
57
- ].each do |css|
58
- _(@default.properties(css)).must_equal ''
59
- _(@relaxed.properties(css)).must_equal ''
60
- _(@custom.properties(css)).must_equal ''
61
- end
62
- end
63
-
64
- it 'should not allow -moz-binding' do
65
- css = "-moz-binding:url('http://ha.ckers.org/xssmoz.xml#xss')"
66
-
67
- _(@default.properties(css)).must_equal ''
68
- _(@relaxed.properties(css)).must_equal ''
69
- _(@custom.properties(css)).must_equal ''
70
- end
71
-
72
- it 'should not allow expressions' do
73
- [
74
- "width:expression(alert(1))",
75
- "width: /**/expression(alert(1)",
76
- "width:e\\78 pression(\n\nalert(\n1)",
77
- "width:\nexpression(alert(1));",
78
- "xss:expression(alert(1))",
79
- "height: foo(expression(alert(1)));"
80
- ].each do |css|
81
- _(@default.properties(css)).must_equal ''
82
- _(@relaxed.properties(css)).must_equal ''
83
- _(@custom.properties(css)).must_equal ''
84
- end
85
- end
86
-
87
- it 'should not allow behaviors' do
88
- css = "behavior: url(xss.htc);"
89
-
90
- _(@default.properties(css)).must_equal ''
91
- _(@relaxed.properties(css)).must_equal ''
92
- _(@custom.properties(css)).must_equal ''
93
- end
94
-
95
- describe 'when :allow_comments is true' do
96
- it 'should preserve comments' do
97
- _(@relaxed.properties('color: #fff; /* comment */ width: 100px;'))
98
- .must_equal 'color: #fff; /* comment */ width: 100px;'
99
-
100
- _(@relaxed.properties("color: #fff; /* \n\ncomment */ width: 100px;"))
101
- .must_equal "color: #fff; /* \n\ncomment */ width: 100px;"
102
- end
103
- end
104
-
105
- describe 'when :allow_comments is false' do
106
- it 'should strip comments' do
107
- _(@custom.properties('color: #fff; /* comment */ width: 100px;'))
108
- .must_equal 'color: #fff; width: 100px;'
109
-
110
- _(@custom.properties("color: #fff; /* \n\ncomment */ width: 100px;"))
111
- .must_equal 'color: #fff; width: 100px;'
112
- end
113
- end
114
-
115
- describe 'when :allow_hacks is true' do
116
- it 'should allow common CSS hacks' do
117
- _(@relaxed.properties('_border: 1px solid #fff; *width: 10px'))
118
- .must_equal '_border: 1px solid #fff; *width: 10px'
119
- end
120
- end
121
-
122
- describe 'when :allow_hacks is false' do
123
- it 'should not allow common CSS hacks' do
124
- _(@custom.properties('_border: 1px solid #fff; *width: 10px'))
125
- .must_equal ' '
126
- end
127
- end
128
- end
129
-
130
- describe '#stylesheet' do
131
- it 'should sanitize a CSS stylesheet' do
132
- css = %[
133
- /* Yay CSS! */
134
- .foo { color: #fff; }
135
- #bar { background: url(yay.jpg); }
136
-
137
- @media screen (max-width:480px) {
138
- .foo { width: 400px; }
139
- #bar:not(.baz) { height: 100px; }
140
- }
141
- ].strip
142
-
143
- _(@default.stylesheet(css).strip).must_equal %[
144
- .foo { }
145
- #bar { }
146
- ].strip
147
-
148
- _(@relaxed.stylesheet(css)).must_equal css
149
-
150
- _(@custom.stylesheet(css).strip).must_equal %[
151
- .foo { color: #fff; }
152
- #bar { }
153
- ].strip
154
- end
155
-
156
- describe 'when :allow_comments is true' do
157
- it 'should preserve comments' do
158
- _(@relaxed.stylesheet('.foo { color: #fff; /* comment */ width: 100px; }'))
159
- .must_equal '.foo { color: #fff; /* comment */ width: 100px; }'
160
-
161
- _(@relaxed.stylesheet(".foo { color: #fff; /* \n\ncomment */ width: 100px; }"))
162
- .must_equal ".foo { color: #fff; /* \n\ncomment */ width: 100px; }"
163
- end
164
- end
165
-
166
- describe 'when :allow_comments is false' do
167
- it 'should strip comments' do
168
- _(@custom.stylesheet('.foo { color: #fff; /* comment */ width: 100px; }'))
169
- .must_equal '.foo { color: #fff; width: 100px; }'
170
-
171
- _(@custom.stylesheet(".foo { color: #fff; /* \n\ncomment */ width: 100px; }"))
172
- .must_equal '.foo { color: #fff; width: 100px; }'
173
- end
174
- end
175
-
176
- describe 'when :allow_hacks is true' do
177
- it 'should allow common CSS hacks' do
178
- _(@relaxed.stylesheet('.foo { _border: 1px solid #fff; *width: 10px }'))
179
- .must_equal '.foo { _border: 1px solid #fff; *width: 10px }'
180
- end
181
- end
182
-
183
- describe 'when :allow_hacks is false' do
184
- it 'should not allow common CSS hacks' do
185
- _(@custom.stylesheet('.foo { _border: 1px solid #fff; *width: 10px }'))
186
- .must_equal '.foo { }'
187
- end
188
- end
189
- end
190
-
191
- describe '#tree!' do
192
- it 'should sanitize a Crass CSS parse tree' do
193
- tree = Crass.parse(String.new("@import url(foo.css);\n") <<
194
- ".foo { background: #fff; font: 16pt 'Comic Sans MS'; }\n" <<
195
- "#bar { top: 125px; background: green; }")
196
-
197
- _(@custom.tree!(tree)).must_be_same_as tree
198
-
199
- _(Crass::Parser.stringify(tree)).must_equal String.new("\n") <<
200
- ".foo { background: #fff; }\n" <<
201
- "#bar { background: green; }"
202
- end
203
- end
204
- end
205
-
206
- describe 'class methods' do
207
- describe '.properties' do
208
- it 'should sanitize CSS properties with the given config' do
209
- css = 'background: #fff; width: expression(alert("hi"));'
210
-
211
- _(Sanitize::CSS.properties(css)).must_equal ' '
212
- _(Sanitize::CSS.properties(css, Sanitize::Config::RELAXED[:css])).must_equal 'background: #fff; '
213
- _(Sanitize::CSS.properties(css, :properties => %w[background color width])).must_equal 'background: #fff; '
214
- end
215
- end
216
-
217
- describe '.stylesheet' do
218
- it 'should sanitize a CSS stylesheet with the given config' do
219
- css = %[
220
- /* Yay CSS! */
221
- .foo { color: #fff; }
222
- #bar { background: url(yay.jpg); }
223
-
224
- @media screen (max-width:480px) {
225
- .foo { width: 400px; }
226
- #bar:not(.baz) { height: 100px; }
227
- }
228
- ].strip
229
-
230
- _(Sanitize::CSS.stylesheet(css).strip).must_equal %[
231
- .foo { }
232
- #bar { }
233
- ].strip
234
-
235
- _(Sanitize::CSS.stylesheet(css, Sanitize::Config::RELAXED[:css])).must_equal css
236
-
237
- _(Sanitize::CSS.stylesheet(css, :properties => %w[background color width]).strip).must_equal %[
238
- .foo { color: #fff; }
239
- #bar { }
240
- ].strip
241
- end
242
- end
243
-
244
- describe '.tree!' do
245
- it 'should sanitize a Crass CSS parse tree with the given config' do
246
- tree = Crass.parse(String.new("@import url(foo.css);\n") <<
247
- ".foo { background: #fff; font: 16pt 'Comic Sans MS'; }\n" <<
248
- "#bar { top: 125px; background: green; }")
249
-
250
- _(Sanitize::CSS.tree!(tree, :properties => %w[background color width])).must_be_same_as tree
251
-
252
- _(Crass::Parser.stringify(tree)).must_equal String.new("\n") <<
253
- ".foo { background: #fff; }\n" <<
254
- "#bar { background: green; }"
255
- end
256
- end
257
- end
258
-
259
- describe 'functionality' do
260
- before do
261
- @default = Sanitize::CSS.new
262
- @relaxed = Sanitize::CSS.new(Sanitize::Config::RELAXED[:css])
263
- end
264
-
265
- # https://github.com/rgrove/sanitize/issues/121
266
- it 'should parse the contents of @media rules properly' do
267
- css = '@media { p[class="center"] { text-align: center; }}'
268
- _(@relaxed.stylesheet(css)).must_equal css
269
-
270
- css = %[
271
- @media (max-width: 720px) {
272
- p.foo > .bar { float: right; width: expression(body.scrollLeft + 50 + 'px'); }
273
- #baz { color: green; }
274
-
275
- @media (orientation: portrait) {
276
- #baz { color: red; }
277
- }
278
- }
279
- ].strip
280
-
281
- _(@relaxed.stylesheet(css)).must_equal %[
282
- @media (max-width: 720px) {
283
- p.foo > .bar { float: right; }
284
- #baz { color: green; }
285
-
286
- @media (orientation: portrait) {
287
- #baz { color: red; }
288
- }
289
- }
290
- ].strip
291
- end
292
-
293
- it 'should parse @page rules properly' do
294
- css = %[
295
- @page { margin: 2cm } /* All margins set to 2cm */
296
-
297
- @page :right {
298
- @top-center { content: "Preliminary edition" }
299
- @bottom-center { content: counter(page) }
300
- }
301
-
302
- @page {
303
- size: 8.5in 11in;
304
- margin: 10%;
305
-
306
- @top-left {
307
- content: "Hamlet";
308
- }
309
- @top-right {
310
- content: "Page " counter(page);
311
- }
312
- }
313
- ].strip
314
-
315
- _(@relaxed.stylesheet(css)).must_equal css
316
- end
317
-
318
- describe ":at_rules" do
319
- it "should remove blockless at-rules that aren't allowlisted" do
320
- css = %[
321
- @charset 'utf-8';
322
- @import url('foo.css');
323
- .foo { color: green; }
324
- ].strip
325
-
326
- _(@relaxed.stylesheet(css).strip).must_equal %[
327
- .foo { color: green; }
328
- ].strip
329
- end
330
-
331
- describe "when blockless at-rules are allowlisted" do
332
- before do
333
- @scss = Sanitize::CSS.new(Sanitize::Config.merge(Sanitize::Config::RELAXED[:css], {
334
- :at_rules => ['charset', 'import']
335
- }))
336
- end
337
-
338
- it "should not remove them" do
339
- css = %[
340
- @charset 'utf-8';
341
- @import url('foo.css');
342
- .foo { color: green; }
343
- ].strip
344
-
345
- _(@scss.stylesheet(css)).must_equal %[
346
- @charset 'utf-8';
347
- @import url('foo.css');
348
- .foo { color: green; }
349
- ].strip
350
- end
351
-
352
- it "should remove them if they have invalid blocks" do
353
- css = %[
354
- @charset { color: green }
355
- @import { color: green }
356
- .foo { color: green; }
357
- ].strip
358
-
359
- _(@scss.stylesheet(css).strip).must_equal %[
360
- .foo { color: green; }
361
- ].strip
362
- end
363
- end
364
-
365
- describe "when validating @import rules" do
366
-
367
- describe "with no validation proc specified" do
368
- before do
369
- @scss = Sanitize::CSS.new(Sanitize::Config.merge(Sanitize::Config::RELAXED[:css], {
370
- :at_rules => ['import']
371
- }))
372
- end
373
-
374
- it "should allow any URL value" do
375
- css = %[
376
- @import url('https://somesite.com/something.css');
377
- ].strip
378
-
379
- _(@scss.stylesheet(css).strip).must_equal %[
380
- @import url('https://somesite.com/something.css');
381
- ].strip
382
- end
383
- end
384
-
385
- describe "with a validation proc specified" do
386
- before do
387
- google_font_validator = Proc.new { |url| url.start_with?("https://fonts.googleapis.com") }
388
-
389
- @scss = Sanitize::CSS.new(Sanitize::Config.merge(Sanitize::Config::RELAXED[:css], {
390
- :at_rules => ['import'], :import_url_validator => google_font_validator
391
- }))
392
- end
393
-
394
- it "should allow a google fonts url" do
395
- css = %[
396
- @import 'https://fonts.googleapis.com/css?family=Indie+Flower';
397
- @import url('https://fonts.googleapis.com/css?family=Indie+Flower');
398
- ].strip
399
-
400
- _(@scss.stylesheet(css).strip).must_equal %[
401
- @import 'https://fonts.googleapis.com/css?family=Indie+Flower';
402
- @import url('https://fonts.googleapis.com/css?family=Indie+Flower');
403
- ].strip
404
- end
405
-
406
- it "should not allow a nasty url" do
407
- css = %[
408
- @import 'https://fonts.googleapis.com/css?family=Indie+Flower';
409
- @import 'https://nastysite.com/nasty_hax0r.css';
410
- @import url('https://nastysite.com/nasty_hax0r.css');
411
- ].strip
412
-
413
- _(@scss.stylesheet(css).strip).must_equal %[
414
- @import 'https://fonts.googleapis.com/css?family=Indie+Flower';
415
- ].strip
416
- end
417
-
418
- it "should not allow a blank url" do
419
- css = %[
420
- @import 'https://fonts.googleapis.com/css?family=Indie+Flower';
421
- @import '';
422
- @import url('');
423
- ].strip
424
-
425
- _(@scss.stylesheet(css).strip).must_equal %[
426
- @import 'https://fonts.googleapis.com/css?family=Indie+Flower';
427
- ].strip
428
- end
429
- end
430
- end
431
- end
432
- end
433
- end
@@ -1,230 +0,0 @@
1
- # encoding: utf-8
2
- require_relative 'common'
3
-
4
- describe 'Transformers' do
5
- make_my_diffs_pretty!
6
- parallelize_me!
7
-
8
- it 'should receive a complete env Hash as input' do
9
- Sanitize.fragment('<SPAN>foo</SPAN>',
10
- :foo => :bar,
11
- :transformers => lambda {|env|
12
- return unless env[:node].element?
13
-
14
- _(env[:config][:foo]).must_equal :bar
15
- _(env[:is_allowlisted]).must_equal false
16
- _(env[:is_whitelisted]).must_equal env[:is_allowlisted]
17
- _(env[:node]).must_be_kind_of Nokogiri::XML::Node
18
- _(env[:node_name]).must_equal 'span'
19
- _(env[:node_allowlist]).must_be_kind_of Set
20
- _(env[:node_allowlist]).must_be_empty
21
- _(env[:node_whitelist]).must_equal env[:node_allowlist]
22
- }
23
- )
24
- end
25
-
26
- it 'should traverse all node types, including the fragment itself' do
27
- nodes = []
28
-
29
- Sanitize.fragment('<div>foo</div><!--bar--><script>cdata!</script>',
30
- :transformers => proc {|env| nodes << env[:node_name] }
31
- )
32
-
33
- _(nodes).must_equal %w[
34
- #document-fragment div text text text comment script text
35
- ]
36
- end
37
-
38
- it 'should perform top-down traversal' do
39
- nodes = []
40
-
41
- Sanitize.fragment('<div><span><strong>foo</strong></span><b></b></div><p>bar</p>',
42
- :transformers => proc {|env| nodes << env[:node_name] if env[:node].element? }
43
- )
44
-
45
- _(nodes).must_equal %w[div span strong b p]
46
- end
47
-
48
- it 'should allowlist nodes in the node allowlist' do
49
- _(Sanitize.fragment('<div class="foo">foo</div><span>bar</span>',
50
- :transformers => [
51
- proc {|env|
52
- {:node_allowlist => [env[:node]]} if env[:node_name] == 'div'
53
- },
54
-
55
- proc {|env|
56
- _(env[:is_allowlisted]).must_equal false unless env[:node_name] == 'div'
57
- _(env[:is_allowlisted]).must_equal true if env[:node_name] == 'div'
58
- _(env[:node_allowlist]).must_include env[:node] if env[:node_name] == 'div'
59
- _(env[:is_whitelisted]).must_equal env[:is_allowlisted]
60
- _(env[:node_whitelist]).must_equal env[:node_allowlist]
61
- }
62
- ]
63
- )).must_equal '<div class="foo">foo</div>bar'
64
- end
65
-
66
- it 'should clear the node allowlist after each fragment' do
67
- called = false
68
-
69
- Sanitize.fragment('<div>foo</div>',
70
- :transformers => proc {|env| {:node_allowlist => [env[:node]]}}
71
- )
72
-
73
- Sanitize.fragment('<div>foo</div>',
74
- :transformers => proc {|env|
75
- called = true
76
- _(env[:is_allowlisted]).must_equal false
77
- _(env[:is_whitelisted]).must_equal env[:is_allowlisted]
78
- _(env[:node_allowlist]).must_be_empty
79
- _(env[:node_whitelist]).must_equal env[:node_allowlist]
80
- }
81
- )
82
-
83
- _(called).must_equal true
84
- end
85
-
86
- it 'should accept a method transformer' do
87
- def transformer(env); end
88
- _(Sanitize.fragment('<div>foo</div>', :transformers => method(:transformer)))
89
- .must_equal(' foo ')
90
- end
91
-
92
- describe 'Image allowlist transformer' do
93
- require 'uri'
94
-
95
- image_allowlist_transformer = lambda do |env|
96
- # Ignore everything except <img> elements.
97
- return unless env[:node_name] == 'img'
98
-
99
- node = env[:node]
100
- image_uri = URI.parse(node['src'])
101
-
102
- # Only allow relative URLs or URLs with the example.com domain. The
103
- # image_uri.host.nil? check ensures that protocol-relative URLs like
104
- # "//evil.com/foo.jpg".
105
- unless image_uri.host == 'example.com' || (image_uri.host.nil? && image_uri.relative?)
106
- node.unlink # `Nokogiri::XML::Node#unlink` removes a node from the document
107
- end
108
- end
109
-
110
- before do
111
- @s = Sanitize.new(Sanitize::Config.merge(Sanitize::Config::RELAXED,
112
- :transformers => image_allowlist_transformer))
113
- end
114
-
115
- it 'should allow images with relative URLs' do
116
- input = '<img src="/foo/bar.jpg">'
117
- _(@s.fragment(input)).must_equal(input)
118
- end
119
-
120
- it 'should allow images at the example.com domain' do
121
- input = '<img src="http://example.com/foo/bar.jpg">'
122
- _(@s.fragment(input)).must_equal(input)
123
-
124
- input = '<img src="https://example.com/foo/bar.jpg">'
125
- _(@s.fragment(input)).must_equal(input)
126
-
127
- input = '<img src="//example.com/foo/bar.jpg">'
128
- _(@s.fragment(input)).must_equal(input)
129
- end
130
-
131
- it 'should not allow images at other domains' do
132
- input = '<img src="http://evil.com/foo/bar.jpg">'
133
- _(@s.fragment(input)).must_equal('')
134
-
135
- input = '<img src="https://evil.com/foo/bar.jpg">'
136
- _(@s.fragment(input)).must_equal('')
137
-
138
- input = '<img src="//evil.com/foo/bar.jpg">'
139
- _(@s.fragment(input)).must_equal('')
140
-
141
- input = '<img src="http://subdomain.example.com/foo/bar.jpg">'
142
- _(@s.fragment(input)).must_equal('')
143
- end
144
- end
145
-
146
- describe 'YouTube transformer' do
147
- youtube_transformer = lambda do |env|
148
- node = env[:node]
149
- node_name = env[:node_name]
150
-
151
- # Don't continue if this node is already allowlisted or is not an element.
152
- return if env[:is_allowlisted] || !node.element?
153
-
154
- # Don't continue unless the node is an iframe.
155
- return unless node_name == 'iframe'
156
-
157
- # Verify that the video URL is actually a valid YouTube video URL.
158
- return unless node['src'] =~ %r|\A(?:https?:)?//(?:www\.)?youtube(?:-nocookie)?\.com/|
159
-
160
- # We're now certain that this is a YouTube embed, but we still need to run
161
- # it through a special Sanitize step to ensure that no unwanted elements or
162
- # attributes that don't belong in a YouTube embed can sneak in.
163
- Sanitize.node!(node, {
164
- :elements => %w[iframe],
165
-
166
- :attributes => {
167
- 'iframe' => %w[allowfullscreen frameborder height src width]
168
- }
169
- })
170
-
171
- # Now that we're sure that this is a valid YouTube embed and that there are
172
- # no unwanted elements or attributes hidden inside it, we can tell Sanitize
173
- # to allowlist the current node.
174
- {:node_allowlist => [node]}
175
- end
176
-
177
- it 'should allow HTTP YouTube video embeds' do
178
- input = '<iframe width="420" height="315" src="http://www.youtube.com/embed/QH2-TGUlwu4" frameborder="0" allowfullscreen bogus="bogus"><script>alert()</script></iframe>'
179
-
180
- _(Sanitize.fragment(input, :transformers => youtube_transformer))
181
- .must_equal '<iframe width="420" height="315" src="http://www.youtube.com/embed/QH2-TGUlwu4" frameborder="0" allowfullscreen=""></iframe>'
182
- end
183
-
184
- it 'should allow HTTPS YouTube video embeds' do
185
- input = '<iframe width="420" height="315" src="https://www.youtube.com/embed/QH2-TGUlwu4" frameborder="0" allowfullscreen bogus="bogus"><script>alert()</script></iframe>'
186
-
187
- _(Sanitize.fragment(input, :transformers => youtube_transformer))
188
- .must_equal '<iframe width="420" height="315" src="https://www.youtube.com/embed/QH2-TGUlwu4" frameborder="0" allowfullscreen=""></iframe>'
189
- end
190
-
191
- it 'should allow protocol-relative YouTube video embeds' do
192
- input = '<iframe width="420" height="315" src="//www.youtube.com/embed/QH2-TGUlwu4" frameborder="0" allowfullscreen bogus="bogus"><script>alert()</script></iframe>'
193
-
194
- _(Sanitize.fragment(input, :transformers => youtube_transformer))
195
- .must_equal '<iframe width="420" height="315" src="//www.youtube.com/embed/QH2-TGUlwu4" frameborder="0" allowfullscreen=""></iframe>'
196
- end
197
-
198
- it 'should allow privacy-enhanced YouTube video embeds' do
199
- input = '<iframe width="420" height="315" src="https://www.youtube-nocookie.com/embed/QH2-TGUlwu4" frameborder="0" allowfullscreen bogus="bogus"><script>alert()</script></iframe>'
200
-
201
- _(Sanitize.fragment(input, :transformers => youtube_transformer))
202
- .must_equal '<iframe width="420" height="315" src="https://www.youtube-nocookie.com/embed/QH2-TGUlwu4" frameborder="0" allowfullscreen=""></iframe>'
203
- end
204
-
205
- it 'should not allow non-YouTube video embeds' do
206
- input = '<iframe width="420" height="315" src="http://www.fake-youtube.com/embed/QH2-TGUlwu4" frameborder="0" allowfullscreen></iframe>'
207
-
208
- _(Sanitize.fragment(input, :transformers => youtube_transformer))
209
- .must_equal('')
210
- end
211
- end
212
-
213
- describe 'DOM modification transformer' do
214
- b_to_strong_tag_transformer = lambda do |env|
215
- node = env[:node]
216
- node_name = env[:node_name]
217
-
218
- if node_name == 'b'
219
- node.name = 'strong'
220
- end
221
- end
222
-
223
- it 'should allow the <b> tag to be changed to a <strong> tag' do
224
- input = '<b>text</b>'
225
-
226
- _(Sanitize.fragment(input, :elements => ['strong'], :transformers => b_to_strong_tag_transformer))
227
- .must_equal '<strong>text</strong>'
228
- end
229
- end
230
- end