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