secure_headers 3.9.0 → 4.0.0.alpha01

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of secure_headers might be problematic. Click here for more details.

Files changed (55) hide show
  1. checksums.yaml +5 -5
  2. data/.rspec +1 -0
  3. data/.rubocop.yml +3 -0
  4. data/.ruby-version +1 -1
  5. data/.travis.yml +8 -6
  6. data/CHANGELOG.md +2 -34
  7. data/CONTRIBUTING.md +1 -1
  8. data/Gemfile +7 -4
  9. data/Guardfile +1 -0
  10. data/README.md +4 -25
  11. data/Rakefile +22 -18
  12. data/docs/cookies.md +18 -5
  13. data/lib/secure_headers.rb +1 -2
  14. data/lib/secure_headers/configuration.rb +6 -16
  15. data/lib/secure_headers/hash_helper.rb +2 -1
  16. data/lib/secure_headers/headers/clear_site_data.rb +2 -1
  17. data/lib/secure_headers/headers/content_security_policy.rb +14 -60
  18. data/lib/secure_headers/headers/content_security_policy_config.rb +1 -1
  19. data/lib/secure_headers/headers/cookie.rb +22 -10
  20. data/lib/secure_headers/headers/policy_management.rb +57 -98
  21. data/lib/secure_headers/headers/public_key_pins.rb +4 -3
  22. data/lib/secure_headers/headers/referrer_policy.rb +1 -0
  23. data/lib/secure_headers/headers/strict_transport_security.rb +2 -1
  24. data/lib/secure_headers/headers/x_content_type_options.rb +1 -0
  25. data/lib/secure_headers/headers/x_download_options.rb +2 -1
  26. data/lib/secure_headers/headers/x_frame_options.rb +1 -0
  27. data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +2 -1
  28. data/lib/secure_headers/headers/x_xss_protection.rb +2 -1
  29. data/lib/secure_headers/middleware.rb +10 -9
  30. data/lib/secure_headers/railtie.rb +7 -6
  31. data/lib/secure_headers/utils/cookies_config.rb +17 -18
  32. data/lib/secure_headers/view_helper.rb +2 -1
  33. data/lib/tasks/tasks.rake +2 -1
  34. data/secure_headers.gemspec +13 -3
  35. data/spec/lib/secure_headers/configuration_spec.rb +9 -8
  36. data/spec/lib/secure_headers/headers/clear_site_data_spec.rb +2 -1
  37. data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +17 -53
  38. data/spec/lib/secure_headers/headers/cookie_spec.rb +58 -37
  39. data/spec/lib/secure_headers/headers/policy_management_spec.rb +20 -41
  40. data/spec/lib/secure_headers/headers/public_key_pins_spec.rb +7 -6
  41. data/spec/lib/secure_headers/headers/referrer_policy_spec.rb +4 -3
  42. data/spec/lib/secure_headers/headers/strict_transport_security_spec.rb +5 -4
  43. data/spec/lib/secure_headers/headers/x_content_type_options_spec.rb +2 -1
  44. data/spec/lib/secure_headers/headers/x_download_options_spec.rb +3 -2
  45. data/spec/lib/secure_headers/headers/x_frame_options_spec.rb +2 -1
  46. data/spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb +4 -3
  47. data/spec/lib/secure_headers/headers/x_xss_protection_spec.rb +4 -3
  48. data/spec/lib/secure_headers/middleware_spec.rb +18 -21
  49. data/spec/lib/secure_headers/view_helpers_spec.rb +5 -4
  50. data/spec/lib/secure_headers_spec.rb +92 -120
  51. data/spec/spec_helper.rb +9 -23
  52. data/upgrading-to-4-0.md +49 -0
  53. metadata +16 -11
  54. data/lib/secure_headers/headers/expect_certificate_transparency.rb +0 -70
  55. data/spec/lib/secure_headers/headers/expect_certificate_transparency_spec.rb +0 -42
@@ -1,4 +1,5 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
2
3
 
3
4
  module SecureHeaders
4
5
  describe PolicyManagement do
@@ -16,7 +17,7 @@ module SecureHeaders
16
17
  it "accepts all keys" do
17
18
  # (pulled from README)
18
19
  config = {
19
- # "meta" values. these will shaped the header, but the values are not included in the header.
20
+ # "meta" values. these will shape the header, but the values are not included in the header.
20
21
  report_only: true, # default: false
21
22
  preserve_schemes: true, # default: false. Schemes are removed from host sources to save bytes and discourage mixed content.
22
23
 
@@ -32,7 +33,6 @@ module SecureHeaders
32
33
  object_src: %w('self'),
33
34
  script_src: %w('self'),
34
35
  style_src: %w('unsafe-inline'),
35
- worker_src: %w(worker.com),
36
36
  base_uri: %w('self'),
37
37
  form_action: %w('self' github.com),
38
38
  frame_ancestors: %w('none'),
@@ -47,7 +47,13 @@ module SecureHeaders
47
47
 
48
48
  it "requires a :default_src value" do
49
49
  expect do
50
- ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(script_src: %('self')))
50
+ ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(script_src: %w('self')))
51
+ end.to raise_error(ContentSecurityPolicyConfigError)
52
+ end
53
+
54
+ it "requires a :script_src value" do
55
+ expect do
56
+ ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_src: %w('self')))
51
57
  end.to raise_error(ContentSecurityPolicyConfigError)
52
58
  end
53
59
 
@@ -96,58 +102,30 @@ module SecureHeaders
96
102
  # this is mostly to ensure people don't use the antiquated shorthands common in other configs
97
103
  it "performs light validation on source lists" do
98
104
  expect do
99
- ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_src: %w(self none inline eval)))
100
- end.to raise_error(ContentSecurityPolicyConfigError)
101
- end
102
-
103
- it "rejects anything not of the form allow-* as a sandbox value" do
104
- expect do
105
- ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(sandbox: ["steve"])))
106
- end.to raise_error(ContentSecurityPolicyConfigError)
107
- end
108
-
109
- it "accepts anything of the form allow-* as a sandbox value " do
110
- expect do
111
- ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(sandbox: ["allow-foo"])))
112
- end.to_not raise_error
113
- end
114
-
115
- it "accepts true as a sandbox policy" do
116
- expect do
117
- ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(sandbox: true)))
118
- end.to_not raise_error
119
- end
120
-
121
- it "rejects anything not of the form type/subtype as a plugin-type value" do
122
- expect do
123
- ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(plugin_types: ["steve"])))
105
+ ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_src: %w(self none inline eval), script_src: %w('self')))
124
106
  end.to raise_error(ContentSecurityPolicyConfigError)
125
107
  end
126
-
127
- it "accepts anything of the form type/subtype as a plugin-type value " do
128
- expect do
129
- ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(plugin_types: ["application/pdf"])))
130
- end.to_not raise_error
131
- end
132
108
  end
133
109
 
134
110
  describe "#combine_policies" do
135
111
  it "combines the default-src value with the override if the directive was unconfigured" do
136
112
  Configuration.default do |config|
137
113
  config.csp = {
138
- default_src: %w(https:)
114
+ default_src: %w(https:),
115
+ script_src: %w('self'),
139
116
  }
140
117
  end
141
- combined_config = ContentSecurityPolicy.combine_policies(Configuration.get.csp.to_h, script_src: %w(anothercdn.com))
118
+ combined_config = ContentSecurityPolicy.combine_policies(Configuration.get.csp.to_h, style_src: %w(anothercdn.com))
142
119
  csp = ContentSecurityPolicy.new(combined_config)
143
120
  expect(csp.name).to eq(ContentSecurityPolicyConfig::HEADER_NAME)
144
- expect(csp.value).to eq("default-src https:; script-src https: anothercdn.com")
121
+ expect(csp.value).to eq("default-src https:; script-src 'self'; style-src https: anothercdn.com")
145
122
  end
146
123
 
147
124
  it "combines directives where the original value is nil and the hash is frozen" do
148
125
  Configuration.default do |config|
149
126
  config.csp = {
150
127
  default_src: %w('self'),
128
+ script_src: %w('self'),
151
129
  report_only: false
152
130
  }.freeze
153
131
  end
@@ -161,6 +139,7 @@ module SecureHeaders
161
139
  Configuration.default do |config|
162
140
  config.csp = {
163
141
  default_src: %w('self'),
142
+ script_src: %w('self'),
164
143
  report_only: false
165
144
  }.freeze
166
145
  end
@@ -172,14 +151,13 @@ module SecureHeaders
172
151
  ContentSecurityPolicy::NON_FETCH_SOURCES.each do |directive|
173
152
  expect(combined_config[directive]).to eq(%w("http://example.org))
174
153
  end
175
-
176
- ContentSecurityPolicy.new(combined_config, USER_AGENTS[:firefox]).value
177
154
  end
178
155
 
179
156
  it "overrides the report_only flag" do
180
157
  Configuration.default do |config|
181
158
  config.csp = {
182
159
  default_src: %w('self'),
160
+ script_src: %w('self'),
183
161
  report_only: false
184
162
  }
185
163
  end
@@ -192,12 +170,13 @@ module SecureHeaders
192
170
  Configuration.default do |config|
193
171
  config.csp = {
194
172
  default_src: %w(https:),
173
+ script_src: %w('self'),
195
174
  block_all_mixed_content: false
196
175
  }
197
176
  end
198
177
  combined_config = ContentSecurityPolicy.combine_policies(Configuration.get.csp.to_h, block_all_mixed_content: true)
199
178
  csp = ContentSecurityPolicy.new(combined_config)
200
- expect(csp.value).to eq("default-src https:; block-all-mixed-content")
179
+ expect(csp.value).to eq("default-src https:; block-all-mixed-content; script-src 'self'")
201
180
  end
202
181
 
203
182
  it "raises an error if appending to a OPT_OUT policy" do
@@ -1,4 +1,5 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
2
3
 
3
4
  module SecureHeaders
4
5
  describe PublicKeyPins do
@@ -8,7 +9,7 @@ module SecureHeaders
8
9
  specify { expect(PublicKeyPins.new(max_age: 1234).value).to eq("max-age=1234") }
9
10
  specify { expect(PublicKeyPins.new(max_age: 1234).value).to eq("max-age=1234") }
10
11
  specify do
11
- config = { max_age: 1234, pins: [{ sha256: 'base64encodedpin1' }, { sha256: 'base64encodedpin2' }] }
12
+ config = { max_age: 1234, pins: [{ sha256: "base64encodedpin1" }, { sha256: "base64encodedpin2" }] }
12
13
  header_value = "max-age=1234; pin-sha256=\"base64encodedpin1\"; pin-sha256=\"base64encodedpin2\""
13
14
  expect(PublicKeyPins.new(config).value).to eq(header_value)
14
15
  end
@@ -16,19 +17,19 @@ module SecureHeaders
16
17
  context "with an invalid configuration" do
17
18
  it "raises an exception when max-age is not provided" do
18
19
  expect do
19
- PublicKeyPins.validate_config!(foo: 'bar')
20
+ PublicKeyPins.validate_config!(foo: "bar")
20
21
  end.to raise_error(PublicKeyPinsConfigError)
21
22
  end
22
23
 
23
24
  it "raises an exception with an invalid max-age" do
24
25
  expect do
25
- PublicKeyPins.validate_config!(max_age: 'abc123')
26
+ PublicKeyPins.validate_config!(max_age: "abc123")
26
27
  end.to raise_error(PublicKeyPinsConfigError)
27
28
  end
28
29
 
29
- it 'raises an exception with less than 2 pins' do
30
+ it "raises an exception with less than 2 pins" do
30
31
  expect do
31
- config = { max_age: 1234, pins: [{ sha256: 'base64encodedpin' }] }
32
+ config = { max_age: 1234, pins: [{ sha256: "base64encodedpin" }] }
32
33
  PublicKeyPins.validate_config!(config)
33
34
  end.to raise_error(PublicKeyPinsConfigError)
34
35
  end
@@ -1,9 +1,10 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
2
3
 
3
4
  module SecureHeaders
4
5
  describe ReferrerPolicy do
5
6
  specify { expect(ReferrerPolicy.make_header).to eq([ReferrerPolicy::HEADER_NAME, "origin-when-cross-origin"]) }
6
- specify { expect(ReferrerPolicy.make_header('no-referrer')).to eq([ReferrerPolicy::HEADER_NAME, "no-referrer"]) }
7
+ specify { expect(ReferrerPolicy.make_header("no-referrer")).to eq([ReferrerPolicy::HEADER_NAME, "no-referrer"]) }
7
8
 
8
9
  context "valid configuration values" do
9
10
  it "accepts 'no-referrer'" do
@@ -61,7 +62,7 @@ module SecureHeaders
61
62
  end
62
63
  end
63
64
 
64
- context 'invlaid configuration values' do
65
+ context "invlaid configuration values" do
65
66
  it "doesn't accept invalid values" do
66
67
  expect do
67
68
  ReferrerPolicy.validate_config!("open")
@@ -1,4 +1,5 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
2
3
 
3
4
  module SecureHeaders
4
5
  describe StrictTransportSecurity do
@@ -10,19 +11,19 @@ module SecureHeaders
10
11
  context "with a string argument" do
11
12
  it "raises an exception with an invalid max-age" do
12
13
  expect do
13
- StrictTransportSecurity.validate_config!('max-age=abc123')
14
+ StrictTransportSecurity.validate_config!("max-age=abc123")
14
15
  end.to raise_error(STSConfigError)
15
16
  end
16
17
 
17
18
  it "raises an exception if max-age is not supplied" do
18
19
  expect do
19
- StrictTransportSecurity.validate_config!('includeSubdomains')
20
+ StrictTransportSecurity.validate_config!("includeSubdomains")
20
21
  end.to raise_error(STSConfigError)
21
22
  end
22
23
 
23
24
  it "raises an exception with an invalid format" do
24
25
  expect do
25
- StrictTransportSecurity.validate_config!('max-age=123includeSubdomains')
26
+ StrictTransportSecurity.validate_config!("max-age=123includeSubdomains")
26
27
  end.to raise_error(STSConfigError)
27
28
  end
28
29
  end
@@ -1,4 +1,5 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
2
3
 
3
4
  module SecureHeaders
4
5
  describe XContentTypeOptions do
@@ -1,9 +1,10 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
2
3
 
3
4
  module SecureHeaders
4
5
  describe XDownloadOptions do
5
6
  specify { expect(XDownloadOptions.make_header).to eq([XDownloadOptions::HEADER_NAME, XDownloadOptions::DEFAULT_VALUE]) }
6
- specify { expect(XDownloadOptions.make_header('noopen')).to eq([XDownloadOptions::HEADER_NAME, 'noopen']) }
7
+ specify { expect(XDownloadOptions.make_header("noopen")).to eq([XDownloadOptions::HEADER_NAME, "noopen"]) }
7
8
 
8
9
  context "invalid configuration values" do
9
10
  it "accepts noopen" do
@@ -1,4 +1,5 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
2
3
 
3
4
  module SecureHeaders
4
5
  describe XFrameOptions do
@@ -1,9 +1,10 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
2
3
 
3
4
  module SecureHeaders
4
5
  describe XPermittedCrossDomainPolicies do
5
6
  specify { expect(XPermittedCrossDomainPolicies.make_header).to eq([XPermittedCrossDomainPolicies::HEADER_NAME, "none"]) }
6
- specify { expect(XPermittedCrossDomainPolicies.make_header('master-only')).to eq([XPermittedCrossDomainPolicies::HEADER_NAME, 'master-only']) }
7
+ specify { expect(XPermittedCrossDomainPolicies.make_header("master-only")).to eq([XPermittedCrossDomainPolicies::HEADER_NAME, "master-only"]) }
7
8
 
8
9
  context "valid configuration values" do
9
10
  it "accepts 'all'" do
@@ -36,7 +37,7 @@ module SecureHeaders
36
37
  end
37
38
  end
38
39
 
39
- context 'invlaid configuration values' do
40
+ context "invlaid configuration values" do
40
41
  it "doesn't accept invalid values" do
41
42
  expect do
42
43
  XPermittedCrossDomainPolicies.validate_config!("open")
@@ -1,9 +1,10 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
2
3
 
3
4
  module SecureHeaders
4
5
  describe XXssProtection do
5
6
  specify { expect(XXssProtection.make_header).to eq([XXssProtection::HEADER_NAME, XXssProtection::DEFAULT_VALUE]) }
6
- specify { expect(XXssProtection.make_header("1; mode=block; report=https://www.secure.com/reports")).to eq([XXssProtection::HEADER_NAME, '1; mode=block; report=https://www.secure.com/reports']) }
7
+ specify { expect(XXssProtection.make_header("1; mode=block; report=https://www.secure.com/reports")).to eq([XXssProtection::HEADER_NAME, "1; mode=block; report=https://www.secure.com/reports"]) }
7
8
 
8
9
  context "with invalid configuration" do
9
10
  it "should raise an error when providing a string that is not valid" do
@@ -19,7 +20,7 @@ module SecureHeaders
19
20
  context "when using a hash value" do
20
21
  it "should allow string values ('1' or '0' are the only valid strings)" do
21
22
  expect do
22
- XXssProtection.validate_config!('1')
23
+ XXssProtection.validate_config!("1")
23
24
  end.not_to raise_error
24
25
  end
25
26
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "spec_helper"
2
3
 
3
4
  module SecureHeaders
@@ -19,8 +20,8 @@ module SecureHeaders
19
20
  config.hpkp = {
20
21
  max_age: 10000000,
21
22
  pins: [
22
- {sha256: 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c'},
23
- {sha256: '73a2c64f9545172c1195efb6616ca5f7afd1df6f245407cafb90de3998a1c97f'}
23
+ {sha256: "b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"},
24
+ {sha256: "73a2c64f9545172c1195efb6616ca5f7afd1df6f245407cafb90de3998a1c97f"}
24
25
  ],
25
26
  report_uri: "https://#{report_host}/example-hpkp"
26
27
  }
@@ -54,68 +55,64 @@ module SecureHeaders
54
55
  expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match("example.org")
55
56
  end
56
57
 
57
- context "secure_cookies" do
58
+ context "cookies" do
58
59
  context "cookies should be flagged" do
59
60
  it "flags cookies as secure" do
60
- capture_warning do
61
- Configuration.default { |config| config.secure_cookies = true }
62
- end
61
+ Configuration.default { |config| config.cookies = {secure: true, httponly: OPT_OUT, samesite: OPT_OUT} }
63
62
  request = Rack::Request.new("HTTPS" => "on")
64
63
  _, env = cookie_middleware.call request.env
65
- expect(env['Set-Cookie']).to eq("foo=bar; secure")
64
+ expect(env["Set-Cookie"]).to eq("foo=bar; secure")
66
65
  end
67
66
  end
68
67
 
69
68
  context "cookies should not be flagged" do
70
69
  it "does not flags cookies as secure" do
71
- capture_warning do
72
- Configuration.default { |config| config.secure_cookies = false }
73
- end
70
+ Configuration.default { |config| config.cookies = {secure: OPT_OUT, httponly: OPT_OUT, samesite: OPT_OUT} }
74
71
  request = Rack::Request.new("HTTPS" => "on")
75
72
  _, env = cookie_middleware.call request.env
76
- expect(env['Set-Cookie']).to eq("foo=bar")
73
+ expect(env["Set-Cookie"]).to eq("foo=bar")
77
74
  end
78
75
  end
79
76
  end
80
77
 
81
78
  context "cookies" do
82
79
  it "flags cookies from configuration" do
83
- Configuration.default { |config| config.cookies = { secure: true, httponly: true } }
80
+ Configuration.default { |config| config.cookies = { secure: true, httponly: true, samesite: { lax: true} } }
84
81
  request = Rack::Request.new("HTTPS" => "on")
85
82
  _, env = cookie_middleware.call request.env
86
83
 
87
- expect(env['Set-Cookie']).to eq("foo=bar; secure; HttpOnly")
84
+ expect(env["Set-Cookie"]).to eq("foo=bar; secure; HttpOnly; SameSite=Lax")
88
85
  end
89
86
 
90
87
  it "flags cookies with a combination of SameSite configurations" do
91
88
  cookie_middleware = Middleware.new(lambda { |env| [200, env.merge("Set-Cookie" => ["_session=foobar", "_guest=true"]), "app"] })
92
89
 
93
- Configuration.default { |config| config.cookies = { samesite: { lax: { except: ["_session"] }, strict: { only: ["_session"] } } } }
90
+ Configuration.default { |config| config.cookies = { samesite: { lax: { except: ["_session"] }, strict: { only: ["_session"] } }, httponly: OPT_OUT, secure: OPT_OUT} }
94
91
  request = Rack::Request.new("HTTPS" => "on")
95
92
  _, env = cookie_middleware.call request.env
96
93
 
97
- expect(env['Set-Cookie']).to match("_session=foobar; SameSite=Strict")
98
- expect(env['Set-Cookie']).to match("_guest=true; SameSite=Lax")
94
+ expect(env["Set-Cookie"]).to match("_session=foobar; SameSite=Strict")
95
+ expect(env["Set-Cookie"]).to match("_guest=true; SameSite=Lax")
99
96
  end
100
97
 
101
98
  it "disables secure cookies for non-https requests" do
102
- Configuration.default { |config| config.cookies = { secure: true } }
99
+ Configuration.default { |config| config.cookies = { secure: true, httponly: OPT_OUT, samesite: OPT_OUT } }
103
100
 
104
101
  request = Rack::Request.new("HTTPS" => "off")
105
102
  _, env = cookie_middleware.call request.env
106
- expect(env['Set-Cookie']).to eq("foo=bar")
103
+ expect(env["Set-Cookie"]).to eq("foo=bar")
107
104
  end
108
105
 
109
106
  it "sets the secure cookie flag correctly on interleaved http/https requests" do
110
- Configuration.default { |config| config.cookies = { secure: true } }
107
+ Configuration.default { |config| config.cookies = { secure: true, httponly: OPT_OUT, samesite: OPT_OUT } }
111
108
 
112
109
  request = Rack::Request.new("HTTPS" => "off")
113
110
  _, env = cookie_middleware.call request.env
114
- expect(env['Set-Cookie']).to eq("foo=bar")
111
+ expect(env["Set-Cookie"]).to eq("foo=bar")
115
112
 
116
113
  request = Rack::Request.new("HTTPS" => "on")
117
114
  _, env = cookie_middleware.call request.env
118
- expect(env['Set-Cookie']).to eq("foo=bar; secure")
115
+ expect(env["Set-Cookie"]).to eq("foo=bar; secure")
119
116
  end
120
117
  end
121
118
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "spec_helper"
2
3
  require "erb"
3
4
 
@@ -58,7 +59,7 @@ TEMPLATE
58
59
  end
59
60
 
60
61
  if options.is_a?(Hash)
61
- options = options.map {|k,v| " #{k}=#{v}"}
62
+ options = options.map {|k, v| " #{k}=#{v}"}
62
63
  end
63
64
  "<#{type}#{options}>#{content}</#{type}>"
64
65
  end
@@ -82,9 +83,9 @@ module SecureHeaders
82
83
  before(:all) do
83
84
  Configuration.default do |config|
84
85
  config.csp = {
85
- :default_src => %w('self'),
86
- :script_src => %w('self'),
87
- :style_src => %w('self')
86
+ default_src: %w('self'),
87
+ script_src: %w('self'),
88
+ style_src: %w('self')
88
89
  }
89
90
  end
90
91
  end
@@ -1,4 +1,5 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
2
3
 
3
4
  module SecureHeaders
4
5
  describe SecureHeaders do
@@ -30,16 +31,16 @@ module SecureHeaders
30
31
  describe "#header_hash_for" do
31
32
  it "allows you to opt out of individual headers via API" do
32
33
  Configuration.default do |config|
33
- config.csp = { default_src: %w('self')}
34
+ config.csp = { default_src: %w('self'), script_src: %w('self')}
34
35
  config.csp_report_only = config.csp
35
36
  end
36
37
  SecureHeaders.opt_out_of_header(request, ContentSecurityPolicyConfig::CONFIG_KEY)
37
38
  SecureHeaders.opt_out_of_header(request, ContentSecurityPolicyReportOnlyConfig::CONFIG_KEY)
38
39
  SecureHeaders.opt_out_of_header(request, XContentTypeOptions::CONFIG_KEY)
39
40
  hash = SecureHeaders.header_hash_for(request)
40
- expect(hash['Content-Security-Policy-Report-Only']).to be_nil
41
- expect(hash['Content-Security-Policy']).to be_nil
42
- expect(hash['X-Content-Type-Options']).to be_nil
41
+ expect(hash["Content-Security-Policy-Report-Only"]).to be_nil
42
+ expect(hash["Content-Security-Policy"]).to be_nil
43
+ expect(hash["X-Content-Type-Options"]).to be_nil
43
44
  end
44
45
 
45
46
  it "Carries options over when using overrides" do
@@ -54,15 +55,15 @@ module SecureHeaders
54
55
 
55
56
  SecureHeaders.use_secure_headers_override(request, :api)
56
57
  hash = SecureHeaders.header_hash_for(request)
57
- expect(hash['X-Download-Options']).to be_nil
58
- expect(hash['X-Permitted-Cross-Domain-Policies']).to be_nil
59
- expect(hash['X-Frame-Options']).to be_nil
58
+ expect(hash["X-Download-Options"]).to be_nil
59
+ expect(hash["X-Permitted-Cross-Domain-Policies"]).to be_nil
60
+ expect(hash["X-Frame-Options"]).to be_nil
60
61
  end
61
62
 
62
63
  it "allows you to opt out entirely" do
63
64
  # configure the disabled-by-default headers to ensure they also do not get set
64
65
  Configuration.default do |config|
65
- config.csp = { :default_src => ["example.com"] }
66
+ config.csp = { default_src: ["example.com"], script_src: %w('self') }
66
67
  config.csp_report_only = config.csp
67
68
  config.hpkp = {
68
69
  report_only: false,
@@ -108,6 +109,7 @@ module SecureHeaders
108
109
  Configuration.default do |config|
109
110
  config.csp = {
110
111
  default_src: %w('self'),
112
+ script_src: %w('self'),
111
113
  child_src: %w('self')
112
114
  }
113
115
  end
@@ -144,10 +146,10 @@ module SecureHeaders
144
146
  config.hpkp = {
145
147
  max_age: 1_000_000,
146
148
  include_subdomains: true,
147
- report_uri: '//example.com/uri-directive',
149
+ report_uri: "//example.com/uri-directive",
148
150
  pins: [
149
- { sha256: 'abc' },
150
- { sha256: '123' }
151
+ { sha256: "abc" },
152
+ { sha256: "123" }
151
153
  ]
152
154
  }
153
155
  end
@@ -173,42 +175,32 @@ module SecureHeaders
173
175
  expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to eq("default-src 'self'; script-src mycdn.com 'unsafe-inline' anothercdn.com")
174
176
  end
175
177
 
176
- it "appends child-src to frame-src" do
178
+ it "child-src and frame-src must match" do
177
179
  Configuration.default do |config|
178
180
  config.csp = {
179
181
  default_src: %w('self'),
180
- frame_src: %w(frame_src.com)
182
+ frame_src: %w(frame_src.com),
183
+ script_src: %w('self')
181
184
  }
182
185
  end
183
186
 
184
187
  SecureHeaders.append_content_security_policy_directives(chrome_request, child_src: %w(child_src.com))
185
- hash = SecureHeaders.header_hash_for(chrome_request)
186
- expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to eq("default-src 'self'; child-src frame_src.com child_src.com")
187
- end
188
-
189
- it "appends frame-src to child-src" do
190
- Configuration.default do |config|
191
- config.csp = {
192
- default_src: %w('self'),
193
- child_src: %w(child_src.com)
194
- }
195
- end
196
188
 
197
- safari_request = Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:safari6]))
198
- SecureHeaders.append_content_security_policy_directives(safari_request, frame_src: %w(frame_src.com))
199
- hash = SecureHeaders.header_hash_for(safari_request)
200
- expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to eq("default-src 'self'; frame-src child_src.com frame_src.com")
189
+ expect {
190
+ SecureHeaders.header_hash_for(chrome_request)
191
+ }.to raise_error(ArgumentError)
201
192
  end
202
193
 
203
194
  it "supports named appends" do
204
195
  Configuration.default do |config|
205
196
  config.csp = {
206
- default_src: %w('self')
197
+ default_src: %w('self'),
198
+ script_src: %w('self')
207
199
  }
208
200
  end
209
201
 
210
202
  Configuration.named_append(:moar_default_sources) do |request|
211
- { default_src: %w(https:)}
203
+ { default_src: %w(https:), style_src: %w('self')}
212
204
  end
213
205
 
214
206
  Configuration.named_append(:how_about_a_script_src_too) do |request|
@@ -219,13 +211,14 @@ module SecureHeaders
219
211
  SecureHeaders.use_content_security_policy_named_append(request, :how_about_a_script_src_too)
220
212
  hash = SecureHeaders.header_hash_for(request)
221
213
 
222
- expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to eq("default-src 'self' https:; script-src 'self' https: 'unsafe-inline'")
214
+ expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to eq("default-src 'self' https:; script-src 'self' 'unsafe-inline'; style-src 'self'")
223
215
  end
224
216
 
225
217
  it "appends a nonce to a missing script-src value" do
226
218
  Configuration.default do |config|
227
219
  config.csp = {
228
- default_src: %w('self')
220
+ default_src: %w('self'),
221
+ script_src: %w('self')
229
222
  }
230
223
  end
231
224
 
@@ -237,7 +230,8 @@ module SecureHeaders
237
230
  it "appends a hash to a missing script-src value" do
238
231
  Configuration.default do |config|
239
232
  config.csp = {
240
- default_src: %w('self')
233
+ default_src: %w('self'),
234
+ script_src: %w('self')
241
235
  }
242
236
  end
243
237
 
@@ -249,24 +243,26 @@ module SecureHeaders
249
243
  it "overrides individual directives" do
250
244
  Configuration.default do |config|
251
245
  config.csp = {
252
- default_src: %w('self')
246
+ default_src: %w('self'),
247
+ script_src: %w('self')
253
248
  }
254
249
  end
255
250
  SecureHeaders.override_content_security_policy_directives(request, default_src: %w('none'))
256
251
  hash = SecureHeaders.header_hash_for(request)
257
- expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to eq("default-src 'none'")
252
+ expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to eq("default-src 'none'; script-src 'self'")
258
253
  end
259
254
 
260
255
  it "overrides non-existant directives" do
261
256
  Configuration.default do |config|
262
257
  config.csp = {
263
- default_src: %w(https:)
258
+ default_src: %w(https:),
259
+ script_src: %w('self')
264
260
  }
265
261
  end
266
262
  SecureHeaders.override_content_security_policy_directives(request, img_src: [ContentSecurityPolicy::DATA_PROTOCOL])
267
263
  hash = SecureHeaders.header_hash_for(request)
268
264
  expect(hash[ContentSecurityPolicyReportOnlyConfig::HEADER_NAME]).to be_nil
269
- expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to eq("default-src https:; img-src data:")
265
+ expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to eq("default-src https:; img-src data:; script-src 'self'")
270
266
  end
271
267
 
272
268
  it "does not append a nonce when the browser does not support it" do
@@ -301,7 +297,7 @@ module SecureHeaders
301
297
  SecureHeaders.content_security_policy_script_nonce(chrome_request)
302
298
 
303
299
  hash = SecureHeaders.header_hash_for(chrome_request)
304
- expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src mycdn.com 'nonce-#{nonce}'; style-src 'self'")
300
+ expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src mycdn.com 'nonce-#{nonce}'; style-src 'self'")
305
301
  end
306
302
 
307
303
  it "uses a nonce for safari 10+" do
@@ -315,25 +311,18 @@ module SecureHeaders
315
311
  safari_request = Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:safari10]))
316
312
  nonce = SecureHeaders.content_security_policy_script_nonce(safari_request)
317
313
  hash = SecureHeaders.header_hash_for(safari_request)
318
- expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src mycdn.com 'nonce-#{nonce}'")
314
+ expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src mycdn.com 'nonce-#{nonce}'")
319
315
  end
320
316
 
321
- it "supports the deprecated `report_only: true` format" do
322
- expect(Kernel).to receive(:warn).once
323
-
324
- Configuration.default do |config|
325
- config.csp = {
326
- default_src: %w('self'),
327
- report_only: true
328
- }
329
- end
330
-
331
- expect(Configuration.get.csp).to eq(OPT_OUT)
332
- expect(Configuration.get.csp_report_only).to be_a(ContentSecurityPolicyReportOnlyConfig)
333
-
334
- hash = SecureHeaders.header_hash_for(request)
335
- expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to be_nil
336
- expect(hash[ContentSecurityPolicyReportOnlyConfig::HEADER_NAME]).to eq("default-src 'self'")
317
+ it "does not support the deprecated `report_only: true` format" do
318
+ expect {
319
+ Configuration.default do |config|
320
+ config.csp = {
321
+ default_src: %w('self'),
322
+ report_only: true
323
+ }
324
+ end
325
+ }.to raise_error(ArgumentError)
337
326
  end
338
327
 
339
328
  it "Raises an error if csp_report_only is used with `report_only: false`" do
@@ -341,6 +330,7 @@ module SecureHeaders
341
330
  Configuration.default do |config|
342
331
  config.csp_report_only = {
343
332
  default_src: %w('self'),
333
+ script_src: %w('self'),
344
334
  report_only: false
345
335
  }
346
336
  end
@@ -351,7 +341,8 @@ module SecureHeaders
351
341
  before(:each) do
352
342
  Configuration.default do |config|
353
343
  config.csp = {
354
- default_src: %w('self')
344
+ default_src: %w('self'),
345
+ script_src: %w('self')
355
346
  }
356
347
  config.csp_report_only = config.csp
357
348
  end
@@ -360,155 +351,136 @@ module SecureHeaders
360
351
  it "sets identical values when the configs are the same" do
361
352
  Configuration.default do |config|
362
353
  config.csp = {
363
- default_src: %w('self')
354
+ default_src: %w('self'),
355
+ script_src: %w('self')
364
356
  }
365
357
  config.csp_report_only = {
366
- default_src: %w('self')
358
+ default_src: %w('self'),
359
+ script_src: %w('self')
367
360
  }
368
361
  end
369
362
 
370
363
  hash = SecureHeaders.header_hash_for(request)
371
- expect(hash['Content-Security-Policy']).to eq("default-src 'self'")
372
- expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'")
364
+ expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src 'self'")
365
+ expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'; script-src 'self'")
373
366
  end
374
367
 
375
368
  it "sets different headers when the configs are different" do
376
369
  Configuration.default do |config|
377
370
  config.csp = {
378
- default_src: %w('self')
371
+ default_src: %w('self'),
372
+ script_src: %w('self')
379
373
  }
380
- config.csp_report_only = config.csp.merge({script_src: %w('self')})
374
+ config.csp_report_only = config.csp.merge({script_src: %w(foo.com)})
381
375
  end
382
376
 
383
377
  hash = SecureHeaders.header_hash_for(request)
384
- expect(hash['Content-Security-Policy']).to eq("default-src 'self'")
385
- expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; script-src 'self'")
378
+ expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src 'self'")
379
+ expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'; script-src 'self' foo.com")
386
380
  end
387
381
 
388
382
  it "allows you to opt-out of enforced CSP" do
389
383
  Configuration.default do |config|
390
384
  config.csp = SecureHeaders::OPT_OUT
391
385
  config.csp_report_only = {
392
- default_src: %w('self')
393
- }
394
- end
395
-
396
- hash = SecureHeaders.header_hash_for(request)
397
- expect(hash['Content-Security-Policy']).to be_nil
398
- expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'")
399
- end
400
-
401
- it "opts-out of enforced CSP when only csp_report_only is set" do
402
- expect(Kernel).to receive(:warn).once
403
- Configuration.default do |config|
404
- config.csp_report_only = {
405
- default_src: %w('self')
406
- }
407
- end
408
-
409
- hash = SecureHeaders.header_hash_for(request)
410
- expect(hash['Content-Security-Policy']).to be_nil
411
- expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'")
412
- end
413
-
414
- it "allows you to set csp_report_only before csp" do
415
- expect(Kernel).to receive(:warn).once
416
- Configuration.default do |config|
417
- config.csp_report_only = {
418
- default_src: %w('self')
386
+ default_src: %w('self'),
387
+ script_src: %w('self')
419
388
  }
420
- config.csp = config.csp_report_only.merge({script_src: %w('unsafe-inline')})
421
389
  end
422
390
 
423
391
  hash = SecureHeaders.header_hash_for(request)
424
- expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src 'self' 'unsafe-inline'")
425
- expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'")
392
+ expect(hash["Content-Security-Policy"]).to be_nil
393
+ expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'; script-src 'self'")
426
394
  end
427
395
 
428
396
  it "allows appending to the enforced policy" do
429
397
  SecureHeaders.append_content_security_policy_directives(request, {script_src: %w(anothercdn.com)}, :enforced)
430
398
  hash = SecureHeaders.header_hash_for(request)
431
- expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src 'self' anothercdn.com")
432
- expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'")
399
+ expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src 'self' anothercdn.com")
400
+ expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'; script-src 'self'")
433
401
  end
434
402
 
435
403
  it "allows appending to the report only policy" do
436
404
  SecureHeaders.append_content_security_policy_directives(request, {script_src: %w(anothercdn.com)}, :report_only)
437
405
  hash = SecureHeaders.header_hash_for(request)
438
- expect(hash['Content-Security-Policy']).to eq("default-src 'self'")
439
- expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; script-src 'self' anothercdn.com")
406
+ expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src 'self'")
407
+ expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'; script-src 'self' anothercdn.com")
440
408
  end
441
409
 
442
410
  it "allows appending to both policies" do
443
411
  SecureHeaders.append_content_security_policy_directives(request, {script_src: %w(anothercdn.com)}, :both)
444
412
  hash = SecureHeaders.header_hash_for(request)
445
- expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src 'self' anothercdn.com")
446
- expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; script-src 'self' anothercdn.com")
413
+ expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src 'self' anothercdn.com")
414
+ expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'; script-src 'self' anothercdn.com")
447
415
  end
448
416
 
449
417
  it "allows overriding the enforced policy" do
450
418
  SecureHeaders.override_content_security_policy_directives(request, {script_src: %w(anothercdn.com)}, :enforced)
451
419
  hash = SecureHeaders.header_hash_for(request)
452
- expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src anothercdn.com")
453
- expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'")
420
+ expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src anothercdn.com")
421
+ expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'; script-src 'self'")
454
422
  end
455
423
 
456
424
  it "allows overriding the report only policy" do
457
425
  SecureHeaders.override_content_security_policy_directives(request, {script_src: %w(anothercdn.com)}, :report_only)
458
426
  hash = SecureHeaders.header_hash_for(request)
459
- expect(hash['Content-Security-Policy']).to eq("default-src 'self'")
460
- expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; script-src anothercdn.com")
427
+ expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src 'self'")
428
+ expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'; script-src anothercdn.com")
461
429
  end
462
430
 
463
431
  it "allows overriding both policies" do
464
432
  SecureHeaders.override_content_security_policy_directives(request, {script_src: %w(anothercdn.com)}, :both)
465
433
  hash = SecureHeaders.header_hash_for(request)
466
- expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src anothercdn.com")
467
- expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; script-src anothercdn.com")
434
+ expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src anothercdn.com")
435
+ expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'; script-src anothercdn.com")
468
436
  end
469
437
 
470
438
  context "when inferring which config to modify" do
471
439
  it "updates the enforced header when configured" do
472
440
  Configuration.default do |config|
473
441
  config.csp = {
474
- default_src: %w('self')
442
+ default_src: %w('self'),
443
+ script_src: %w('self')
475
444
  }
476
445
  end
477
446
  SecureHeaders.append_content_security_policy_directives(request, {script_src: %w(anothercdn.com)})
478
447
 
479
448
  hash = SecureHeaders.header_hash_for(request)
480
- expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src 'self' anothercdn.com")
481
- expect(hash['Content-Security-Policy-Report-Only']).to be_nil
449
+ expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src 'self' anothercdn.com")
450
+ expect(hash["Content-Security-Policy-Report-Only"]).to be_nil
482
451
  end
483
452
 
484
453
  it "updates the report only header when configured" do
485
454
  Configuration.default do |config|
486
455
  config.csp = OPT_OUT
487
456
  config.csp_report_only = {
488
- default_src: %w('self')
457
+ default_src: %w('self'),
458
+ script_src: %w('self')
489
459
  }
490
460
  end
491
461
  SecureHeaders.append_content_security_policy_directives(request, {script_src: %w(anothercdn.com)})
492
462
 
493
463
  hash = SecureHeaders.header_hash_for(request)
494
- expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; script-src 'self' anothercdn.com")
495
- expect(hash['Content-Security-Policy']).to be_nil
464
+ expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'; script-src 'self' anothercdn.com")
465
+ expect(hash["Content-Security-Policy"]).to be_nil
496
466
  end
497
467
 
498
468
  it "updates both headers if both are configured" do
499
469
  Configuration.default do |config|
500
470
  config.csp = {
501
- default_src: %w(enforced.com)
471
+ default_src: %w(enforced.com),
472
+ script_src: %w('self')
502
473
  }
503
474
  config.csp_report_only = {
504
- default_src: %w(reportonly.com)
475
+ default_src: %w(reportonly.com),
476
+ script_src: %w('self')
505
477
  }
506
478
  end
507
479
  SecureHeaders.append_content_security_policy_directives(request, {script_src: %w(anothercdn.com)})
508
480
 
509
481
  hash = SecureHeaders.header_hash_for(request)
510
- expect(hash['Content-Security-Policy']).to eq("default-src enforced.com; script-src enforced.com anothercdn.com")
511
- expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src reportonly.com; script-src reportonly.com anothercdn.com")
482
+ expect(hash["Content-Security-Policy"]).to eq("default-src enforced.com; script-src 'self' anothercdn.com")
483
+ expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src reportonly.com; script-src 'self' anothercdn.com")
512
484
  end
513
485
 
514
486
  end
@@ -520,7 +492,7 @@ module SecureHeaders
520
492
  it "validates your hsts config upon configuration" do
521
493
  expect do
522
494
  Configuration.default do |config|
523
- config.hsts = 'lol'
495
+ config.hsts = "lol"
524
496
  end
525
497
  end.to raise_error(STSConfigError)
526
498
  end
@@ -528,7 +500,7 @@ module SecureHeaders
528
500
  it "validates your csp config upon configuration" do
529
501
  expect do
530
502
  Configuration.default do |config|
531
- config.csp = { ContentSecurityPolicy::DEFAULT_SRC => '123456' }
503
+ config.csp = { ContentSecurityPolicy::DEFAULT_SRC => "123456" }
532
504
  end
533
505
  end.to raise_error(ContentSecurityPolicyConfigError)
534
506
  end
@@ -536,7 +508,7 @@ module SecureHeaders
536
508
  it "raises errors for unknown directives" do
537
509
  expect do
538
510
  Configuration.default do |config|
539
- config.csp = { made_up_directive: '123456' }
511
+ config.csp = { made_up_directive: "123456" }
540
512
  end
541
513
  end.to raise_error(ContentSecurityPolicyConfigError)
542
514
  end