secure_headers 3.4.1 → 3.5.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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/CHANGELOG.md +5 -0
- data/Gemfile +1 -0
- data/README.md +31 -7
- data/lib/secure_headers/configuration.rb +57 -36
- data/lib/secure_headers/headers/content_security_policy.rb +62 -36
- data/lib/secure_headers/headers/content_security_policy_config.rb +128 -0
- data/lib/secure_headers/headers/policy_management.rb +19 -18
- data/lib/secure_headers/view_helper.rb +8 -0
- data/lib/secure_headers.rb +122 -55
- data/secure_headers.gemspec +1 -1
- data/spec/lib/secure_headers/configuration_spec.rb +5 -5
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +2 -2
- data/spec/lib/secure_headers/headers/policy_management_spec.rb +25 -34
- data/spec/lib/secure_headers/middleware_spec.rb +13 -1
- data/spec/lib/secure_headers/view_helpers_spec.rb +19 -6
- data/spec/lib/secure_headers_spec.rb +250 -58
- data/spec/spec_helper.rb +3 -2
- metadata +3 -2
|
@@ -16,7 +16,7 @@ module SecureHeaders
|
|
|
16
16
|
|
|
17
17
|
it "raises a NotYetConfiguredError if trying to opt-out of unconfigured headers" do
|
|
18
18
|
expect do
|
|
19
|
-
SecureHeaders.opt_out_of_header(request,
|
|
19
|
+
SecureHeaders.opt_out_of_header(request, ContentSecurityPolicyConfig::CONFIG_KEY)
|
|
20
20
|
end.to raise_error(Configuration::NotYetConfiguredError)
|
|
21
21
|
end
|
|
22
22
|
|
|
@@ -29,8 +29,12 @@ module SecureHeaders
|
|
|
29
29
|
|
|
30
30
|
describe "#header_hash_for" do
|
|
31
31
|
it "allows you to opt out of individual headers via API" do
|
|
32
|
-
Configuration.default
|
|
33
|
-
|
|
32
|
+
Configuration.default do |config|
|
|
33
|
+
config.csp = { default_src: %w('self')}
|
|
34
|
+
config.csp_report_only = config.csp
|
|
35
|
+
end
|
|
36
|
+
SecureHeaders.opt_out_of_header(request, ContentSecurityPolicyConfig::CONFIG_KEY)
|
|
37
|
+
SecureHeaders.opt_out_of_header(request, ContentSecurityPolicyReportOnlyConfig::CONFIG_KEY)
|
|
34
38
|
SecureHeaders.opt_out_of_header(request, XContentTypeOptions::CONFIG_KEY)
|
|
35
39
|
hash = SecureHeaders.header_hash_for(request)
|
|
36
40
|
expect(hash['Content-Security-Policy-Report-Only']).to be_nil
|
|
@@ -56,7 +60,21 @@ module SecureHeaders
|
|
|
56
60
|
end
|
|
57
61
|
|
|
58
62
|
it "allows you to opt out entirely" do
|
|
59
|
-
|
|
63
|
+
# configure the disabled-by-default headers to ensure they also do not get set
|
|
64
|
+
Configuration.default do |config|
|
|
65
|
+
config.csp = { :default_src => ["example.com"] }
|
|
66
|
+
config.csp_report_only = config.csp
|
|
67
|
+
config.hpkp = {
|
|
68
|
+
report_only: false,
|
|
69
|
+
max_age: 10000000,
|
|
70
|
+
include_subdomains: true,
|
|
71
|
+
report_uri: "https://report-uri.io/example-hpkp",
|
|
72
|
+
pins: [
|
|
73
|
+
{sha256: "abc"},
|
|
74
|
+
{sha256: "123"}
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
end
|
|
60
78
|
SecureHeaders.opt_out_of_all_protection(request)
|
|
61
79
|
hash = SecureHeaders.header_hash_for(request)
|
|
62
80
|
ALL_HEADER_CLASSES.each do |klass|
|
|
@@ -82,7 +100,7 @@ module SecureHeaders
|
|
|
82
100
|
SecureHeaders.override_content_security_policy_directives(request, default_src: %w(https:), script_src: %w('self'))
|
|
83
101
|
|
|
84
102
|
hash = SecureHeaders.header_hash_for(request)
|
|
85
|
-
expect(hash[
|
|
103
|
+
expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to eq("default-src https:; script-src 'self'")
|
|
86
104
|
expect(hash[XFrameOptions::HEADER_NAME]).to eq(XFrameOptions::SAMEORIGIN)
|
|
87
105
|
end
|
|
88
106
|
|
|
@@ -96,14 +114,14 @@ module SecureHeaders
|
|
|
96
114
|
firefox_request = Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:firefox]))
|
|
97
115
|
|
|
98
116
|
# append an unsupported directive
|
|
99
|
-
SecureHeaders.override_content_security_policy_directives(firefox_request, plugin_types: %w(flash))
|
|
117
|
+
SecureHeaders.override_content_security_policy_directives(firefox_request, {plugin_types: %w(flash)})
|
|
100
118
|
# append a supported directive
|
|
101
|
-
SecureHeaders.override_content_security_policy_directives(firefox_request, script_src: %w('self'))
|
|
119
|
+
SecureHeaders.override_content_security_policy_directives(firefox_request, {script_src: %w('self')})
|
|
102
120
|
|
|
103
121
|
hash = SecureHeaders.header_hash_for(firefox_request)
|
|
104
122
|
|
|
105
123
|
# child-src is translated to frame-src
|
|
106
|
-
expect(hash[
|
|
124
|
+
expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to eq("default-src 'self'; frame-src 'self'; script-src 'self'")
|
|
107
125
|
end
|
|
108
126
|
|
|
109
127
|
it "produces a hash of headers with default config" do
|
|
@@ -152,7 +170,7 @@ module SecureHeaders
|
|
|
152
170
|
|
|
153
171
|
SecureHeaders.append_content_security_policy_directives(request, script_src: %w(anothercdn.com))
|
|
154
172
|
hash = SecureHeaders.header_hash_for(request)
|
|
155
|
-
expect(hash[
|
|
173
|
+
expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to eq("default-src 'self'; script-src mycdn.com 'unsafe-inline' anothercdn.com")
|
|
156
174
|
end
|
|
157
175
|
|
|
158
176
|
it "supports named appends" do
|
|
@@ -174,7 +192,7 @@ module SecureHeaders
|
|
|
174
192
|
SecureHeaders.use_content_security_policy_named_append(request, :how_about_a_script_src_too)
|
|
175
193
|
hash = SecureHeaders.header_hash_for(request)
|
|
176
194
|
|
|
177
|
-
expect(hash[
|
|
195
|
+
expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to eq("default-src 'self' https:; script-src 'self' https: 'unsafe-inline'")
|
|
178
196
|
end
|
|
179
197
|
|
|
180
198
|
it "appends a nonce to a missing script-src value" do
|
|
@@ -186,7 +204,7 @@ module SecureHeaders
|
|
|
186
204
|
|
|
187
205
|
SecureHeaders.content_security_policy_script_nonce(request) # should add the value to the header
|
|
188
206
|
hash = SecureHeaders.header_hash_for(chrome_request)
|
|
189
|
-
expect(hash[
|
|
207
|
+
expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to match /\Adefault-src 'self'; script-src 'self' 'nonce-.*'\z/
|
|
190
208
|
end
|
|
191
209
|
|
|
192
210
|
it "appends a hash to a missing script-src value" do
|
|
@@ -198,48 +216,7 @@ module SecureHeaders
|
|
|
198
216
|
|
|
199
217
|
SecureHeaders.append_content_security_policy_directives(request, script_src: %w('sha256-abc123'))
|
|
200
218
|
hash = SecureHeaders.header_hash_for(chrome_request)
|
|
201
|
-
expect(hash[
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
it "dups global configuration just once when overriding n times and only calls idempotent_additions? once" do
|
|
205
|
-
Configuration.default do |config|
|
|
206
|
-
config.csp = {
|
|
207
|
-
default_src: %w('self')
|
|
208
|
-
}
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
expect(CSP).to receive(:idempotent_additions?).once
|
|
212
|
-
|
|
213
|
-
# before an override occurs, the env is empty
|
|
214
|
-
expect(request.env[SECURE_HEADERS_CONFIG]).to be_nil
|
|
215
|
-
|
|
216
|
-
SecureHeaders.append_content_security_policy_directives(request, script_src: %w(anothercdn.com))
|
|
217
|
-
new_config = SecureHeaders.config_for(request)
|
|
218
|
-
expect(new_config).to_not be(Configuration.get)
|
|
219
|
-
|
|
220
|
-
SecureHeaders.override_content_security_policy_directives(request, script_src: %w(yet.anothercdn.com))
|
|
221
|
-
current_config = SecureHeaders.config_for(request)
|
|
222
|
-
expect(current_config).to be(new_config)
|
|
223
|
-
|
|
224
|
-
SecureHeaders.header_hash_for(request)
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
it "doesn't allow you to muck with csp configs when a dynamic policy is in use" do
|
|
228
|
-
default_config = Configuration.default
|
|
229
|
-
expect { default_config.csp = {} }.to raise_error(NoMethodError)
|
|
230
|
-
|
|
231
|
-
# config is frozen
|
|
232
|
-
expect { default_config.send(:csp=, {}) }.to raise_error(RuntimeError)
|
|
233
|
-
|
|
234
|
-
SecureHeaders.append_content_security_policy_directives(request, script_src: %w(anothercdn.com))
|
|
235
|
-
new_config = SecureHeaders.config_for(request)
|
|
236
|
-
expect { new_config.send(:csp=, {}) }.to raise_error(Configuration::IllegalPolicyModificationError)
|
|
237
|
-
|
|
238
|
-
expect do
|
|
239
|
-
new_config.instance_eval do
|
|
240
|
-
new_config.csp = {}
|
|
241
|
-
end
|
|
242
|
-
end.to raise_error(Configuration::IllegalPolicyModificationError)
|
|
219
|
+
expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to match /\Adefault-src 'self'; script-src 'self' 'sha256-abc123'\z/
|
|
243
220
|
end
|
|
244
221
|
|
|
245
222
|
it "overrides individual directives" do
|
|
@@ -250,14 +227,19 @@ module SecureHeaders
|
|
|
250
227
|
end
|
|
251
228
|
SecureHeaders.override_content_security_policy_directives(request, default_src: %w('none'))
|
|
252
229
|
hash = SecureHeaders.header_hash_for(request)
|
|
253
|
-
expect(hash[
|
|
230
|
+
expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to eq("default-src 'none'")
|
|
254
231
|
end
|
|
255
232
|
|
|
256
233
|
it "overrides non-existant directives" do
|
|
257
|
-
Configuration.default
|
|
234
|
+
Configuration.default do |config|
|
|
235
|
+
config.csp = {
|
|
236
|
+
default_src: %w(https:)
|
|
237
|
+
}
|
|
238
|
+
end
|
|
258
239
|
SecureHeaders.override_content_security_policy_directives(request, img_src: [ContentSecurityPolicy::DATA_PROTOCOL])
|
|
259
240
|
hash = SecureHeaders.header_hash_for(request)
|
|
260
|
-
expect(hash[
|
|
241
|
+
expect(hash[ContentSecurityPolicyReportOnlyConfig::HEADER_NAME]).to be_nil
|
|
242
|
+
expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to eq("default-src https:; img-src data:")
|
|
261
243
|
end
|
|
262
244
|
|
|
263
245
|
it "does not append a nonce when the browser does not support it" do
|
|
@@ -272,7 +254,7 @@ module SecureHeaders
|
|
|
272
254
|
safari_request = Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:safari5]))
|
|
273
255
|
nonce = SecureHeaders.content_security_policy_script_nonce(safari_request)
|
|
274
256
|
hash = SecureHeaders.header_hash_for(safari_request)
|
|
275
|
-
expect(hash[
|
|
257
|
+
expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to eq("default-src 'self'; script-src mycdn.com 'unsafe-inline'; style-src 'self'")
|
|
276
258
|
end
|
|
277
259
|
|
|
278
260
|
it "appends a nonce to the script-src when used" do
|
|
@@ -294,6 +276,216 @@ module SecureHeaders
|
|
|
294
276
|
hash = SecureHeaders.header_hash_for(chrome_request)
|
|
295
277
|
expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src mycdn.com 'nonce-#{nonce}'; style-src 'self'")
|
|
296
278
|
end
|
|
279
|
+
|
|
280
|
+
it "uses a nonce for safari 10+" do
|
|
281
|
+
Configuration.default do |config|
|
|
282
|
+
config.csp = {
|
|
283
|
+
default_src: %w('self'),
|
|
284
|
+
script_src: %w(mycdn.com)
|
|
285
|
+
}
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
safari_request = Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:safari10]))
|
|
289
|
+
nonce = SecureHeaders.content_security_policy_script_nonce(safari_request)
|
|
290
|
+
hash = SecureHeaders.header_hash_for(safari_request)
|
|
291
|
+
expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src mycdn.com 'nonce-#{nonce}'")
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
it "supports the deprecated `report_only: true` format" do
|
|
295
|
+
expect(Kernel).to receive(:warn).once
|
|
296
|
+
|
|
297
|
+
Configuration.default do |config|
|
|
298
|
+
config.csp = {
|
|
299
|
+
default_src: %w('self'),
|
|
300
|
+
report_only: true
|
|
301
|
+
}
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
expect(Configuration.get.csp).to eq(OPT_OUT)
|
|
305
|
+
expect(Configuration.get.csp_report_only).to be_a(ContentSecurityPolicyReportOnlyConfig)
|
|
306
|
+
|
|
307
|
+
hash = SecureHeaders.header_hash_for(request)
|
|
308
|
+
expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to be_nil
|
|
309
|
+
expect(hash[ContentSecurityPolicyReportOnlyConfig::HEADER_NAME]).to eq("default-src 'self'")
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
it "Raises an error if csp_report_only is used with `report_only: false`" do
|
|
313
|
+
expect do
|
|
314
|
+
Configuration.default do |config|
|
|
315
|
+
config.csp_report_only = {
|
|
316
|
+
default_src: %w('self'),
|
|
317
|
+
report_only: false
|
|
318
|
+
}
|
|
319
|
+
end
|
|
320
|
+
end.to raise_error(ContentSecurityPolicyConfigError)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
context "setting two headers" do
|
|
324
|
+
before(:each) do
|
|
325
|
+
Configuration.default do |config|
|
|
326
|
+
config.csp = {
|
|
327
|
+
default_src: %w('self')
|
|
328
|
+
}
|
|
329
|
+
config.csp_report_only = config.csp
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
it "sets identical values when the configs are the same" do
|
|
334
|
+
Configuration.default do |config|
|
|
335
|
+
config.csp = {
|
|
336
|
+
default_src: %w('self')
|
|
337
|
+
}
|
|
338
|
+
config.csp_report_only = {
|
|
339
|
+
default_src: %w('self')
|
|
340
|
+
}
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
hash = SecureHeaders.header_hash_for(request)
|
|
344
|
+
expect(hash['Content-Security-Policy']).to eq("default-src 'self'")
|
|
345
|
+
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'")
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
it "sets different headers when the configs are different" do
|
|
349
|
+
Configuration.default do |config|
|
|
350
|
+
config.csp = {
|
|
351
|
+
default_src: %w('self')
|
|
352
|
+
}
|
|
353
|
+
config.csp_report_only = config.csp.merge({script_src: %w('self')})
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
hash = SecureHeaders.header_hash_for(request)
|
|
357
|
+
expect(hash['Content-Security-Policy']).to eq("default-src 'self'")
|
|
358
|
+
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; script-src 'self'")
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
it "allows you to opt-out of enforced CSP" do
|
|
362
|
+
Configuration.default do |config|
|
|
363
|
+
config.csp = SecureHeaders::OPT_OUT
|
|
364
|
+
config.csp_report_only = {
|
|
365
|
+
default_src: %w('self')
|
|
366
|
+
}
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
hash = SecureHeaders.header_hash_for(request)
|
|
370
|
+
expect(hash['Content-Security-Policy']).to be_nil
|
|
371
|
+
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'")
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
it "opts-out of enforced CSP when only csp_report_only is set" do
|
|
375
|
+
expect(Kernel).to receive(:warn).once
|
|
376
|
+
Configuration.default do |config|
|
|
377
|
+
config.csp_report_only = {
|
|
378
|
+
default_src: %w('self')
|
|
379
|
+
}
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
hash = SecureHeaders.header_hash_for(request)
|
|
383
|
+
expect(hash['Content-Security-Policy']).to be_nil
|
|
384
|
+
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'")
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
it "allows you to set csp_report_only before csp" do
|
|
388
|
+
expect(Kernel).to receive(:warn).once
|
|
389
|
+
Configuration.default do |config|
|
|
390
|
+
config.csp_report_only = {
|
|
391
|
+
default_src: %w('self')
|
|
392
|
+
}
|
|
393
|
+
config.csp = config.csp_report_only.merge({script_src: %w('unsafe-inline')})
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
hash = SecureHeaders.header_hash_for(request)
|
|
397
|
+
expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src 'self' 'unsafe-inline'")
|
|
398
|
+
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'")
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
it "allows appending to the enforced policy" do
|
|
402
|
+
SecureHeaders.append_content_security_policy_directives(request, {script_src: %w(anothercdn.com)}, :enforced)
|
|
403
|
+
hash = SecureHeaders.header_hash_for(request)
|
|
404
|
+
expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src 'self' anothercdn.com")
|
|
405
|
+
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'")
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
it "allows appending to the report only policy" do
|
|
409
|
+
SecureHeaders.append_content_security_policy_directives(request, {script_src: %w(anothercdn.com)}, :report_only)
|
|
410
|
+
hash = SecureHeaders.header_hash_for(request)
|
|
411
|
+
expect(hash['Content-Security-Policy']).to eq("default-src 'self'")
|
|
412
|
+
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; script-src 'self' anothercdn.com")
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
it "allows appending to both policies" do
|
|
416
|
+
SecureHeaders.append_content_security_policy_directives(request, {script_src: %w(anothercdn.com)}, :both)
|
|
417
|
+
hash = SecureHeaders.header_hash_for(request)
|
|
418
|
+
expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src 'self' anothercdn.com")
|
|
419
|
+
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; script-src 'self' anothercdn.com")
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
it "allows overriding the enforced policy" do
|
|
423
|
+
SecureHeaders.override_content_security_policy_directives(request, {script_src: %w(anothercdn.com)}, :enforced)
|
|
424
|
+
hash = SecureHeaders.header_hash_for(request)
|
|
425
|
+
expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src anothercdn.com")
|
|
426
|
+
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'")
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
it "allows overriding the report only policy" do
|
|
430
|
+
SecureHeaders.override_content_security_policy_directives(request, {script_src: %w(anothercdn.com)}, :report_only)
|
|
431
|
+
hash = SecureHeaders.header_hash_for(request)
|
|
432
|
+
expect(hash['Content-Security-Policy']).to eq("default-src 'self'")
|
|
433
|
+
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; script-src anothercdn.com")
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
it "allows overriding both policies" do
|
|
437
|
+
SecureHeaders.override_content_security_policy_directives(request, {script_src: %w(anothercdn.com)}, :both)
|
|
438
|
+
hash = SecureHeaders.header_hash_for(request)
|
|
439
|
+
expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src anothercdn.com")
|
|
440
|
+
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; script-src anothercdn.com")
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
context "when inferring which config to modify" do
|
|
444
|
+
it "updates the enforced header when configured" do
|
|
445
|
+
Configuration.default do |config|
|
|
446
|
+
config.csp = {
|
|
447
|
+
default_src: %w('self')
|
|
448
|
+
}
|
|
449
|
+
end
|
|
450
|
+
SecureHeaders.append_content_security_policy_directives(request, {script_src: %w(anothercdn.com)})
|
|
451
|
+
|
|
452
|
+
hash = SecureHeaders.header_hash_for(request)
|
|
453
|
+
expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src 'self' anothercdn.com")
|
|
454
|
+
expect(hash['Content-Security-Policy-Report-Only']).to be_nil
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
it "updates the report only header when configured" do
|
|
458
|
+
Configuration.default do |config|
|
|
459
|
+
config.csp = OPT_OUT
|
|
460
|
+
config.csp_report_only = {
|
|
461
|
+
default_src: %w('self')
|
|
462
|
+
}
|
|
463
|
+
end
|
|
464
|
+
SecureHeaders.append_content_security_policy_directives(request, {script_src: %w(anothercdn.com)})
|
|
465
|
+
|
|
466
|
+
hash = SecureHeaders.header_hash_for(request)
|
|
467
|
+
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; script-src 'self' anothercdn.com")
|
|
468
|
+
expect(hash['Content-Security-Policy']).to be_nil
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
it "updates both headers if both are configured" do
|
|
472
|
+
Configuration.default do |config|
|
|
473
|
+
config.csp = {
|
|
474
|
+
default_src: %w(enforced.com)
|
|
475
|
+
}
|
|
476
|
+
config.csp_report_only = {
|
|
477
|
+
default_src: %w(reportonly.com)
|
|
478
|
+
}
|
|
479
|
+
end
|
|
480
|
+
SecureHeaders.append_content_security_policy_directives(request, {script_src: %w(anothercdn.com)})
|
|
481
|
+
|
|
482
|
+
hash = SecureHeaders.header_hash_for(request)
|
|
483
|
+
expect(hash['Content-Security-Policy']).to eq("default-src enforced.com; script-src enforced.com anothercdn.com")
|
|
484
|
+
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src reportonly.com; script-src reportonly.com anothercdn.com")
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
end
|
|
488
|
+
end
|
|
297
489
|
end
|
|
298
490
|
end
|
|
299
491
|
|
|
@@ -309,7 +501,7 @@ module SecureHeaders
|
|
|
309
501
|
it "validates your csp config upon configuration" do
|
|
310
502
|
expect do
|
|
311
503
|
Configuration.default do |config|
|
|
312
|
-
config.csp = {
|
|
504
|
+
config.csp = { ContentSecurityPolicy::DEFAULT_SRC => '123456' }
|
|
313
505
|
end
|
|
314
506
|
end.to raise_error(ContentSecurityPolicyConfigError)
|
|
315
507
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -21,11 +21,12 @@ USER_AGENTS = {
|
|
|
21
21
|
ios6: "Mozilla/5.0 (iPhone; CPU iPhone OS 614 like Mac OS X) AppleWebKit/536.26 (KHTML like Gecko) Version/6.0 Mobile/10B350 Safari/8536.25",
|
|
22
22
|
safari5: "Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko ) Version/5.1 Mobile/9B176 Safari/7534.48.3",
|
|
23
23
|
safari5_1: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10",
|
|
24
|
-
safari6: "Mozilla/5.0 (Macintosh; Intel Mac OS X 1084) AppleWebKit/536.30.1 (KHTML like Gecko) Version/6.0.5 Safari/536.30.1"
|
|
24
|
+
safari6: "Mozilla/5.0 (Macintosh; Intel Mac OS X 1084) AppleWebKit/536.30.1 (KHTML like Gecko) Version/6.0.5 Safari/536.30.1",
|
|
25
|
+
safari10: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/602.2.11 (KHTML, like Gecko) Version/10.0.1 Safari/602.2.11"
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
def expect_default_values(hash)
|
|
28
|
-
expect(hash[SecureHeaders::
|
|
29
|
+
expect(hash[SecureHeaders::ContentSecurityPolicyConfig::HEADER_NAME]).to eq("default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'")
|
|
29
30
|
expect(hash[SecureHeaders::XFrameOptions::HEADER_NAME]).to eq(SecureHeaders::XFrameOptions::DEFAULT_VALUE)
|
|
30
31
|
expect(hash[SecureHeaders::XDownloadOptions::HEADER_NAME]).to eq(SecureHeaders::XDownloadOptions::DEFAULT_VALUE)
|
|
31
32
|
expect(hash[SecureHeaders::StrictTransportSecurity::HEADER_NAME]).to eq(SecureHeaders::StrictTransportSecurity::DEFAULT_VALUE)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: secure_headers
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Neil Matatall
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2016-
|
|
11
|
+
date: 2016-10-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rake
|
|
@@ -62,6 +62,7 @@ files:
|
|
|
62
62
|
- lib/secure_headers/configuration.rb
|
|
63
63
|
- lib/secure_headers/hash_helper.rb
|
|
64
64
|
- lib/secure_headers/headers/content_security_policy.rb
|
|
65
|
+
- lib/secure_headers/headers/content_security_policy_config.rb
|
|
65
66
|
- lib/secure_headers/headers/cookie.rb
|
|
66
67
|
- lib/secure_headers/headers/policy_management.rb
|
|
67
68
|
- lib/secure_headers/headers/public_key_pins.rb
|