secure_headers 5.2.0 → 6.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 (29) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +8 -4
  3. data/CHANGELOG.md +3 -7
  4. data/Gemfile +1 -1
  5. data/docs/upgrading-to-6-0.md +50 -0
  6. data/lib/secure_headers.rb +14 -76
  7. data/lib/secure_headers/configuration.rb +114 -164
  8. data/lib/secure_headers/headers/clear_site_data.rb +1 -3
  9. data/lib/secure_headers/headers/content_security_policy.rb +4 -17
  10. data/lib/secure_headers/headers/content_security_policy_config.rb +3 -13
  11. data/lib/secure_headers/headers/expect_certificate_transparency.rb +2 -3
  12. data/lib/secure_headers/headers/policy_management.rb +12 -11
  13. data/lib/secure_headers/headers/public_key_pins.rb +2 -3
  14. data/lib/secure_headers/headers/referrer_policy.rb +2 -2
  15. data/lib/secure_headers/headers/strict_transport_security.rb +2 -2
  16. data/lib/secure_headers/headers/x_content_type_options.rb +2 -2
  17. data/lib/secure_headers/headers/x_download_options.rb +2 -2
  18. data/lib/secure_headers/headers/x_frame_options.rb +1 -2
  19. data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +2 -2
  20. data/lib/secure_headers/headers/x_xss_protection.rb +3 -3
  21. data/secure_headers.gemspec +1 -1
  22. data/spec/lib/secure_headers/configuration_spec.rb +15 -70
  23. data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +12 -22
  24. data/spec/lib/secure_headers/headers/policy_management_spec.rb +33 -7
  25. data/spec/lib/secure_headers/middleware_spec.rb +7 -1
  26. data/spec/lib/secure_headers/view_helpers_spec.rb +1 -0
  27. data/spec/lib/secure_headers_spec.rb +39 -40
  28. data/spec/spec_helper.rb +7 -3
  29. metadata +5 -4
@@ -28,16 +28,6 @@ module SecureHeaders
28
28
  expect(ContentSecurityPolicy.new.value).to eq("default-src https:; form-action 'self'; img-src https: data: 'self'; object-src 'none'; script-src https:; style-src 'self' 'unsafe-inline' https:")
29
29
  end
30
30
 
31
- it "deprecates and escapes semicolons in directive source lists" do
32
- expect(Kernel).to receive(:warn).with(%(frame_ancestors contains a ; in "google.com;script-src *;.;" which will raise an error in future versions. It has been replaced with a blank space.))
33
- expect(ContentSecurityPolicy.new(frame_ancestors: %w(https://google.com;script-src https://*;.;)).value).to eq("frame-ancestors google.com script-src * .")
34
- end
35
-
36
- it "deprecates and escapes semicolons in directive source lists" do
37
- expect(Kernel).to receive(:warn).with(%(frame_ancestors contains a \n in "\\nfoo.com\\nhacked" which will raise an error in future versions. It has been replaced with a blank space.))
38
- expect(ContentSecurityPolicy.new(frame_ancestors: ["\nfoo.com\nhacked"]).value).to eq("frame-ancestors foo.com hacked")
39
- end
40
-
41
31
  it "discards 'none' values if any other source expressions are present" do
42
32
  csp = ContentSecurityPolicy.new(default_opts.merge(child_src: %w('self' 'none')))
43
33
  expect(csp.value).not_to include("'none'")
@@ -134,7 +124,7 @@ module SecureHeaders
134
124
 
135
125
  it "supports strict-dynamic" do
136
126
  csp = ContentSecurityPolicy.new({default_src: %w('self'), script_src: [ContentSecurityPolicy::STRICT_DYNAMIC], script_nonce: 123456}, USER_AGENTS[:chrome])
137
- expect(csp.value).to eq("default-src 'self'; script-src 'strict-dynamic' 'nonce-123456'")
127
+ expect(csp.value).to eq("default-src 'self'; script-src 'strict-dynamic' 'nonce-123456' 'unsafe-inline'")
138
128
  end
139
129
 
140
130
  context "browser sniffing" do
@@ -153,44 +143,44 @@ module SecureHeaders
153
143
 
154
144
  it "does not filter any directives for Chrome" do
155
145
  policy = ContentSecurityPolicy.new(complex_opts, USER_AGENTS[:chrome])
156
- expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; block-all-mixed-content; child-src child-src.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; img-src img-src.com; manifest-src manifest-src.com; media-src media-src.com; object-src object-src.com; plugin-types application/pdf; sandbox allow-forms; script-src script-src.com 'nonce-123456'; style-src style-src.com; upgrade-insecure-requests; worker-src worker-src.com; report-uri report-uri.com")
146
+ expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; block-all-mixed-content; child-src child-src.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; img-src img-src.com; manifest-src manifest-src.com; media-src media-src.com; object-src object-src.com; plugin-types application/pdf; sandbox allow-forms; script-src script-src.com 'nonce-123456' 'unsafe-inline'; style-src style-src.com; upgrade-insecure-requests; worker-src worker-src.com; report-uri report-uri.com")
157
147
  end
158
148
 
159
149
  it "does not filter any directives for Opera" do
160
150
  policy = ContentSecurityPolicy.new(complex_opts, USER_AGENTS[:opera])
161
- expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; block-all-mixed-content; child-src child-src.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; img-src img-src.com; manifest-src manifest-src.com; media-src media-src.com; object-src object-src.com; plugin-types application/pdf; sandbox allow-forms; script-src script-src.com 'nonce-123456'; style-src style-src.com; upgrade-insecure-requests; worker-src worker-src.com; report-uri report-uri.com")
151
+ expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; block-all-mixed-content; child-src child-src.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; img-src img-src.com; manifest-src manifest-src.com; media-src media-src.com; object-src object-src.com; plugin-types application/pdf; sandbox allow-forms; script-src script-src.com 'nonce-123456' 'unsafe-inline'; style-src style-src.com; upgrade-insecure-requests; worker-src worker-src.com; report-uri report-uri.com")
162
152
  end
163
153
 
164
154
  it "filters blocked-all-mixed-content, child-src, and plugin-types for firefox" do
165
155
  policy = ContentSecurityPolicy.new(complex_opts, USER_AGENTS[:firefox])
166
- expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; frame-src child-src.com; img-src img-src.com; manifest-src manifest-src.com; media-src media-src.com; object-src object-src.com; sandbox allow-forms; script-src script-src.com 'nonce-123456'; style-src style-src.com; upgrade-insecure-requests; report-uri report-uri.com")
156
+ expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; frame-src child-src.com; img-src img-src.com; manifest-src manifest-src.com; media-src media-src.com; object-src object-src.com; sandbox allow-forms; script-src script-src.com 'nonce-123456' 'unsafe-inline'; style-src style-src.com; upgrade-insecure-requests; report-uri report-uri.com")
167
157
  end
168
158
 
169
159
  it "filters blocked-all-mixed-content, frame-src, and plugin-types for firefox 46 and higher" do
170
160
  policy = ContentSecurityPolicy.new(complex_opts, USER_AGENTS[:firefox46])
171
- expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; child-src child-src.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; img-src img-src.com; manifest-src manifest-src.com; media-src media-src.com; object-src object-src.com; sandbox allow-forms; script-src script-src.com 'nonce-123456'; style-src style-src.com; upgrade-insecure-requests; report-uri report-uri.com")
161
+ expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; child-src child-src.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; img-src img-src.com; manifest-src manifest-src.com; media-src media-src.com; object-src object-src.com; sandbox allow-forms; script-src script-src.com 'nonce-123456' 'unsafe-inline'; style-src style-src.com; upgrade-insecure-requests; report-uri report-uri.com")
172
162
  end
173
163
 
174
- it "child-src value is copied to frame-src, adds 'unsafe-inline', filters base-uri, blocked-all-mixed-content, upgrade-insecure-requests, child-src, form-action, frame-ancestors, nonce sources, hash sources, and plugin-types for Edge" do
164
+ it "child-src value is copied to frame-src, adds 'unsafe-inline', filters base-uri, blocked-all-mixed-content, upgrade-insecure-requests, child-src, form-action, frame-ancestors, hash sources, and plugin-types for Edge" do
175
165
  policy = ContentSecurityPolicy.new(complex_opts, USER_AGENTS[:edge])
176
- expect(policy.value).to eq("default-src default-src.com; connect-src connect-src.com; font-src font-src.com; frame-src child-src.com; img-src img-src.com; media-src media-src.com; object-src object-src.com; sandbox allow-forms; script-src script-src.com 'unsafe-inline'; style-src style-src.com; report-uri report-uri.com")
166
+ expect(policy.value).to eq("default-src default-src.com; connect-src connect-src.com; font-src font-src.com; frame-src child-src.com; img-src img-src.com; media-src media-src.com; object-src object-src.com; sandbox allow-forms; script-src script-src.com 'nonce-123456' 'unsafe-inline'; style-src style-src.com; report-uri report-uri.com")
177
167
  end
178
168
 
179
- it "child-src value is copied to frame-src, adds 'unsafe-inline', filters base-uri, blocked-all-mixed-content, upgrade-insecure-requests, child-src, form-action, frame-ancestors, nonce sources, hash sources, and plugin-types for safari" do
169
+ it "child-src value is copied to frame-src, adds 'unsafe-inline', filters base-uri, blocked-all-mixed-content, upgrade-insecure-requests, child-src, form-action, frame-ancestors, hash sources, and plugin-types for safari" do
180
170
  policy = ContentSecurityPolicy.new(complex_opts, USER_AGENTS[:safari6])
181
- expect(policy.value).to eq("default-src default-src.com; connect-src connect-src.com; font-src font-src.com; frame-src child-src.com; img-src img-src.com; media-src media-src.com; object-src object-src.com; sandbox allow-forms; script-src script-src.com 'unsafe-inline'; style-src style-src.com; report-uri report-uri.com")
171
+ expect(policy.value).to eq("default-src default-src.com; connect-src connect-src.com; font-src font-src.com; frame-src child-src.com; img-src img-src.com; media-src media-src.com; object-src object-src.com; sandbox allow-forms; script-src script-src.com 'nonce-123456' 'unsafe-inline'; style-src style-src.com; report-uri report-uri.com")
182
172
  end
183
173
 
184
- it "adds 'unsafe-inline', filters blocked-all-mixed-content, upgrade-insecure-requests, nonce sources, and hash sources for safari 10 and higher" do
174
+ it "adds 'unsafe-inline', filters blocked-all-mixed-content, upgrade-insecure-requests, and hash sources for safari 10 and higher" do
185
175
  policy = ContentSecurityPolicy.new(complex_opts, USER_AGENTS[:safari10])
186
- expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; child-src child-src.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; img-src img-src.com; media-src media-src.com; object-src object-src.com; plugin-types application/pdf; sandbox allow-forms; script-src script-src.com 'nonce-123456'; style-src style-src.com; report-uri report-uri.com")
176
+ expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; child-src child-src.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; img-src img-src.com; media-src media-src.com; object-src object-src.com; plugin-types application/pdf; sandbox allow-forms; script-src script-src.com 'nonce-123456' 'unsafe-inline'; style-src style-src.com; report-uri report-uri.com")
187
177
  end
188
178
 
189
179
  it "falls back to standard Firefox defaults when the useragent version is not present" do
190
180
  ua = USER_AGENTS[:firefox].dup
191
181
  allow(ua).to receive(:version).and_return(nil)
192
182
  policy = ContentSecurityPolicy.new(complex_opts, ua)
193
- expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; frame-src child-src.com; img-src img-src.com; manifest-src manifest-src.com; media-src media-src.com; object-src object-src.com; sandbox allow-forms; script-src script-src.com 'nonce-123456'; style-src style-src.com; upgrade-insecure-requests; report-uri report-uri.com")
183
+ expect(policy.value).to eq("default-src default-src.com; base-uri base-uri.com; connect-src connect-src.com; font-src font-src.com; form-action form-action.com; frame-ancestors frame-ancestors.com; frame-src child-src.com; img-src img-src.com; manifest-src manifest-src.com; media-src media-src.com; object-src object-src.com; sandbox allow-forms; script-src script-src.com 'nonce-123456' 'unsafe-inline'; style-src style-src.com; upgrade-insecure-requests; report-uri report-uri.com")
194
184
  end
195
185
  end
196
186
  end
@@ -3,6 +3,11 @@ require "spec_helper"
3
3
 
4
4
  module SecureHeaders
5
5
  describe PolicyManagement do
6
+ before(:each) do
7
+ reset_config
8
+ Configuration.default
9
+ end
10
+
6
11
  let (:default_opts) do
7
12
  {
8
13
  default_src: %w(https:),
@@ -18,7 +23,7 @@ module SecureHeaders
18
23
  # (pulled from README)
19
24
  config = {
20
25
  # "meta" values. these will shape the header, but the values are not included in the header.
21
- report_only: true, # default: false
26
+ report_only: false,
22
27
  preserve_schemes: true, # default: false. Schemes are removed from host sources to save bytes and discourage mixed content.
23
28
 
24
29
  # directive values: these values will directly translate into source directives
@@ -142,9 +147,24 @@ module SecureHeaders
142
147
  ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(plugin_types: ["application/pdf"])))
143
148
  end.to_not raise_error
144
149
  end
150
+
151
+ it "doesn't allow report_only to be set in a non-report-only config" do
152
+ expect do
153
+ ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(report_only: true)))
154
+ end.to raise_error(ContentSecurityPolicyConfigError)
155
+ end
156
+
157
+ it "allows report_only to be set in a report-only config" do
158
+ expect do
159
+ ContentSecurityPolicy.validate_config!(ContentSecurityPolicyReportOnlyConfig.new(default_opts.merge(report_only: true)))
160
+ end.to_not raise_error
161
+ end
145
162
  end
146
163
 
147
164
  describe "#combine_policies" do
165
+ before(:each) do
166
+ reset_config
167
+ end
148
168
  it "combines the default-src value with the override if the directive was unconfigured" do
149
169
  Configuration.default do |config|
150
170
  config.csp = {
@@ -152,7 +172,8 @@ module SecureHeaders
152
172
  script_src: %w('self'),
153
173
  }
154
174
  end
155
- combined_config = ContentSecurityPolicy.combine_policies(Configuration.get(Configuration::DEFAULT_CONFIG, internal: true).csp.to_h, style_src: %w(anothercdn.com))
175
+ default_policy = Configuration.dup
176
+ combined_config = ContentSecurityPolicy.combine_policies(default_policy.csp.to_h, style_src: %w(anothercdn.com))
156
177
  csp = ContentSecurityPolicy.new(combined_config)
157
178
  expect(csp.name).to eq(ContentSecurityPolicyConfig::HEADER_NAME)
158
179
  expect(csp.value).to eq("default-src https:; script-src 'self'; style-src https: anothercdn.com")
@@ -167,7 +188,8 @@ module SecureHeaders
167
188
  }.freeze
168
189
  end
169
190
  report_uri = "https://report-uri.io/asdf"
170
- combined_config = ContentSecurityPolicy.combine_policies(Configuration.get(Configuration::DEFAULT_CONFIG, internal: true).csp.to_h, report_uri: [report_uri])
191
+ default_policy = Configuration.dup
192
+ combined_config = ContentSecurityPolicy.combine_policies(default_policy.csp.to_h, report_uri: [report_uri])
171
193
  csp = ContentSecurityPolicy.new(combined_config, USER_AGENTS[:firefox])
172
194
  expect(csp.value).to include("report-uri #{report_uri}")
173
195
  end
@@ -183,7 +205,8 @@ module SecureHeaders
183
205
  non_default_source_additions = ContentSecurityPolicy::NON_FETCH_SOURCES.each_with_object({}) do |directive, hash|
184
206
  hash[directive] = %w("http://example.org)
185
207
  end
186
- combined_config = ContentSecurityPolicy.combine_policies(Configuration.get(Configuration::DEFAULT_CONFIG, internal: true).csp.to_h, non_default_source_additions)
208
+ default_policy = Configuration.dup
209
+ combined_config = ContentSecurityPolicy.combine_policies(default_policy.csp.to_h, non_default_source_additions)
187
210
 
188
211
  ContentSecurityPolicy::NON_FETCH_SOURCES.each do |directive|
189
212
  expect(combined_config[directive]).to eq(%w("http://example.org))
@@ -198,7 +221,8 @@ module SecureHeaders
198
221
  report_only: false
199
222
  }
200
223
  end
201
- combined_config = ContentSecurityPolicy.combine_policies(Configuration.get(Configuration::DEFAULT_CONFIG, internal: true).csp.to_h, report_only: true)
224
+ default_policy = Configuration.dup
225
+ combined_config = ContentSecurityPolicy.combine_policies(default_policy.csp.to_h, report_only: true)
202
226
  csp = ContentSecurityPolicy.new(combined_config, USER_AGENTS[:firefox])
203
227
  expect(csp.name).to eq(ContentSecurityPolicyReportOnlyConfig::HEADER_NAME)
204
228
  end
@@ -211,7 +235,8 @@ module SecureHeaders
211
235
  block_all_mixed_content: false
212
236
  }
213
237
  end
214
- combined_config = ContentSecurityPolicy.combine_policies(Configuration.get(Configuration::DEFAULT_CONFIG, internal: true).csp.to_h, block_all_mixed_content: true)
238
+ default_policy = Configuration.dup
239
+ combined_config = ContentSecurityPolicy.combine_policies(default_policy.csp.to_h, block_all_mixed_content: true)
215
240
  csp = ContentSecurityPolicy.new(combined_config)
216
241
  expect(csp.value).to eq("default-src https:; block-all-mixed-content; script-src 'self'")
217
242
  end
@@ -220,8 +245,9 @@ module SecureHeaders
220
245
  Configuration.default do |config|
221
246
  config.csp = OPT_OUT
222
247
  end
248
+ default_policy = Configuration.dup
223
249
  expect do
224
- ContentSecurityPolicy.combine_policies(Configuration.get(Configuration::DEFAULT_CONFIG, internal: true).csp.to_h, script_src: %w(anothercdn.com))
250
+ ContentSecurityPolicy.combine_policies(default_policy.csp.to_h, script_src: %w(anothercdn.com))
225
251
  end.to raise_error(ContentSecurityPolicyConfigError)
226
252
  end
227
253
  end
@@ -16,6 +16,7 @@ module SecureHeaders
16
16
 
17
17
  it "warns if the hpkp report-uri host is the same as the current host" do
18
18
  report_host = "report-uri.io"
19
+ reset_config
19
20
  Configuration.default do |config|
20
21
  config.hpkp = {
21
22
  max_age: 10000000,
@@ -50,12 +51,14 @@ module SecureHeaders
50
51
  end
51
52
  request = Rack::Request.new({})
52
53
  SecureHeaders.use_secure_headers_override(request, "my_custom_config")
53
- expect(request.env[SECURE_HEADERS_CONFIG]).to be(Configuration.get("my_custom_config", internal: true))
54
54
  _, env = middleware.call request.env
55
55
  expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match("example.org")
56
56
  end
57
57
 
58
58
  context "cookies" do
59
+ before(:each) do
60
+ reset_config
61
+ end
59
62
  context "cookies should be flagged" do
60
63
  it "flags cookies as secure" do
61
64
  Configuration.default { |config| config.cookies = {secure: true, httponly: OPT_OUT, samesite: OPT_OUT} }
@@ -87,6 +90,9 @@ module SecureHeaders
87
90
  end
88
91
 
89
92
  context "cookies" do
93
+ before(:each) do
94
+ reset_config
95
+ end
90
96
  it "flags cookies from configuration" do
91
97
  Configuration.default { |config| config.cookies = { secure: true, httponly: true, samesite: { lax: true} } }
92
98
  request = Rack::Request.new("HTTPS" => "on")
@@ -105,6 +105,7 @@ module SecureHeaders
105
105
  let(:filename) { "app/views/asdfs/index.html.erb" }
106
106
 
107
107
  before(:all) do
108
+ reset_config
108
109
  Configuration.default do |config|
109
110
  config.csp = {
110
111
  default_src: %w('self'),
@@ -17,10 +17,17 @@ module SecureHeaders
17
17
 
18
18
  it "raises a NotYetConfiguredError if trying to opt-out of unconfigured headers" do
19
19
  expect do
20
- SecureHeaders.opt_out_of_header(request, ContentSecurityPolicyConfig::CONFIG_KEY)
20
+ SecureHeaders.opt_out_of_header(request, :csp)
21
21
  end.to raise_error(Configuration::NotYetConfiguredError)
22
22
  end
23
23
 
24
+ it "raises a AlreadyConfiguredError if trying to configure and default has already been set " do
25
+ Configuration.default
26
+ expect do
27
+ Configuration.default
28
+ end.to raise_error(Configuration::AlreadyConfiguredError)
29
+ end
30
+
24
31
  it "raises and ArgumentError when referencing an override that has not been set" do
25
32
  expect do
26
33
  Configuration.default
@@ -34,9 +41,9 @@ module SecureHeaders
34
41
  config.csp = { default_src: %w('self'), script_src: %w('self')}
35
42
  config.csp_report_only = config.csp
36
43
  end
37
- SecureHeaders.opt_out_of_header(request, ContentSecurityPolicyConfig::CONFIG_KEY)
38
- SecureHeaders.opt_out_of_header(request, ContentSecurityPolicyReportOnlyConfig::CONFIG_KEY)
39
- SecureHeaders.opt_out_of_header(request, XContentTypeOptions::CONFIG_KEY)
44
+ SecureHeaders.opt_out_of_header(request, :csp)
45
+ SecureHeaders.opt_out_of_header(request, :csp_report_only)
46
+ SecureHeaders.opt_out_of_header(request, :x_content_type_options)
40
47
  hash = SecureHeaders.header_hash_for(request)
41
48
  expect(hash["Content-Security-Policy-Report-Only"]).to be_nil
42
49
  expect(hash["Content-Security-Policy"]).to be_nil
@@ -60,6 +67,24 @@ module SecureHeaders
60
67
  expect(hash["X-Frame-Options"]).to be_nil
61
68
  end
62
69
 
70
+ it "Overrides the current default config if default config changes during request" do
71
+ Configuration.default do |config|
72
+ config.x_frame_options = OPT_OUT
73
+ end
74
+
75
+ # Dynamically update the default config for this request
76
+ SecureHeaders.override_x_frame_options(request, "DENY")
77
+
78
+ Configuration.override(:dynamic_override) do |config|
79
+ config.x_content_type_options = "nosniff"
80
+ end
81
+
82
+ SecureHeaders.use_secure_headers_override(request, :dynamic_override)
83
+ hash = SecureHeaders.header_hash_for(request)
84
+ expect(hash["X-Content-Type-Options"]).to eq("nosniff")
85
+ expect(hash["X-Frame-Options"]).to eq("DENY")
86
+ end
87
+
63
88
  it "allows you to opt out entirely" do
64
89
  # configure the disabled-by-default headers to ensure they also do not get set
65
90
  Configuration.default do |config|
@@ -78,9 +103,6 @@ module SecureHeaders
78
103
  end
79
104
  SecureHeaders.opt_out_of_all_protection(request)
80
105
  hash = SecureHeaders.header_hash_for(request)
81
- ALL_HEADER_CLASSES.each do |klass|
82
- expect(hash[klass::CONFIG_KEY]).to be_nil
83
- end
84
106
  expect(hash.count).to eq(0)
85
107
  end
86
108
 
@@ -116,7 +138,7 @@ module SecureHeaders
116
138
  firefox_request = Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:firefox]))
117
139
 
118
140
  # append an unsupported directive
119
- SecureHeaders.override_content_security_policy_directives(firefox_request, {plugin_types: %w(flash)})
141
+ SecureHeaders.override_content_security_policy_directives(firefox_request, {plugin_types: %w(application/pdf)})
120
142
  # append a supported directive
121
143
  SecureHeaders.override_content_security_policy_directives(firefox_request, {script_src: %w('self')})
122
144
 
@@ -265,21 +287,6 @@ module SecureHeaders
265
287
  expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to eq("default-src https:; img-src data:; script-src 'self'")
266
288
  end
267
289
 
268
- it "does not append a nonce when the browser does not support it" do
269
- Configuration.default do |config|
270
- config.csp = {
271
- default_src: %w('self'),
272
- script_src: %w(mycdn.com 'unsafe-inline'),
273
- style_src: %w('self')
274
- }
275
- end
276
-
277
- safari_request = Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:safari5]))
278
- SecureHeaders.content_security_policy_script_nonce(safari_request)
279
- hash = SecureHeaders.header_hash_for(safari_request)
280
- expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to eq("default-src 'self'; script-src mycdn.com 'unsafe-inline'; style-src 'self'")
281
- end
282
-
283
290
  it "appends a nonce to the script-src when used" do
284
291
  Configuration.default do |config|
285
292
  config.csp = {
@@ -297,21 +304,7 @@ module SecureHeaders
297
304
  SecureHeaders.content_security_policy_script_nonce(chrome_request)
298
305
 
299
306
  hash = SecureHeaders.header_hash_for(chrome_request)
300
- expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src mycdn.com 'nonce-#{nonce}'; style-src 'self'")
301
- end
302
-
303
- it "uses a nonce for safari 10+" do
304
- Configuration.default do |config|
305
- config.csp = {
306
- default_src: %w('self'),
307
- script_src: %w(mycdn.com)
308
- }
309
- end
310
-
311
- safari_request = Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:safari10]))
312
- nonce = SecureHeaders.content_security_policy_script_nonce(safari_request)
313
- hash = SecureHeaders.header_hash_for(safari_request)
314
- expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src mycdn.com 'nonce-#{nonce}'")
307
+ expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src mycdn.com 'nonce-#{nonce}' 'unsafe-inline'; style-src 'self'")
315
308
  end
316
309
 
317
310
  it "does not support the deprecated `report_only: true` format" do
@@ -322,7 +315,7 @@ module SecureHeaders
322
315
  report_only: true
323
316
  }
324
317
  end
325
- }.to raise_error(ArgumentError)
318
+ }.to raise_error(ContentSecurityPolicyConfigError)
326
319
  end
327
320
 
328
321
  it "Raises an error if csp_report_only is used with `report_only: false`" do
@@ -349,6 +342,7 @@ module SecureHeaders
349
342
  end
350
343
 
351
344
  it "sets identical values when the configs are the same" do
345
+ reset_config
352
346
  Configuration.default do |config|
353
347
  config.csp = {
354
348
  default_src: %w('self'),
@@ -366,6 +360,7 @@ module SecureHeaders
366
360
  end
367
361
 
368
362
  it "sets different headers when the configs are different" do
363
+ reset_config
369
364
  Configuration.default do |config|
370
365
  config.csp = {
371
366
  default_src: %w('self'),
@@ -376,10 +371,11 @@ module SecureHeaders
376
371
 
377
372
  hash = SecureHeaders.header_hash_for(request)
378
373
  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")
374
+ expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'; script-src foo.com")
380
375
  end
381
376
 
382
377
  it "allows you to opt-out of enforced CSP" do
378
+ reset_config
383
379
  Configuration.default do |config|
384
380
  config.csp = SecureHeaders::OPT_OUT
385
381
  config.csp_report_only = {
@@ -437,6 +433,7 @@ module SecureHeaders
437
433
 
438
434
  context "when inferring which config to modify" do
439
435
  it "updates the enforced header when configured" do
436
+ reset_config
440
437
  Configuration.default do |config|
441
438
  config.csp = {
442
439
  default_src: %w('self'),
@@ -451,6 +448,7 @@ module SecureHeaders
451
448
  end
452
449
 
453
450
  it "updates the report only header when configured" do
451
+ reset_config
454
452
  Configuration.default do |config|
455
453
  config.csp = OPT_OUT
456
454
  config.csp_report_only = {
@@ -466,6 +464,7 @@ module SecureHeaders
466
464
  end
467
465
 
468
466
  it "updates both headers if both are configured" do
467
+ reset_config
469
468
  Configuration.default do |config|
470
469
  config.csp = {
471
470
  default_src: %w(enforced.com),
data/spec/spec_helper.rb CHANGED
@@ -26,6 +26,7 @@ USER_AGENTS = {
26
26
 
27
27
  def expect_default_values(hash)
28
28
  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
+ expect(hash[SecureHeaders::ContentSecurityPolicyReportOnlyConfig::HEADER_NAME]).to be_nil
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)
@@ -34,18 +35,21 @@ def expect_default_values(hash)
34
35
  expect(hash[SecureHeaders::XPermittedCrossDomainPolicies::HEADER_NAME]).to eq(SecureHeaders::XPermittedCrossDomainPolicies::DEFAULT_VALUE)
35
36
  expect(hash[SecureHeaders::ReferrerPolicy::HEADER_NAME]).to be_nil
36
37
  expect(hash[SecureHeaders::ExpectCertificateTransparency::HEADER_NAME]).to be_nil
38
+ expect(hash[SecureHeaders::ClearSiteData::HEADER_NAME]).to be_nil
39
+ expect(hash[SecureHeaders::ExpectCertificateTransparency::HEADER_NAME]).to be_nil
40
+ expect(hash[SecureHeaders::PublicKeyPins::HEADER_NAME]).to be_nil
37
41
  end
38
42
 
39
43
  module SecureHeaders
40
44
  class Configuration
41
45
  class << self
42
- def clear_configurations
43
- @configurations = nil
46
+ def clear_default_config
47
+ remove_instance_variable(:@default_config) if defined?(@default_config)
44
48
  end
45
49
  end
46
50
  end
47
51
  end
48
52
 
49
53
  def reset_config
50
- SecureHeaders::Configuration.clear_configurations
54
+ SecureHeaders::Configuration.clear_default_config
51
55
  end
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: 5.2.0
4
+ version: 6.0.0.alpha01
5
5
  platform: ruby
6
6
  authors:
7
7
  - Neil Matatall
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-21 00:00:00.000000000 Z
11
+ date: 2018-02-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -69,6 +69,7 @@ files:
69
69
  - docs/upgrading-to-3-0.md
70
70
  - docs/upgrading-to-4-0.md
71
71
  - docs/upgrading-to-5-0.md
72
+ - docs/upgrading-to-6-0.md
72
73
  - lib/secure_headers.rb
73
74
  - lib/secure_headers/configuration.rb
74
75
  - lib/secure_headers/hash_helper.rb
@@ -125,9 +126,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
125
126
  version: '0'
126
127
  required_rubygems_version: !ruby/object:Gem::Requirement
127
128
  requirements:
128
- - - ">="
129
+ - - ">"
129
130
  - !ruby/object:Gem::Version
130
- version: '0'
131
+ version: 1.3.1
131
132
  requirements: []
132
133
  rubyforge_project:
133
134
  rubygems_version: 2.6.13