sanitize 6.1.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')",
@@ -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