secure_headers 3.9.0 → 4.0.0.alpha01
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of secure_headers might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/.rspec +1 -0
- data/.rubocop.yml +3 -0
- data/.ruby-version +1 -1
- data/.travis.yml +8 -6
- data/CHANGELOG.md +2 -34
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +7 -4
- data/Guardfile +1 -0
- data/README.md +4 -25
- data/Rakefile +22 -18
- data/docs/cookies.md +18 -5
- data/lib/secure_headers.rb +1 -2
- data/lib/secure_headers/configuration.rb +6 -16
- data/lib/secure_headers/hash_helper.rb +2 -1
- data/lib/secure_headers/headers/clear_site_data.rb +2 -1
- data/lib/secure_headers/headers/content_security_policy.rb +14 -60
- data/lib/secure_headers/headers/content_security_policy_config.rb +1 -1
- data/lib/secure_headers/headers/cookie.rb +22 -10
- data/lib/secure_headers/headers/policy_management.rb +57 -98
- data/lib/secure_headers/headers/public_key_pins.rb +4 -3
- data/lib/secure_headers/headers/referrer_policy.rb +1 -0
- data/lib/secure_headers/headers/strict_transport_security.rb +2 -1
- data/lib/secure_headers/headers/x_content_type_options.rb +1 -0
- data/lib/secure_headers/headers/x_download_options.rb +2 -1
- data/lib/secure_headers/headers/x_frame_options.rb +1 -0
- data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +2 -1
- data/lib/secure_headers/headers/x_xss_protection.rb +2 -1
- data/lib/secure_headers/middleware.rb +10 -9
- data/lib/secure_headers/railtie.rb +7 -6
- data/lib/secure_headers/utils/cookies_config.rb +17 -18
- data/lib/secure_headers/view_helper.rb +2 -1
- data/lib/tasks/tasks.rake +2 -1
- data/secure_headers.gemspec +13 -3
- data/spec/lib/secure_headers/configuration_spec.rb +9 -8
- data/spec/lib/secure_headers/headers/clear_site_data_spec.rb +2 -1
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +17 -53
- data/spec/lib/secure_headers/headers/cookie_spec.rb +58 -37
- data/spec/lib/secure_headers/headers/policy_management_spec.rb +20 -41
- data/spec/lib/secure_headers/headers/public_key_pins_spec.rb +7 -6
- data/spec/lib/secure_headers/headers/referrer_policy_spec.rb +4 -3
- data/spec/lib/secure_headers/headers/strict_transport_security_spec.rb +5 -4
- data/spec/lib/secure_headers/headers/x_content_type_options_spec.rb +2 -1
- data/spec/lib/secure_headers/headers/x_download_options_spec.rb +3 -2
- data/spec/lib/secure_headers/headers/x_frame_options_spec.rb +2 -1
- data/spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb +4 -3
- data/spec/lib/secure_headers/headers/x_xss_protection_spec.rb +4 -3
- data/spec/lib/secure_headers/middleware_spec.rb +18 -21
- data/spec/lib/secure_headers/view_helpers_spec.rb +5 -4
- data/spec/lib/secure_headers_spec.rb +92 -120
- data/spec/spec_helper.rb +9 -23
- data/upgrading-to-4-0.md +49 -0
- metadata +16 -11
- data/lib/secure_headers/headers/expect_certificate_transparency.rb +0 -70
- data/spec/lib/secure_headers/headers/expect_certificate_transparency_spec.rb +0 -42
@@ -1,4 +1,5 @@
|
|
1
|
-
|
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
|
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,
|
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
|
-
|
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:
|
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:
|
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:
|
26
|
+
PublicKeyPins.validate_config!(max_age: "abc123")
|
26
27
|
end.to raise_error(PublicKeyPinsConfigError)
|
27
28
|
end
|
28
29
|
|
29
|
-
it
|
30
|
+
it "raises an exception with less than 2 pins" do
|
30
31
|
expect do
|
31
|
-
config = { max_age: 1234, pins: [{ sha256:
|
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
|
-
|
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(
|
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
|
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
|
-
|
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!(
|
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!(
|
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!(
|
26
|
+
StrictTransportSecurity.validate_config!("max-age=123includeSubdomains")
|
26
27
|
end.to raise_error(STSConfigError)
|
27
28
|
end
|
28
29
|
end
|
@@ -1,9 +1,10 @@
|
|
1
|
-
|
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(
|
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,9 +1,10 @@
|
|
1
|
-
|
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(
|
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
|
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
|
-
|
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,
|
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!(
|
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:
|
23
|
-
{sha256:
|
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 "
|
58
|
+
context "cookies" do
|
58
59
|
context "cookies should be flagged" do
|
59
60
|
it "flags cookies as secure" do
|
60
|
-
|
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[
|
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
|
-
|
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[
|
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[
|
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[
|
98
|
-
expect(env[
|
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[
|
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[
|
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[
|
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
|
-
:
|
86
|
-
:
|
87
|
-
:
|
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
|
-
|
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[
|
41
|
-
expect(hash[
|
42
|
-
expect(hash[
|
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[
|
58
|
-
expect(hash[
|
59
|
-
expect(hash[
|
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 = { :
|
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:
|
149
|
+
report_uri: "//example.com/uri-directive",
|
148
150
|
pins: [
|
149
|
-
{ sha256:
|
150
|
-
{ sha256:
|
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 "
|
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
|
-
|
198
|
-
|
199
|
-
|
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'
|
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[
|
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[
|
314
|
+
expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src mycdn.com 'nonce-#{nonce}'")
|
319
315
|
end
|
320
316
|
|
321
|
-
it "
|
322
|
-
expect
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
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[
|
372
|
-
expect(hash[
|
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(
|
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[
|
385
|
-
expect(hash[
|
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[
|
425
|
-
expect(hash[
|
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[
|
432
|
-
expect(hash[
|
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[
|
439
|
-
expect(hash[
|
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[
|
446
|
-
expect(hash[
|
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[
|
453
|
-
expect(hash[
|
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[
|
460
|
-
expect(hash[
|
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[
|
467
|
-
expect(hash[
|
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[
|
481
|
-
expect(hash[
|
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[
|
495
|
-
expect(hash[
|
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[
|
511
|
-
expect(hash[
|
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 =
|
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 =>
|
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:
|
511
|
+
config.csp = { made_up_directive: "123456" }
|
540
512
|
end
|
541
513
|
end.to raise_error(ContentSecurityPolicyConfigError)
|
542
514
|
end
|