secure_headers 3.6.4 → 4.0.0
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.
- checksums.yaml +4 -4
- data/.rspec +2 -0
- data/.rubocop.yml +3 -0
- data/.ruby-version +1 -1
- data/.travis.yml +8 -6
- data/CHANGELOG.md +21 -1
- data/CONTRIBUTING.md +3 -5
- data/Gemfile +7 -4
- data/Guardfile +1 -0
- data/README.md +15 -19
- data/Rakefile +22 -18
- data/docs/cookies.md +16 -2
- data/docs/hashes.md +1 -1
- data/docs/per_action_configuration.md +28 -0
- data/lib/secure_headers/configuration.rb +29 -16
- data/lib/secure_headers/hash_helper.rb +2 -1
- data/lib/secure_headers/headers/clear_site_data.rb +16 -12
- data/lib/secure_headers/headers/content_security_policy.rb +52 -20
- data/lib/secure_headers/headers/content_security_policy_config.rb +25 -0
- data/lib/secure_headers/headers/cookie.rb +21 -3
- data/lib/secure_headers/headers/expect_certificate_transparency.rb +70 -0
- data/lib/secure_headers/headers/policy_management.rb +115 -68
- data/lib/secure_headers/headers/public_key_pins.rb +5 -4
- 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 +13 -12
- data/lib/secure_headers/view_helper.rb +4 -3
- data/lib/secure_headers.rb +6 -2
- 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 +11 -27
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +42 -26
- data/spec/lib/secure_headers/headers/cookie_spec.rb +38 -20
- data/spec/lib/secure_headers/headers/expect_certificate_spec.rb +42 -0
- data/spec/lib/secure_headers/headers/policy_management_spec.rb +57 -10
- 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 +29 -21
- data/spec/lib/secure_headers/view_helpers_spec.rb +5 -5
- data/spec/lib/secure_headers_spec.rb +96 -124
- data/spec/spec_helper.rb +10 -22
- data/upgrading-to-4-0.md +55 -0
- metadata +16 -6
|
@@ -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
|
|
|
@@ -27,6 +28,7 @@ module SecureHeaders
|
|
|
27
28
|
connect_src: %w(wss:),
|
|
28
29
|
font_src: %w('self' data:),
|
|
29
30
|
img_src: %w(mycdn.com data:),
|
|
31
|
+
manifest_src: %w(manifest.com),
|
|
30
32
|
media_src: %w(utoob.com),
|
|
31
33
|
object_src: %w('self'),
|
|
32
34
|
script_src: %w('self'),
|
|
@@ -45,10 +47,22 @@ module SecureHeaders
|
|
|
45
47
|
|
|
46
48
|
it "requires a :default_src value" do
|
|
47
49
|
expect do
|
|
48
|
-
ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(script_src: %('self')))
|
|
50
|
+
ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(script_src: %w('self')))
|
|
49
51
|
end.to raise_error(ContentSecurityPolicyConfigError)
|
|
50
52
|
end
|
|
51
53
|
|
|
54
|
+
it "requires a :script_src value" do
|
|
55
|
+
expect do
|
|
56
|
+
ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_src: %w('self')))
|
|
57
|
+
end.to raise_error(ContentSecurityPolicyConfigError)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "accepts OPT_OUT as a script-src value" do
|
|
61
|
+
expect do
|
|
62
|
+
ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_src: %w('self'), script_src: OPT_OUT))
|
|
63
|
+
end.to_not raise_error
|
|
64
|
+
end
|
|
65
|
+
|
|
52
66
|
it "requires :report_only to be a truthy value" do
|
|
53
67
|
expect do
|
|
54
68
|
ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(report_only: "steve")))
|
|
@@ -94,28 +108,60 @@ module SecureHeaders
|
|
|
94
108
|
# this is mostly to ensure people don't use the antiquated shorthands common in other configs
|
|
95
109
|
it "performs light validation on source lists" do
|
|
96
110
|
expect do
|
|
97
|
-
ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_src: %w(self none inline eval)))
|
|
111
|
+
ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_src: %w(self none inline eval), script_src: %w('self')))
|
|
112
|
+
end.to raise_error(ContentSecurityPolicyConfigError)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "rejects anything not of the form allow-* as a sandbox value" do
|
|
116
|
+
expect do
|
|
117
|
+
ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(sandbox: ["steve"])))
|
|
118
|
+
end.to raise_error(ContentSecurityPolicyConfigError)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it "accepts anything of the form allow-* as a sandbox value " do
|
|
122
|
+
expect do
|
|
123
|
+
ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(sandbox: ["allow-foo"])))
|
|
124
|
+
end.to_not raise_error
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it "accepts true as a sandbox policy" do
|
|
128
|
+
expect do
|
|
129
|
+
ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(sandbox: true)))
|
|
130
|
+
end.to_not raise_error
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it "rejects anything not of the form type/subtype as a plugin-type value" do
|
|
134
|
+
expect do
|
|
135
|
+
ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(plugin_types: ["steve"])))
|
|
98
136
|
end.to raise_error(ContentSecurityPolicyConfigError)
|
|
99
137
|
end
|
|
138
|
+
|
|
139
|
+
it "accepts anything of the form type/subtype as a plugin-type value " do
|
|
140
|
+
expect do
|
|
141
|
+
ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(plugin_types: ["application/pdf"])))
|
|
142
|
+
end.to_not raise_error
|
|
143
|
+
end
|
|
100
144
|
end
|
|
101
145
|
|
|
102
146
|
describe "#combine_policies" do
|
|
103
147
|
it "combines the default-src value with the override if the directive was unconfigured" do
|
|
104
148
|
Configuration.default do |config|
|
|
105
149
|
config.csp = {
|
|
106
|
-
default_src: %w(https:)
|
|
150
|
+
default_src: %w(https:),
|
|
151
|
+
script_src: %w('self'),
|
|
107
152
|
}
|
|
108
153
|
end
|
|
109
|
-
combined_config = ContentSecurityPolicy.combine_policies(Configuration.get.csp.to_h,
|
|
154
|
+
combined_config = ContentSecurityPolicy.combine_policies(Configuration.get.csp.to_h, style_src: %w(anothercdn.com))
|
|
110
155
|
csp = ContentSecurityPolicy.new(combined_config)
|
|
111
156
|
expect(csp.name).to eq(ContentSecurityPolicyConfig::HEADER_NAME)
|
|
112
|
-
expect(csp.value).to eq("default-src https:; script-src https: anothercdn.com")
|
|
157
|
+
expect(csp.value).to eq("default-src https:; script-src 'self'; style-src https: anothercdn.com")
|
|
113
158
|
end
|
|
114
159
|
|
|
115
160
|
it "combines directives where the original value is nil and the hash is frozen" do
|
|
116
161
|
Configuration.default do |config|
|
|
117
162
|
config.csp = {
|
|
118
163
|
default_src: %w('self'),
|
|
164
|
+
script_src: %w('self'),
|
|
119
165
|
report_only: false
|
|
120
166
|
}.freeze
|
|
121
167
|
end
|
|
@@ -129,6 +175,7 @@ module SecureHeaders
|
|
|
129
175
|
Configuration.default do |config|
|
|
130
176
|
config.csp = {
|
|
131
177
|
default_src: %w('self'),
|
|
178
|
+
script_src: %w('self'),
|
|
132
179
|
report_only: false
|
|
133
180
|
}.freeze
|
|
134
181
|
end
|
|
@@ -140,14 +187,13 @@ module SecureHeaders
|
|
|
140
187
|
ContentSecurityPolicy::NON_FETCH_SOURCES.each do |directive|
|
|
141
188
|
expect(combined_config[directive]).to eq(%w("http://example.org))
|
|
142
189
|
end
|
|
143
|
-
|
|
144
|
-
ContentSecurityPolicy.new(combined_config, USER_AGENTS[:firefox]).value
|
|
145
190
|
end
|
|
146
191
|
|
|
147
192
|
it "overrides the report_only flag" do
|
|
148
193
|
Configuration.default do |config|
|
|
149
194
|
config.csp = {
|
|
150
195
|
default_src: %w('self'),
|
|
196
|
+
script_src: %w('self'),
|
|
151
197
|
report_only: false
|
|
152
198
|
}
|
|
153
199
|
end
|
|
@@ -160,12 +206,13 @@ module SecureHeaders
|
|
|
160
206
|
Configuration.default do |config|
|
|
161
207
|
config.csp = {
|
|
162
208
|
default_src: %w(https:),
|
|
209
|
+
script_src: %w('self'),
|
|
163
210
|
block_all_mixed_content: false
|
|
164
211
|
}
|
|
165
212
|
end
|
|
166
213
|
combined_config = ContentSecurityPolicy.combine_policies(Configuration.get.csp.to_h, block_all_mixed_content: true)
|
|
167
214
|
csp = ContentSecurityPolicy.new(combined_config)
|
|
168
|
-
expect(csp.value).to eq("default-src https:; block-all-mixed-content")
|
|
215
|
+
expect(csp.value).to eq("default-src https:; block-all-mixed-content; script-src 'self'")
|
|
169
216
|
end
|
|
170
217
|
|
|
171
218
|
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,75 @@ 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
|
|
|
68
|
+
it "allows opting out of cookie protection with OPT_OUT alone" do
|
|
69
|
+
Configuration.default { |config| config.cookies = OPT_OUT}
|
|
70
|
+
|
|
71
|
+
# do NOT make this request https. non-https requests modify a config,
|
|
72
|
+
# causing an exception when operating on OPT_OUT. This ensures we don't
|
|
73
|
+
# try to modify the config.
|
|
74
|
+
request = Rack::Request.new({})
|
|
75
|
+
_, env = cookie_middleware.call request.env
|
|
76
|
+
expect(env["Set-Cookie"]).to eq("foo=bar")
|
|
77
|
+
end
|
|
78
|
+
|
|
69
79
|
context "cookies should not be flagged" do
|
|
70
80
|
it "does not flags cookies as secure" do
|
|
71
|
-
|
|
72
|
-
Configuration.default { |config| config.secure_cookies = false }
|
|
73
|
-
end
|
|
81
|
+
Configuration.default { |config| config.cookies = {secure: OPT_OUT, httponly: OPT_OUT, samesite: OPT_OUT} }
|
|
74
82
|
request = Rack::Request.new("HTTPS" => "on")
|
|
75
83
|
_, env = cookie_middleware.call request.env
|
|
76
|
-
expect(env[
|
|
84
|
+
expect(env["Set-Cookie"]).to eq("foo=bar")
|
|
77
85
|
end
|
|
78
86
|
end
|
|
79
87
|
end
|
|
80
88
|
|
|
81
89
|
context "cookies" do
|
|
82
90
|
it "flags cookies from configuration" do
|
|
83
|
-
Configuration.default { |config| config.cookies = { secure: true, httponly: true } }
|
|
91
|
+
Configuration.default { |config| config.cookies = { secure: true, httponly: true, samesite: { lax: true} } }
|
|
84
92
|
request = Rack::Request.new("HTTPS" => "on")
|
|
85
93
|
_, env = cookie_middleware.call request.env
|
|
86
94
|
|
|
87
|
-
expect(env[
|
|
95
|
+
expect(env["Set-Cookie"]).to eq("foo=bar; secure; HttpOnly; SameSite=Lax")
|
|
88
96
|
end
|
|
89
97
|
|
|
90
98
|
it "flags cookies with a combination of SameSite configurations" do
|
|
91
99
|
cookie_middleware = Middleware.new(lambda { |env| [200, env.merge("Set-Cookie" => ["_session=foobar", "_guest=true"]), "app"] })
|
|
92
100
|
|
|
93
|
-
Configuration.default { |config| config.cookies = { samesite: { lax: { except: ["_session"] }, strict: { only: ["_session"] } } } }
|
|
101
|
+
Configuration.default { |config| config.cookies = { samesite: { lax: { except: ["_session"] }, strict: { only: ["_session"] } }, httponly: OPT_OUT, secure: OPT_OUT} }
|
|
94
102
|
request = Rack::Request.new("HTTPS" => "on")
|
|
95
103
|
_, env = cookie_middleware.call request.env
|
|
96
104
|
|
|
97
|
-
expect(env[
|
|
98
|
-
expect(env[
|
|
105
|
+
expect(env["Set-Cookie"]).to match("_session=foobar; SameSite=Strict")
|
|
106
|
+
expect(env["Set-Cookie"]).to match("_guest=true; SameSite=Lax")
|
|
99
107
|
end
|
|
100
108
|
|
|
101
109
|
it "disables secure cookies for non-https requests" do
|
|
102
|
-
Configuration.default { |config| config.cookies = { secure: true } }
|
|
110
|
+
Configuration.default { |config| config.cookies = { secure: true, httponly: OPT_OUT, samesite: OPT_OUT } }
|
|
103
111
|
|
|
104
112
|
request = Rack::Request.new("HTTPS" => "off")
|
|
105
113
|
_, env = cookie_middleware.call request.env
|
|
106
|
-
expect(env[
|
|
114
|
+
expect(env["Set-Cookie"]).to eq("foo=bar")
|
|
107
115
|
end
|
|
108
116
|
|
|
109
117
|
it "sets the secure cookie flag correctly on interleaved http/https requests" do
|
|
110
|
-
Configuration.default { |config| config.cookies = { secure: true } }
|
|
118
|
+
Configuration.default { |config| config.cookies = { secure: true, httponly: OPT_OUT, samesite: OPT_OUT } }
|
|
111
119
|
|
|
112
120
|
request = Rack::Request.new("HTTPS" => "off")
|
|
113
121
|
_, env = cookie_middleware.call request.env
|
|
114
|
-
expect(env[
|
|
122
|
+
expect(env["Set-Cookie"]).to eq("foo=bar")
|
|
115
123
|
|
|
116
124
|
request = Rack::Request.new("HTTPS" => "on")
|
|
117
125
|
_, env = cookie_middleware.call request.env
|
|
118
|
-
expect(env[
|
|
126
|
+
expect(env["Set-Cookie"]).to eq("foo=bar; secure")
|
|
119
127
|
end
|
|
120
128
|
end
|
|
121
129
|
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require "spec_helper"
|
|
2
3
|
require "erb"
|
|
3
4
|
|
|
@@ -37,7 +38,6 @@ class Message < ERB
|
|
|
37
38
|
background-color: black;
|
|
38
39
|
}
|
|
39
40
|
</style>
|
|
40
|
-
<%= @name %>
|
|
41
41
|
|
|
42
42
|
TEMPLATE
|
|
43
43
|
end
|
|
@@ -59,7 +59,7 @@ TEMPLATE
|
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
if options.is_a?(Hash)
|
|
62
|
-
options = options.map {|k,v| " #{k}=#{v}"}
|
|
62
|
+
options = options.map {|k, v| " #{k}=#{v}"}
|
|
63
63
|
end
|
|
64
64
|
"<#{type}#{options}>#{content}</#{type}>"
|
|
65
65
|
end
|
|
@@ -83,9 +83,9 @@ module SecureHeaders
|
|
|
83
83
|
before(:all) do
|
|
84
84
|
Configuration.default do |config|
|
|
85
85
|
config.csp = {
|
|
86
|
-
:
|
|
87
|
-
:
|
|
88
|
-
:
|
|
86
|
+
default_src: %w('self'),
|
|
87
|
+
script_src: %w('self'),
|
|
88
|
+
style_src: %w('self')
|
|
89
89
|
}
|
|
90
90
|
end
|
|
91
91
|
end
|