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.
- checksums.yaml +4 -4
- data/.travis.yml +8 -4
- data/CHANGELOG.md +3 -7
- data/Gemfile +1 -1
- data/docs/upgrading-to-6-0.md +50 -0
- data/lib/secure_headers.rb +14 -76
- data/lib/secure_headers/configuration.rb +114 -164
- data/lib/secure_headers/headers/clear_site_data.rb +1 -3
- data/lib/secure_headers/headers/content_security_policy.rb +4 -17
- data/lib/secure_headers/headers/content_security_policy_config.rb +3 -13
- data/lib/secure_headers/headers/expect_certificate_transparency.rb +2 -3
- data/lib/secure_headers/headers/policy_management.rb +12 -11
- data/lib/secure_headers/headers/public_key_pins.rb +2 -3
- data/lib/secure_headers/headers/referrer_policy.rb +2 -2
- data/lib/secure_headers/headers/strict_transport_security.rb +2 -2
- data/lib/secure_headers/headers/x_content_type_options.rb +2 -2
- data/lib/secure_headers/headers/x_download_options.rb +2 -2
- data/lib/secure_headers/headers/x_frame_options.rb +1 -2
- data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +2 -2
- data/lib/secure_headers/headers/x_xss_protection.rb +3 -3
- data/secure_headers.gemspec +1 -1
- data/spec/lib/secure_headers/configuration_spec.rb +15 -70
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +12 -22
- data/spec/lib/secure_headers/headers/policy_management_spec.rb +33 -7
- data/spec/lib/secure_headers/middleware_spec.rb +7 -1
- data/spec/lib/secure_headers/view_helpers_spec.rb +1 -0
- data/spec/lib/secure_headers_spec.rb +39 -40
- data/spec/spec_helper.rb +7 -3
- 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,
|
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,
|
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,
|
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:
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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(
|
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")
|
@@ -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,
|
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,
|
38
|
-
SecureHeaders.opt_out_of_header(request,
|
39
|
-
SecureHeaders.opt_out_of_header(request,
|
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(
|
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(
|
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
|
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
|
43
|
-
|
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.
|
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:
|
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:
|
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:
|
131
|
+
version: 1.3.1
|
131
132
|
requirements: []
|
132
133
|
rubyforge_project:
|
133
134
|
rubygems_version: 2.6.13
|