secure_headers 3.2.0 → 3.8.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.
Files changed (53) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE.md +41 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +20 -0
  4. data/.rspec +1 -0
  5. data/.ruby-version +1 -1
  6. data/.travis.yml +2 -0
  7. data/CHANGELOG.md +160 -1
  8. data/CODE_OF_CONDUCT.md +46 -0
  9. data/CONTRIBUTING.md +41 -0
  10. data/Gemfile +3 -1
  11. data/LICENSE +4 -199
  12. data/README.md +75 -334
  13. data/docs/HPKP.md +17 -0
  14. data/docs/cookies.md +51 -0
  15. data/docs/hashes.md +64 -0
  16. data/docs/named_overrides_and_appends.md +107 -0
  17. data/docs/per_action_configuration.md +105 -0
  18. data/docs/sinatra.md +25 -0
  19. data/lib/secure_headers/configuration.rb +120 -39
  20. data/lib/secure_headers/headers/clear_site_data.rb +54 -0
  21. data/lib/secure_headers/headers/content_security_policy.rb +134 -36
  22. data/lib/secure_headers/headers/content_security_policy_config.rb +162 -0
  23. data/lib/secure_headers/headers/cookie.rb +7 -1
  24. data/lib/secure_headers/headers/expect_certificate_transparency.rb +70 -0
  25. data/lib/secure_headers/headers/policy_management.rb +150 -66
  26. data/lib/secure_headers/headers/public_key_pins.rb +1 -1
  27. data/lib/secure_headers/headers/referrer_policy.rb +36 -0
  28. data/lib/secure_headers/headers/strict_transport_security.rb +1 -1
  29. data/lib/secure_headers/headers/x_content_type_options.rb +1 -1
  30. data/lib/secure_headers/headers/x_download_options.rb +1 -1
  31. data/lib/secure_headers/headers/x_frame_options.rb +1 -1
  32. data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +1 -1
  33. data/lib/secure_headers/headers/x_xss_protection.rb +1 -1
  34. data/lib/secure_headers/middleware.rb +6 -0
  35. data/lib/secure_headers/railtie.rb +1 -1
  36. data/lib/secure_headers/utils/cookies_config.rb +6 -4
  37. data/lib/secure_headers/view_helper.rb +13 -7
  38. data/lib/secure_headers.rb +138 -55
  39. data/lib/tasks/tasks.rake +5 -4
  40. data/secure_headers.gemspec +2 -2
  41. data/spec/lib/secure_headers/configuration_spec.rb +5 -5
  42. data/spec/lib/secure_headers/headers/clear_site_data_spec.rb +86 -0
  43. data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +96 -13
  44. data/spec/lib/secure_headers/headers/cookie_spec.rb +22 -25
  45. data/spec/lib/secure_headers/headers/expect_certificate_transparency_spec.rb +42 -0
  46. data/spec/lib/secure_headers/headers/policy_management_spec.rb +59 -36
  47. data/spec/lib/secure_headers/headers/referrer_policy_spec.rb +72 -0
  48. data/spec/lib/secure_headers/middleware_spec.rb +31 -1
  49. data/spec/lib/secure_headers/view_helpers_spec.rb +19 -7
  50. data/spec/lib/secure_headers_spec.rb +344 -44
  51. data/spec/spec_helper.rb +8 -3
  52. data/upgrading-to-3-0.md +13 -10
  53. metadata +24 -5
@@ -62,29 +62,21 @@ module SecureHeaders
62
62
  end
63
63
 
64
64
  context "SameSite cookies" do
65
- it "flags SameSite=Lax" do
66
- cookie = Cookie.new(raw_cookie, samesite: { lax: { only: ["_session"] } })
67
- expect(cookie.to_s).to eq("_session=thisisatest; SameSite=Lax")
68
- end
69
-
70
- it "flags SameSite=Lax when configured with a boolean" do
71
- cookie = Cookie.new(raw_cookie, samesite: { lax: true})
72
- expect(cookie.to_s).to eq("_session=thisisatest; SameSite=Lax")
73
- end
74
-
75
- it "does not flag cookies as SameSite=Lax when excluded" do
76
- cookie = Cookie.new(raw_cookie, samesite: { lax: { except: ["_session"] } })
77
- expect(cookie.to_s).to eq("_session=thisisatest")
78
- end
65
+ %w(None Lax Strict).each do |flag|
66
+ it "flags SameSite=#{flag}" do
67
+ cookie = Cookie.new(raw_cookie, samesite: { flag.downcase.to_sym => { only: ["_session"] } })
68
+ expect(cookie.to_s).to eq("_session=thisisatest; SameSite=#{flag}")
69
+ end
79
70
 
80
- it "flags SameSite=Strict" do
81
- cookie = Cookie.new(raw_cookie, samesite: { strict: { only: ["_session"] } })
82
- expect(cookie.to_s).to eq("_session=thisisatest; SameSite=Strict")
83
- end
71
+ it "flags SameSite=#{flag} when configured with a boolean" do
72
+ cookie = Cookie.new(raw_cookie, samesite: { flag.downcase.to_sym => true })
73
+ expect(cookie.to_s).to eq("_session=thisisatest; SameSite=#{flag}")
74
+ end
84
75
 
85
- it "does not flag cookies as SameSite=Strict when excluded" do
86
- cookie = Cookie.new(raw_cookie, samesite: { strict: { except: ["_session"] } })
87
- expect(cookie.to_s).to eq("_session=thisisatest")
76
+ it "does not flag cookies as SameSite=#{flag} when excluded" do
77
+ cookie = Cookie.new(raw_cookie, samesite: { flag.downcase.to_sym => { except: ["_session"] } })
78
+ expect(cookie.to_s).to eq("_session=thisisatest")
79
+ end
88
80
  end
89
81
 
90
82
  it "flags SameSite=Strict when configured with a boolean" do
@@ -131,10 +123,15 @@ module SecureHeaders
131
123
  end.to raise_error(CookiesConfigError)
132
124
  end
133
125
 
134
- it "raises an exception when SameSite lax and strict enforcement modes are configured with booleans" do
135
- expect do
136
- Cookie.validate_config!(samesite: { lax: true, strict: true})
137
- end.to raise_error(CookiesConfigError)
126
+ cookie_options = %w(none lax strict).map(&:to_sym)
127
+ cookie_options.each do |flag|
128
+ (cookie_options - [flag]).each do |other_flag|
129
+ it "raises an exception when SameSite #{flag} and #{other_flag} enforcement modes are configured with booleans" do
130
+ expect do
131
+ Cookie.validate_config!(samesite: { flag => true, other_flag => true})
132
+ end.to raise_error(CookiesConfigError)
133
+ end
134
+ end
138
135
  end
139
136
 
140
137
  it "raises an exception when SameSite lax and strict enforcement modes are configured with booleans" do
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
3
+
4
+ module SecureHeaders
5
+ describe ExpectCertificateTransparency do
6
+ specify { expect(ExpectCertificateTransparency.new(max_age: 1234, enforce: true).value).to eq("enforce, max-age=1234") }
7
+ specify { expect(ExpectCertificateTransparency.new(max_age: 1234, enforce: false).value).to eq("max-age=1234") }
8
+ specify { expect(ExpectCertificateTransparency.new(max_age: 1234, enforce: "yolocopter").value).to eq("max-age=1234") }
9
+ specify { expect(ExpectCertificateTransparency.new(max_age: 1234, report_uri: "https://report-uri.io/expect-ct").value).to eq("max-age=1234, report-uri=\"https://report-uri.io/expect-ct\"") }
10
+ specify do
11
+ config = { enforce: true, max_age: 1234, report_uri: "https://report-uri.io/expect-ct" }
12
+ header_value = "enforce, max-age=1234, report-uri=\"https://report-uri.io/expect-ct\""
13
+ expect(ExpectCertificateTransparency.new(config).value).to eq(header_value)
14
+ end
15
+
16
+ context "with an invalid configuration" do
17
+ it "raises an exception when configuration isn't a hash" do
18
+ expect do
19
+ ExpectCertificateTransparency.validate_config!(%w(a))
20
+ end.to raise_error(ExpectCertificateTransparencyConfigError)
21
+ end
22
+
23
+ it "raises an exception when max-age is not provided" do
24
+ expect do
25
+ ExpectCertificateTransparency.validate_config!(foo: "bar")
26
+ end.to raise_error(ExpectCertificateTransparencyConfigError)
27
+ end
28
+
29
+ it "raises an exception with an invalid max-age" do
30
+ expect do
31
+ ExpectCertificateTransparency.validate_config!(max_age: "abc123")
32
+ end.to raise_error(ExpectCertificateTransparencyConfigError)
33
+ end
34
+
35
+ it "raises an exception with an invalid enforce value" do
36
+ expect do
37
+ ExpectCertificateTransparency.validate_config!(enforce: "brokenstring")
38
+ end.to raise_error(ExpectCertificateTransparencyConfigError)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -23,15 +23,17 @@ module SecureHeaders
23
23
  # directive values: these values will directly translate into source directives
24
24
  default_src: %w(https: 'self'),
25
25
  frame_src: %w('self' *.twimg.com itunes.apple.com),
26
- connect_src: %w(wws:),
26
+ child_src: %w('self' *.twimg.com itunes.apple.com),
27
+ connect_src: %w(wss:),
27
28
  font_src: %w('self' data:),
28
29
  img_src: %w(mycdn.com data:),
30
+ manifest_src: %w(manifest.com),
29
31
  media_src: %w(utoob.com),
30
32
  object_src: %w('self'),
31
33
  script_src: %w('self'),
32
34
  style_src: %w('unsafe-inline'),
35
+ worker_src: %w(worker.com),
33
36
  base_uri: %w('self'),
34
- child_src: %w('self'),
35
37
  form_action: %w('self' github.com),
36
38
  frame_ancestors: %w('none'),
37
39
  plugin_types: %w(application/x-shockwave-flash),
@@ -40,70 +42,105 @@ module SecureHeaders
40
42
  report_uri: %w(https://example.com/uri-directive)
41
43
  }
42
44
 
43
- CSP.validate_config!(config)
45
+ ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(config))
44
46
  end
45
47
 
46
48
  it "requires a :default_src value" do
47
49
  expect do
48
- CSP.validate_config!(script_src: %('self'))
50
+ ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(script_src: %('self')))
49
51
  end.to raise_error(ContentSecurityPolicyConfigError)
50
52
  end
51
53
 
52
54
  it "requires :report_only to be a truthy value" do
53
55
  expect do
54
- CSP.validate_config!(default_opts.merge(report_only: "steve"))
56
+ ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(report_only: "steve")))
55
57
  end.to raise_error(ContentSecurityPolicyConfigError)
56
58
  end
57
59
 
58
60
  it "requires :preserve_schemes to be a truthy value" do
59
61
  expect do
60
- CSP.validate_config!(default_opts.merge(preserve_schemes: "steve"))
62
+ ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(preserve_schemes: "steve")))
61
63
  end.to raise_error(ContentSecurityPolicyConfigError)
62
64
  end
63
65
 
64
66
  it "requires :block_all_mixed_content to be a boolean value" do
65
67
  expect do
66
- CSP.validate_config!(default_opts.merge(block_all_mixed_content: "steve"))
68
+ ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(block_all_mixed_content: "steve")))
67
69
  end.to raise_error(ContentSecurityPolicyConfigError)
68
70
  end
69
71
 
70
72
  it "requires :upgrade_insecure_requests to be a boolean value" do
71
73
  expect do
72
- CSP.validate_config!(default_opts.merge(upgrade_insecure_requests: "steve"))
74
+ ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(upgrade_insecure_requests: "steve")))
73
75
  end.to raise_error(ContentSecurityPolicyConfigError)
74
76
  end
75
77
 
76
78
  it "requires all source lists to be an array of strings" do
77
79
  expect do
78
- CSP.validate_config!(default_src: "steve")
80
+ ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_src: "steve"))
79
81
  end.to raise_error(ContentSecurityPolicyConfigError)
80
82
  end
81
83
 
82
84
  it "allows nil values" do
83
85
  expect do
84
- CSP.validate_config!(default_src: %w('self'), script_src: ["https:", nil])
86
+ ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_src: %w('self'), script_src: ["https:", nil]))
85
87
  end.to_not raise_error
86
88
  end
87
89
 
88
90
  it "rejects unknown directives / config" do
89
91
  expect do
90
- CSP.validate_config!(default_src: %w('self'), default_src_totally_mispelled: "steve")
92
+ ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_src: %w('self'), default_src_totally_mispelled: "steve"))
91
93
  end.to raise_error(ContentSecurityPolicyConfigError)
92
94
  end
93
95
 
94
96
  # this is mostly to ensure people don't use the antiquated shorthands common in other configs
95
97
  it "performs light validation on source lists" do
96
98
  expect do
97
- CSP.validate_config!(default_src: %w(self none inline eval))
99
+ ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_src: %w(self none inline eval)))
98
100
  end.to raise_error(ContentSecurityPolicyConfigError)
99
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"])))
124
+ end.to raise_error(ContentSecurityPolicyConfigError)
125
+ 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
100
132
  end
101
133
 
102
134
  describe "#combine_policies" do
103
135
  it "combines the default-src value with the override if the directive was unconfigured" do
104
- combined_config = CSP.combine_policies(Configuration.default.csp, script_src: %w(anothercdn.com))
136
+ Configuration.default do |config|
137
+ config.csp = {
138
+ default_src: %w(https:)
139
+ }
140
+ end
141
+ combined_config = ContentSecurityPolicy.combine_policies(Configuration.get.csp.to_h, script_src: %w(anothercdn.com))
105
142
  csp = ContentSecurityPolicy.new(combined_config)
106
- expect(csp.name).to eq(CSP::HEADER_NAME)
143
+ expect(csp.name).to eq(ContentSecurityPolicyConfig::HEADER_NAME)
107
144
  expect(csp.value).to eq("default-src https:; script-src https: anothercdn.com")
108
145
  end
109
146
 
@@ -115,7 +152,7 @@ module SecureHeaders
115
152
  }.freeze
116
153
  end
117
154
  report_uri = "https://report-uri.io/asdf"
118
- combined_config = CSP.combine_policies(Configuration.get.csp, report_uri: [report_uri])
155
+ combined_config = ContentSecurityPolicy.combine_policies(Configuration.get.csp.to_h, report_uri: [report_uri])
119
156
  csp = ContentSecurityPolicy.new(combined_config, USER_AGENTS[:firefox])
120
157
  expect(csp.value).to include("report-uri #{report_uri}")
121
158
  end
@@ -127,12 +164,12 @@ module SecureHeaders
127
164
  report_only: false
128
165
  }.freeze
129
166
  end
130
- non_default_source_additions = CSP::NON_FETCH_SOURCES.each_with_object({}) do |directive, hash|
167
+ non_default_source_additions = ContentSecurityPolicy::NON_FETCH_SOURCES.each_with_object({}) do |directive, hash|
131
168
  hash[directive] = %w("http://example.org)
132
169
  end
133
- combined_config = CSP.combine_policies(Configuration.get.csp, non_default_source_additions)
170
+ combined_config = ContentSecurityPolicy.combine_policies(Configuration.get.csp.to_h, non_default_source_additions)
134
171
 
135
- CSP::NON_FETCH_SOURCES.each do |directive|
172
+ ContentSecurityPolicy::NON_FETCH_SOURCES.each do |directive|
136
173
  expect(combined_config[directive]).to eq(%w("http://example.org))
137
174
  end
138
175
 
@@ -146,9 +183,9 @@ module SecureHeaders
146
183
  report_only: false
147
184
  }
148
185
  end
149
- combined_config = CSP.combine_policies(Configuration.get.csp, report_only: true)
186
+ combined_config = ContentSecurityPolicy.combine_policies(Configuration.get.csp.to_h, report_only: true)
150
187
  csp = ContentSecurityPolicy.new(combined_config, USER_AGENTS[:firefox])
151
- expect(csp.name).to eq(CSP::REPORT_ONLY)
188
+ expect(csp.name).to eq(ContentSecurityPolicyReportOnlyConfig::HEADER_NAME)
152
189
  end
153
190
 
154
191
  it "overrides the :block_all_mixed_content flag" do
@@ -158,7 +195,7 @@ module SecureHeaders
158
195
  block_all_mixed_content: false
159
196
  }
160
197
  end
161
- combined_config = CSP.combine_policies(Configuration.get.csp, block_all_mixed_content: true)
198
+ combined_config = ContentSecurityPolicy.combine_policies(Configuration.get.csp.to_h, block_all_mixed_content: true)
162
199
  csp = ContentSecurityPolicy.new(combined_config)
163
200
  expect(csp.value).to eq("default-src https:; block-all-mixed-content")
164
201
  end
@@ -168,23 +205,9 @@ module SecureHeaders
168
205
  config.csp = OPT_OUT
169
206
  end
170
207
  expect do
171
- CSP.combine_policies(Configuration.get.csp, script_src: %w(anothercdn.com))
208
+ ContentSecurityPolicy.combine_policies(Configuration.get.csp.to_h, script_src: %w(anothercdn.com))
172
209
  end.to raise_error(ContentSecurityPolicyConfigError)
173
210
  end
174
211
  end
175
-
176
- describe "#idempotent_additions?" do
177
- specify { expect(ContentSecurityPolicy.idempotent_additions?(OPT_OUT, script_src: %w(b.com))).to be false }
178
- specify { expect(ContentSecurityPolicy.idempotent_additions?({script_src: %w(a.com b.com)}, script_src: %w(c.com))).to be false }
179
- specify { expect(ContentSecurityPolicy.idempotent_additions?({script_src: %w(a.com b.com)}, style_src: %w(b.com))).to be false }
180
- specify { expect(ContentSecurityPolicy.idempotent_additions?({script_src: %w(a.com b.com)}, script_src: %w(a.com b.com c.com))).to be false }
181
-
182
- specify { expect(ContentSecurityPolicy.idempotent_additions?({script_src: %w(a.com b.com)}, script_src: %w(b.com))).to be true }
183
- specify { expect(ContentSecurityPolicy.idempotent_additions?({script_src: %w(a.com b.com)}, script_src: %w(b.com a.com))).to be true }
184
- specify { expect(ContentSecurityPolicy.idempotent_additions?({script_src: %w(a.com b.com)}, script_src: %w())).to be true }
185
- specify { expect(ContentSecurityPolicy.idempotent_additions?({script_src: %w(a.com b.com)}, script_src: [nil])).to be true }
186
- specify { expect(ContentSecurityPolicy.idempotent_additions?({script_src: %w(a.com b.com)}, style_src: [nil])).to be true }
187
- specify { expect(ContentSecurityPolicy.idempotent_additions?({script_src: %w(a.com b.com)}, style_src: nil)).to be true }
188
- end
189
212
  end
190
213
  end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ module SecureHeaders
4
+ describe ReferrerPolicy do
5
+ specify { expect(ReferrerPolicy.make_header).to eq([ReferrerPolicy::HEADER_NAME, "origin-when-cross-origin"]) }
6
+ specify { expect(ReferrerPolicy.make_header('no-referrer')).to eq([ReferrerPolicy::HEADER_NAME, "no-referrer"]) }
7
+
8
+ context "valid configuration values" do
9
+ it "accepts 'no-referrer'" do
10
+ expect do
11
+ ReferrerPolicy.validate_config!("no-referrer")
12
+ end.not_to raise_error
13
+ end
14
+
15
+ it "accepts 'no-referrer-when-downgrade'" do
16
+ expect do
17
+ ReferrerPolicy.validate_config!("no-referrer-when-downgrade")
18
+ end.not_to raise_error
19
+ end
20
+
21
+ it "accepts 'same-origin'" do
22
+ expect do
23
+ ReferrerPolicy.validate_config!("same-origin")
24
+ end.not_to raise_error
25
+ end
26
+
27
+ it "accepts 'strict-origin'" do
28
+ expect do
29
+ ReferrerPolicy.validate_config!("strict-origin")
30
+ end.not_to raise_error
31
+ end
32
+
33
+ it "accepts 'strict-origin-when-cross-origin'" do
34
+ expect do
35
+ ReferrerPolicy.validate_config!("strict-origin-when-cross-origin")
36
+ end.not_to raise_error
37
+ end
38
+
39
+ it "accepts 'origin'" do
40
+ expect do
41
+ ReferrerPolicy.validate_config!("origin")
42
+ end.not_to raise_error
43
+ end
44
+
45
+ it "accepts 'origin-when-cross-origin'" do
46
+ expect do
47
+ ReferrerPolicy.validate_config!("origin-when-cross-origin")
48
+ end.not_to raise_error
49
+ end
50
+
51
+ it "accepts 'unsafe-url'" do
52
+ expect do
53
+ ReferrerPolicy.validate_config!("unsafe-url")
54
+ end.not_to raise_error
55
+ end
56
+
57
+ it "accepts nil" do
58
+ expect do
59
+ ReferrerPolicy.validate_config!(nil)
60
+ end.not_to raise_error
61
+ end
62
+ end
63
+
64
+ context 'invlaid configuration values' do
65
+ it "doesn't accept invalid values" do
66
+ expect do
67
+ ReferrerPolicy.validate_config!("open")
68
+ end.to raise_error(ReferrerPolicyConfigError)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -13,6 +13,24 @@ module SecureHeaders
13
13
  Configuration.default
14
14
  end
15
15
 
16
+ it "warns if the hpkp report-uri host is the same as the current host" do
17
+ report_host = "report-uri.io"
18
+ Configuration.default do |config|
19
+ config.hpkp = {
20
+ max_age: 10000000,
21
+ pins: [
22
+ {sha256: 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c'},
23
+ {sha256: '73a2c64f9545172c1195efb6616ca5f7afd1df6f245407cafb90de3998a1c97f'}
24
+ ],
25
+ report_uri: "https://#{report_host}/example-hpkp"
26
+ }
27
+ end
28
+
29
+ expect(Kernel).to receive(:warn).with(Middleware::HPKP_SAME_HOST_WARNING)
30
+
31
+ middleware.call(Rack::MockRequest.env_for("https://#{report_host}", {}))
32
+ end
33
+
16
34
  it "sets the headers" do
17
35
  _, env = middleware.call(Rack::MockRequest.env_for("https://looocalhost", {}))
18
36
  expect_default_values(env)
@@ -33,7 +51,7 @@ module SecureHeaders
33
51
  SecureHeaders.use_secure_headers_override(request, "my_custom_config")
34
52
  expect(request.env[SECURE_HEADERS_CONFIG]).to be(Configuration.get("my_custom_config"))
35
53
  _, env = middleware.call request.env
36
- expect(env[CSP::HEADER_NAME]).to match("example.org")
54
+ expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match("example.org")
37
55
  end
38
56
 
39
57
  context "secure_cookies" do
@@ -87,6 +105,18 @@ module SecureHeaders
87
105
  _, env = cookie_middleware.call request.env
88
106
  expect(env['Set-Cookie']).to eq("foo=bar")
89
107
  end
108
+
109
+ it "sets the secure cookie flag correctly on interleaved http/https requests" do
110
+ Configuration.default { |config| config.cookies = { secure: true } }
111
+
112
+ request = Rack::Request.new("HTTPS" => "off")
113
+ _, env = cookie_middleware.call request.env
114
+ expect(env['Set-Cookie']).to eq("foo=bar")
115
+
116
+ request = Rack::Request.new("HTTPS" => "on")
117
+ _, env = cookie_middleware.call request.env
118
+ expect(env['Set-Cookie']).to eq("foo=bar; secure")
119
+ end
90
120
  end
91
121
  end
92
122
  end
@@ -27,7 +27,16 @@ class Message < ERB
27
27
  background-color: black;
28
28
  }
29
29
  <% end %>
30
- <%= @name %>
30
+
31
+ <script nonce="<%= content_security_policy_script_nonce %>">
32
+ alert(1)
33
+ </script>
34
+
35
+ <style nonce="<%= content_security_policy_style_nonce %>">
36
+ body {
37
+ background-color: black;
38
+ }
39
+ </style>
31
40
 
32
41
  TEMPLATE
33
42
  end
@@ -72,8 +81,11 @@ module SecureHeaders
72
81
 
73
82
  before(:all) do
74
83
  Configuration.default do |config|
75
- config.csp[:script_src] = %w('self')
76
- config.csp[:style_src] = %w('self')
84
+ config.csp = {
85
+ :default_src => %w('self'),
86
+ :script_src => %w('self'),
87
+ :style_src => %w('self')
88
+ }
77
89
  end
78
90
  end
79
91
 
@@ -115,10 +127,10 @@ module SecureHeaders
115
127
  Message.new(request).result
116
128
  _, env = middleware.call request.env
117
129
 
118
- expect(env[CSP::HEADER_NAME]).to match(/script-src[^;]*'#{Regexp.escape(expected_hash)}'/)
119
- expect(env[CSP::HEADER_NAME]).to match(/script-src[^;]*'nonce-abc123'/)
120
- expect(env[CSP::HEADER_NAME]).to match(/style-src[^;]*'nonce-abc123'/)
121
- expect(env[CSP::HEADER_NAME]).to match(/style-src[^;]*'#{Regexp.escape(expected_style_hash)}'/)
130
+ expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match(/script-src[^;]*'#{Regexp.escape(expected_hash)}'/)
131
+ expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match(/script-src[^;]*'nonce-abc123'/)
132
+ expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match(/style-src[^;]*'nonce-abc123'/)
133
+ expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match(/style-src[^;]*'#{Regexp.escape(expected_style_hash)}'/)
122
134
  end
123
135
  end
124
136
  end