secure_headers 3.9.0 → 4.0.0.alpha01
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of secure_headers might be problematic. Click here for more details.
- 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
|