sanitize 6.0.2 → 7.0.0

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