sanitize 6.1.3 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,27 +1,28 @@
1
- # encoding: utf-8
2
- require_relative 'common'
1
+ # frozen_string_literal: true
3
2
 
4
- describe 'Sanitize::CSS' do
3
+ require_relative "common"
4
+
5
+ describe "Sanitize::CSS" do
5
6
  make_my_diffs_pretty!
6
7
  parallelize_me!
7
8
 
8
- describe 'instance methods' do
9
+ describe "instance methods" do
9
10
  before do
10
11
  @default = Sanitize::CSS.new
11
12
  @relaxed = Sanitize::CSS.new(Sanitize::Config::RELAXED[:css])
12
- @custom = Sanitize::CSS.new(:properties => %w[background color width])
13
+ @custom = Sanitize::CSS.new(properties: %w[background color width])
13
14
  end
14
15
 
15
- describe '#properties' do
16
- it 'should sanitize CSS properties' do
16
+ describe "#properties" do
17
+ it "should sanitize CSS properties" do
17
18
  css = 'background: #fff; width: expression(alert("hi"));'
18
19
 
19
- _(@default.properties(css)).must_equal ' '
20
- _(@relaxed.properties(css)).must_equal 'background: #fff; '
21
- _(@custom.properties(css)).must_equal 'background: #fff; '
20
+ _(@default.properties(css)).must_equal " "
21
+ _(@relaxed.properties(css)).must_equal "background: #fff; "
22
+ _(@custom.properties(css)).must_equal "background: #fff; "
22
23
  end
23
24
 
24
- it 'should allow allowlisted URL protocols' do
25
+ it "should allow allowlisted URL protocols" do
25
26
  [
26
27
  "background: url(relative.jpg)",
27
28
  "background: url('relative.jpg')",
@@ -39,13 +40,13 @@ describe 'Sanitize::CSS' do
39
40
  "background: image('https://example.com/https.jpg');",
40
41
  "background: image(rtl 'https://example.com/https.jpg');"
41
42
  ].each do |css|
42
- _(@default.properties(css)).must_equal ''
43
+ _(@default.properties(css)).must_equal ""
43
44
  _(@relaxed.properties(css)).must_equal css
44
- _(@custom.properties(css)).must_equal ''
45
+ _(@custom.properties(css)).must_equal ""
45
46
  end
46
47
  end
47
48
 
48
- it 'should not allow non-allowlisted URL protocols' do
49
+ it "should not allow non-allowlisted URL protocols" do
49
50
  [
50
51
  "background: url(javascript:alert(0))",
51
52
  "background: url(ja\\56 ascript:alert(0))",
@@ -55,21 +56,21 @@ describe 'Sanitize::CSS' do
55
56
  "background: url('javas\\\ncript:alert(0)')",
56
57
  "background: url('java\\0script:foo')"
57
58
  ].each do |css|
58
- _(@default.properties(css)).must_equal ''
59
- _(@relaxed.properties(css)).must_equal ''
60
- _(@custom.properties(css)).must_equal ''
59
+ _(@default.properties(css)).must_equal ""
60
+ _(@relaxed.properties(css)).must_equal ""
61
+ _(@custom.properties(css)).must_equal ""
61
62
  end
62
63
  end
63
64
 
64
- it 'should not allow -moz-binding' do
65
+ it "should not allow -moz-binding" do
65
66
  css = "-moz-binding:url('http://ha.ckers.org/xssmoz.xml#xss')"
66
67
 
67
- _(@default.properties(css)).must_equal ''
68
- _(@relaxed.properties(css)).must_equal ''
69
- _(@custom.properties(css)).must_equal ''
68
+ _(@default.properties(css)).must_equal ""
69
+ _(@relaxed.properties(css)).must_equal ""
70
+ _(@custom.properties(css)).must_equal ""
70
71
  end
71
72
 
72
- it 'should not allow expressions' do
73
+ it "should not allow expressions" do
73
74
  [
74
75
  "width:expression(alert(1))",
75
76
  "width: /**/expression(alert(1)",
@@ -78,57 +79,57 @@ describe 'Sanitize::CSS' do
78
79
  "xss:expression(alert(1))",
79
80
  "height: foo(expression(alert(1)));"
80
81
  ].each do |css|
81
- _(@default.properties(css)).must_equal ''
82
- _(@relaxed.properties(css)).must_equal ''
83
- _(@custom.properties(css)).must_equal ''
82
+ _(@default.properties(css)).must_equal ""
83
+ _(@relaxed.properties(css)).must_equal ""
84
+ _(@custom.properties(css)).must_equal ""
84
85
  end
85
86
  end
86
87
 
87
- it 'should not allow behaviors' do
88
+ it "should not allow behaviors" do
88
89
  css = "behavior: url(xss.htc);"
89
90
 
90
- _(@default.properties(css)).must_equal ''
91
- _(@relaxed.properties(css)).must_equal ''
92
- _(@custom.properties(css)).must_equal ''
91
+ _(@default.properties(css)).must_equal ""
92
+ _(@relaxed.properties(css)).must_equal ""
93
+ _(@custom.properties(css)).must_equal ""
93
94
  end
94
95
 
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;'
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;"
99
100
 
100
101
  _(@relaxed.properties("color: #fff; /* \n\ncomment */ width: 100px;"))
101
102
  .must_equal "color: #fff; /* \n\ncomment */ width: 100px;"
102
103
  end
103
104
  end
104
105
 
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;'
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;"
109
110
 
110
111
  _(@custom.properties("color: #fff; /* \n\ncomment */ width: 100px;"))
111
- .must_equal 'color: #fff; width: 100px;'
112
+ .must_equal "color: #fff; width: 100px;"
112
113
  end
113
114
  end
114
115
 
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'
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"
119
120
  end
120
121
  end
121
122
 
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 ' '
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 " "
126
127
  end
127
128
  end
128
129
  end
129
130
 
130
- describe '#stylesheet' do
131
- it 'should sanitize a CSS stylesheet' do
131
+ describe "#stylesheet" do
132
+ it "should sanitize a CSS stylesheet" do
132
133
  css = %[
133
134
  /* Yay CSS! */
134
135
  .foo { color: #fff; }
@@ -140,82 +141,82 @@ describe 'Sanitize::CSS' do
140
141
  }
141
142
  ].strip
142
143
 
143
- _(@default.stylesheet(css).strip).must_equal %[
144
+ _(@default.stylesheet(css).strip).must_equal %(
144
145
  .foo { }
145
146
  #bar { }
146
- ].strip
147
+ ).strip
147
148
 
148
149
  _(@relaxed.stylesheet(css)).must_equal css
149
150
 
150
- _(@custom.stylesheet(css).strip).must_equal %[
151
+ _(@custom.stylesheet(css).strip).must_equal %(
151
152
  .foo { color: #fff; }
152
153
  #bar { }
153
- ].strip
154
+ ).strip
154
155
  end
155
156
 
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; }'
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; }"
160
161
 
161
162
  _(@relaxed.stylesheet(".foo { color: #fff; /* \n\ncomment */ width: 100px; }"))
162
163
  .must_equal ".foo { color: #fff; /* \n\ncomment */ width: 100px; }"
163
164
  end
164
165
  end
165
166
 
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; }'
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; }"
170
171
 
171
172
  _(@custom.stylesheet(".foo { color: #fff; /* \n\ncomment */ width: 100px; }"))
172
- .must_equal '.foo { color: #fff; width: 100px; }'
173
+ .must_equal ".foo { color: #fff; width: 100px; }"
173
174
  end
174
175
  end
175
176
 
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 }'
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 }"
180
181
  end
181
182
  end
182
183
 
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 { }'
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 { }"
187
188
  end
188
189
  end
189
190
  end
190
191
 
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" <<
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" \
195
196
  "#bar { top: 125px; background: green; }")
196
197
 
197
198
  _(@custom.tree!(tree)).must_be_same_as tree
198
199
 
199
- _(Crass::Parser.stringify(tree)).must_equal String.new("\n") <<
200
- ".foo { background: #fff; }\n" <<
201
- "#bar { background: green; }"
200
+ _(Crass::Parser.stringify(tree)).must_equal "\n" \
201
+ ".foo { background: #fff; }\n" \
202
+ "#bar { background: green; }"
202
203
  end
203
204
  end
204
205
  end
205
206
 
206
- describe 'class methods' do
207
- describe '.properties' do
208
- it 'should sanitize CSS properties with the given config' do
207
+ describe "class methods" do
208
+ describe ".properties" do
209
+ it "should sanitize CSS properties with the given config" do
209
210
  css = 'background: #fff; width: expression(alert("hi"));'
210
211
 
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; '
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; "
214
215
  end
215
216
  end
216
217
 
217
- describe '.stylesheet' do
218
- it 'should sanitize a CSS stylesheet with the given config' do
218
+ describe ".stylesheet" do
219
+ it "should sanitize a CSS stylesheet with the given config" do
219
220
  css = %[
220
221
  /* Yay CSS! */
221
222
  .foo { color: #fff; }
@@ -227,43 +228,43 @@ describe 'Sanitize::CSS' do
227
228
  }
228
229
  ].strip
229
230
 
230
- _(Sanitize::CSS.stylesheet(css).strip).must_equal %[
231
+ _(Sanitize::CSS.stylesheet(css).strip).must_equal %(
231
232
  .foo { }
232
233
  #bar { }
233
- ].strip
234
+ ).strip
234
235
 
235
236
  _(Sanitize::CSS.stylesheet(css, Sanitize::Config::RELAXED[:css])).must_equal css
236
237
 
237
- _(Sanitize::CSS.stylesheet(css, :properties => %w[background color width]).strip).must_equal %[
238
+ _(Sanitize::CSS.stylesheet(css, properties: %w[background color width]).strip).must_equal %(
238
239
  .foo { color: #fff; }
239
240
  #bar { }
240
- ].strip
241
+ ).strip
241
242
  end
242
243
  end
243
244
 
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" <<
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" \
248
249
  "#bar { top: 125px; background: green; }")
249
250
 
250
- _(Sanitize::CSS.tree!(tree, :properties => %w[background color width])).must_be_same_as tree
251
+ _(Sanitize::CSS.tree!(tree, properties: %w[background color width])).must_be_same_as tree
251
252
 
252
- _(Crass::Parser.stringify(tree)).must_equal String.new("\n") <<
253
- ".foo { background: #fff; }\n" <<
254
- "#bar { background: green; }"
253
+ _(Crass::Parser.stringify(tree)).must_equal "\n" \
254
+ ".foo { background: #fff; }\n" \
255
+ "#bar { background: green; }"
255
256
  end
256
257
  end
257
258
  end
258
259
 
259
- describe 'functionality' do
260
+ describe "functionality" do
260
261
  before do
261
262
  @default = Sanitize::CSS.new
262
263
  @relaxed = Sanitize::CSS.new(Sanitize::Config::RELAXED[:css])
263
264
  end
264
265
 
265
266
  # https://github.com/rgrove/sanitize/issues/121
266
- it 'should parse the contents of @media rules properly' do
267
+ it "should parse the contents of @media rules properly" do
267
268
  css = '@media { p[class="center"] { text-align: center; }}'
268
269
  _(@relaxed.stylesheet(css)).must_equal css
269
270
 
@@ -290,7 +291,7 @@ describe 'Sanitize::CSS' do
290
291
  ].strip
291
292
  end
292
293
 
293
- it 'should parse @page rules properly' do
294
+ it "should parse @page rules properly" do
294
295
  css = %[
295
296
  @page { margin: 2cm } /* All margins set to 2cm */
296
297
 
@@ -323,15 +324,50 @@ describe 'Sanitize::CSS' do
323
324
  .foo { color: green; }
324
325
  ].strip
325
326
 
326
- _(@relaxed.stylesheet(css).strip).must_equal %[
327
+ _(@relaxed.stylesheet(css).strip).must_equal %(
327
328
  .foo { color: green; }
328
- ].strip
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
329
365
  end
330
366
 
331
367
  describe "when blockless at-rules are allowlisted" do
332
368
  before do
333
369
  @scss = Sanitize::CSS.new(Sanitize::Config.merge(Sanitize::Config::RELAXED[:css], {
334
- :at_rules => ['charset', 'import']
370
+ at_rules: ["charset", "import"]
335
371
  }))
336
372
  end
337
373
 
@@ -350,24 +386,23 @@ describe 'Sanitize::CSS' do
350
386
  end
351
387
 
352
388
  it "should remove them if they have invalid blocks" do
353
- css = %[
389
+ css = %(
354
390
  @charset { color: green }
355
391
  @import { color: green }
356
392
  .foo { color: green; }
357
- ].strip
393
+ ).strip
358
394
 
359
- _(@scss.stylesheet(css).strip).must_equal %[
395
+ _(@scss.stylesheet(css).strip).must_equal %(
360
396
  .foo { color: green; }
361
- ].strip
397
+ ).strip
362
398
  end
363
399
  end
364
400
 
365
401
  describe "when validating @import rules" do
366
-
367
402
  describe "with no validation proc specified" do
368
403
  before do
369
404
  @scss = Sanitize::CSS.new(Sanitize::Config.merge(Sanitize::Config::RELAXED[:css], {
370
- :at_rules => ['import']
405
+ at_rules: ["import"]
371
406
  }))
372
407
  end
373
408
 
@@ -384,10 +419,10 @@ describe 'Sanitize::CSS' do
384
419
 
385
420
  describe "with a validation proc specified" do
386
421
  before do
387
- google_font_validator = Proc.new { |url| url.start_with?("https://fonts.googleapis.com") }
422
+ google_font_validator = proc { |url| url.start_with?("https://fonts.googleapis.com") }
388
423
 
389
424
  @scss = Sanitize::CSS.new(Sanitize::Config.merge(Sanitize::Config::RELAXED[:css], {
390
- :at_rules => ['import'], :import_url_validator => google_font_validator
425
+ at_rules: ["import"], import_url_validator: google_font_validator
391
426
  }))
392
427
  end
393
428
 
@@ -410,9 +445,9 @@ describe 'Sanitize::CSS' do
410
445
  @import url('https://nastysite.com/nasty_hax0r.css');
411
446
  ].strip
412
447
 
413
- _(@scss.stylesheet(css).strip).must_equal %[
448
+ _(@scss.stylesheet(css).strip).must_equal %(
414
449
  @import 'https://fonts.googleapis.com/css?family=Indie+Flower';
415
- ].strip
450
+ ).strip
416
451
  end
417
452
 
418
453
  it "should not allow a blank url" do
@@ -422,9 +457,9 @@ describe 'Sanitize::CSS' do
422
457
  @import url('');
423
458
  ].strip
424
459
 
425
- _(@scss.stylesheet(css).strip).must_equal %[
460
+ _(@scss.stylesheet(css).strip).must_equal %(
426
461
  @import 'https://fonts.googleapis.com/css?family=Indie+Flower';
427
- ].strip
462
+ ).strip
428
463
  end
429
464
  end
430
465
  end