sanitize 6.1.2 → 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')",
@@ -32,17 +33,20 @@ describe 'Sanitize::CSS' do
32
33
  "background: image-set('relative.jpg' 1x, 'relative-2x.jpg' 2x)",
33
34
  "background: image-set('https://example.com/https.jpg' 1x, 'https://example.com/https-2x.jpg' 2x)",
34
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'))",
35
39
  "background: image('relative.jpg');",
36
40
  "background: image('https://example.com/https.jpg');",
37
41
  "background: image(rtl 'https://example.com/https.jpg');"
38
42
  ].each do |css|
39
- _(@default.properties(css)).must_equal ''
43
+ _(@default.properties(css)).must_equal ""
40
44
  _(@relaxed.properties(css)).must_equal css
41
- _(@custom.properties(css)).must_equal ''
45
+ _(@custom.properties(css)).must_equal ""
42
46
  end
43
47
  end
44
48
 
45
- it 'should not allow non-allowlisted URL protocols' do
49
+ it "should not allow non-allowlisted URL protocols" do
46
50
  [
47
51
  "background: url(javascript:alert(0))",
48
52
  "background: url(ja\\56 ascript:alert(0))",
@@ -52,21 +56,21 @@ describe 'Sanitize::CSS' do
52
56
  "background: url('javas\\\ncript:alert(0)')",
53
57
  "background: url('java\\0script:foo')"
54
58
  ].each do |css|
55
- _(@default.properties(css)).must_equal ''
56
- _(@relaxed.properties(css)).must_equal ''
57
- _(@custom.properties(css)).must_equal ''
59
+ _(@default.properties(css)).must_equal ""
60
+ _(@relaxed.properties(css)).must_equal ""
61
+ _(@custom.properties(css)).must_equal ""
58
62
  end
59
63
  end
60
64
 
61
- it 'should not allow -moz-binding' do
65
+ it "should not allow -moz-binding" do
62
66
  css = "-moz-binding:url('http://ha.ckers.org/xssmoz.xml#xss')"
63
67
 
64
- _(@default.properties(css)).must_equal ''
65
- _(@relaxed.properties(css)).must_equal ''
66
- _(@custom.properties(css)).must_equal ''
68
+ _(@default.properties(css)).must_equal ""
69
+ _(@relaxed.properties(css)).must_equal ""
70
+ _(@custom.properties(css)).must_equal ""
67
71
  end
68
72
 
69
- it 'should not allow expressions' do
73
+ it "should not allow expressions" do
70
74
  [
71
75
  "width:expression(alert(1))",
72
76
  "width: /**/expression(alert(1)",
@@ -75,57 +79,57 @@ describe 'Sanitize::CSS' do
75
79
  "xss:expression(alert(1))",
76
80
  "height: foo(expression(alert(1)));"
77
81
  ].each do |css|
78
- _(@default.properties(css)).must_equal ''
79
- _(@relaxed.properties(css)).must_equal ''
80
- _(@custom.properties(css)).must_equal ''
82
+ _(@default.properties(css)).must_equal ""
83
+ _(@relaxed.properties(css)).must_equal ""
84
+ _(@custom.properties(css)).must_equal ""
81
85
  end
82
86
  end
83
87
 
84
- it 'should not allow behaviors' do
88
+ it "should not allow behaviors" do
85
89
  css = "behavior: url(xss.htc);"
86
90
 
87
- _(@default.properties(css)).must_equal ''
88
- _(@relaxed.properties(css)).must_equal ''
89
- _(@custom.properties(css)).must_equal ''
91
+ _(@default.properties(css)).must_equal ""
92
+ _(@relaxed.properties(css)).must_equal ""
93
+ _(@custom.properties(css)).must_equal ""
90
94
  end
91
95
 
92
- describe 'when :allow_comments is true' do
93
- it 'should preserve comments' do
94
- _(@relaxed.properties('color: #fff; /* comment */ width: 100px;'))
95
- .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;"
96
100
 
97
101
  _(@relaxed.properties("color: #fff; /* \n\ncomment */ width: 100px;"))
98
102
  .must_equal "color: #fff; /* \n\ncomment */ width: 100px;"
99
103
  end
100
104
  end
101
105
 
102
- describe 'when :allow_comments is false' do
103
- it 'should strip comments' do
104
- _(@custom.properties('color: #fff; /* comment */ width: 100px;'))
105
- .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;"
106
110
 
107
111
  _(@custom.properties("color: #fff; /* \n\ncomment */ width: 100px;"))
108
- .must_equal 'color: #fff; width: 100px;'
112
+ .must_equal "color: #fff; width: 100px;"
109
113
  end
110
114
  end
111
115
 
112
- describe 'when :allow_hacks is true' do
113
- it 'should allow common CSS hacks' do
114
- _(@relaxed.properties('_border: 1px solid #fff; *width: 10px'))
115
- .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"
116
120
  end
117
121
  end
118
122
 
119
- describe 'when :allow_hacks is false' do
120
- it 'should not allow common CSS hacks' do
121
- _(@custom.properties('_border: 1px solid #fff; *width: 10px'))
122
- .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 " "
123
127
  end
124
128
  end
125
129
  end
126
130
 
127
- describe '#stylesheet' do
128
- it 'should sanitize a CSS stylesheet' do
131
+ describe "#stylesheet" do
132
+ it "should sanitize a CSS stylesheet" do
129
133
  css = %[
130
134
  /* Yay CSS! */
131
135
  .foo { color: #fff; }
@@ -137,82 +141,82 @@ describe 'Sanitize::CSS' do
137
141
  }
138
142
  ].strip
139
143
 
140
- _(@default.stylesheet(css).strip).must_equal %[
144
+ _(@default.stylesheet(css).strip).must_equal %(
141
145
  .foo { }
142
146
  #bar { }
143
- ].strip
147
+ ).strip
144
148
 
145
149
  _(@relaxed.stylesheet(css)).must_equal css
146
150
 
147
- _(@custom.stylesheet(css).strip).must_equal %[
151
+ _(@custom.stylesheet(css).strip).must_equal %(
148
152
  .foo { color: #fff; }
149
153
  #bar { }
150
- ].strip
154
+ ).strip
151
155
  end
152
156
 
153
- describe 'when :allow_comments is true' do
154
- it 'should preserve comments' do
155
- _(@relaxed.stylesheet('.foo { color: #fff; /* comment */ width: 100px; }'))
156
- .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; }"
157
161
 
158
162
  _(@relaxed.stylesheet(".foo { color: #fff; /* \n\ncomment */ width: 100px; }"))
159
163
  .must_equal ".foo { color: #fff; /* \n\ncomment */ width: 100px; }"
160
164
  end
161
165
  end
162
166
 
163
- describe 'when :allow_comments is false' do
164
- it 'should strip comments' do
165
- _(@custom.stylesheet('.foo { color: #fff; /* comment */ width: 100px; }'))
166
- .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; }"
167
171
 
168
172
  _(@custom.stylesheet(".foo { color: #fff; /* \n\ncomment */ width: 100px; }"))
169
- .must_equal '.foo { color: #fff; width: 100px; }'
173
+ .must_equal ".foo { color: #fff; width: 100px; }"
170
174
  end
171
175
  end
172
176
 
173
- describe 'when :allow_hacks is true' do
174
- it 'should allow common CSS hacks' do
175
- _(@relaxed.stylesheet('.foo { _border: 1px solid #fff; *width: 10px }'))
176
- .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 }"
177
181
  end
178
182
  end
179
183
 
180
- describe 'when :allow_hacks is false' do
181
- it 'should not allow common CSS hacks' do
182
- _(@custom.stylesheet('.foo { _border: 1px solid #fff; *width: 10px }'))
183
- .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 { }"
184
188
  end
185
189
  end
186
190
  end
187
191
 
188
- describe '#tree!' do
189
- it 'should sanitize a Crass CSS parse tree' do
190
- tree = Crass.parse(String.new("@import url(foo.css);\n") <<
191
- ".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" \
192
196
  "#bar { top: 125px; background: green; }")
193
197
 
194
198
  _(@custom.tree!(tree)).must_be_same_as tree
195
199
 
196
- _(Crass::Parser.stringify(tree)).must_equal String.new("\n") <<
197
- ".foo { background: #fff; }\n" <<
198
- "#bar { background: green; }"
200
+ _(Crass::Parser.stringify(tree)).must_equal "\n" \
201
+ ".foo { background: #fff; }\n" \
202
+ "#bar { background: green; }"
199
203
  end
200
204
  end
201
205
  end
202
206
 
203
- describe 'class methods' do
204
- describe '.properties' do
205
- 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
206
210
  css = 'background: #fff; width: expression(alert("hi"));'
207
211
 
208
- _(Sanitize::CSS.properties(css)).must_equal ' '
209
- _(Sanitize::CSS.properties(css, Sanitize::Config::RELAXED[:css])).must_equal 'background: #fff; '
210
- _(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; "
211
215
  end
212
216
  end
213
217
 
214
- describe '.stylesheet' do
215
- 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
216
220
  css = %[
217
221
  /* Yay CSS! */
218
222
  .foo { color: #fff; }
@@ -224,43 +228,43 @@ describe 'Sanitize::CSS' do
224
228
  }
225
229
  ].strip
226
230
 
227
- _(Sanitize::CSS.stylesheet(css).strip).must_equal %[
231
+ _(Sanitize::CSS.stylesheet(css).strip).must_equal %(
228
232
  .foo { }
229
233
  #bar { }
230
- ].strip
234
+ ).strip
231
235
 
232
236
  _(Sanitize::CSS.stylesheet(css, Sanitize::Config::RELAXED[:css])).must_equal css
233
237
 
234
- _(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 %(
235
239
  .foo { color: #fff; }
236
240
  #bar { }
237
- ].strip
241
+ ).strip
238
242
  end
239
243
  end
240
244
 
241
- describe '.tree!' do
242
- it 'should sanitize a Crass CSS parse tree with the given config' do
243
- tree = Crass.parse(String.new("@import url(foo.css);\n") <<
244
- ".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" \
245
249
  "#bar { top: 125px; background: green; }")
246
250
 
247
- _(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
248
252
 
249
- _(Crass::Parser.stringify(tree)).must_equal String.new("\n") <<
250
- ".foo { background: #fff; }\n" <<
251
- "#bar { background: green; }"
253
+ _(Crass::Parser.stringify(tree)).must_equal "\n" \
254
+ ".foo { background: #fff; }\n" \
255
+ "#bar { background: green; }"
252
256
  end
253
257
  end
254
258
  end
255
259
 
256
- describe 'functionality' do
260
+ describe "functionality" do
257
261
  before do
258
262
  @default = Sanitize::CSS.new
259
263
  @relaxed = Sanitize::CSS.new(Sanitize::Config::RELAXED[:css])
260
264
  end
261
265
 
262
266
  # https://github.com/rgrove/sanitize/issues/121
263
- it 'should parse the contents of @media rules properly' do
267
+ it "should parse the contents of @media rules properly" do
264
268
  css = '@media { p[class="center"] { text-align: center; }}'
265
269
  _(@relaxed.stylesheet(css)).must_equal css
266
270
 
@@ -287,7 +291,7 @@ describe 'Sanitize::CSS' do
287
291
  ].strip
288
292
  end
289
293
 
290
- it 'should parse @page rules properly' do
294
+ it "should parse @page rules properly" do
291
295
  css = %[
292
296
  @page { margin: 2cm } /* All margins set to 2cm */
293
297
 
@@ -320,15 +324,50 @@ describe 'Sanitize::CSS' do
320
324
  .foo { color: green; }
321
325
  ].strip
322
326
 
323
- _(@relaxed.stylesheet(css).strip).must_equal %[
327
+ _(@relaxed.stylesheet(css).strip).must_equal %(
324
328
  .foo { color: green; }
325
- ].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
326
365
  end
327
366
 
328
367
  describe "when blockless at-rules are allowlisted" do
329
368
  before do
330
369
  @scss = Sanitize::CSS.new(Sanitize::Config.merge(Sanitize::Config::RELAXED[:css], {
331
- :at_rules => ['charset', 'import']
370
+ at_rules: ["charset", "import"]
332
371
  }))
333
372
  end
334
373
 
@@ -347,24 +386,23 @@ describe 'Sanitize::CSS' do
347
386
  end
348
387
 
349
388
  it "should remove them if they have invalid blocks" do
350
- css = %[
389
+ css = %(
351
390
  @charset { color: green }
352
391
  @import { color: green }
353
392
  .foo { color: green; }
354
- ].strip
393
+ ).strip
355
394
 
356
- _(@scss.stylesheet(css).strip).must_equal %[
395
+ _(@scss.stylesheet(css).strip).must_equal %(
357
396
  .foo { color: green; }
358
- ].strip
397
+ ).strip
359
398
  end
360
399
  end
361
400
 
362
401
  describe "when validating @import rules" do
363
-
364
402
  describe "with no validation proc specified" do
365
403
  before do
366
404
  @scss = Sanitize::CSS.new(Sanitize::Config.merge(Sanitize::Config::RELAXED[:css], {
367
- :at_rules => ['import']
405
+ at_rules: ["import"]
368
406
  }))
369
407
  end
370
408
 
@@ -381,10 +419,10 @@ describe 'Sanitize::CSS' do
381
419
 
382
420
  describe "with a validation proc specified" do
383
421
  before do
384
- 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") }
385
423
 
386
424
  @scss = Sanitize::CSS.new(Sanitize::Config.merge(Sanitize::Config::RELAXED[:css], {
387
- :at_rules => ['import'], :import_url_validator => google_font_validator
425
+ at_rules: ["import"], import_url_validator: google_font_validator
388
426
  }))
389
427
  end
390
428
 
@@ -407,9 +445,9 @@ describe 'Sanitize::CSS' do
407
445
  @import url('https://nastysite.com/nasty_hax0r.css');
408
446
  ].strip
409
447
 
410
- _(@scss.stylesheet(css).strip).must_equal %[
448
+ _(@scss.stylesheet(css).strip).must_equal %(
411
449
  @import 'https://fonts.googleapis.com/css?family=Indie+Flower';
412
- ].strip
450
+ ).strip
413
451
  end
414
452
 
415
453
  it "should not allow a blank url" do
@@ -419,9 +457,9 @@ describe 'Sanitize::CSS' do
419
457
  @import url('');
420
458
  ].strip
421
459
 
422
- _(@scss.stylesheet(css).strip).must_equal %[
460
+ _(@scss.stylesheet(css).strip).must_equal %(
423
461
  @import 'https://fonts.googleapis.com/css?family=Indie+Flower';
424
- ].strip
462
+ ).strip
425
463
  end
426
464
  end
427
465
  end