secure_headers 3.1.2 → 3.3.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.
@@ -10,9 +10,25 @@ module SecureHeaders
10
10
 
11
11
  before(:each) do
12
12
  reset_config
13
+ Configuration.default
14
+ end
15
+
16
+ it "warns if the hpkp report-uri host is the same as the current host" do
17
+ report_host = "report-uri.io"
13
18
  Configuration.default do |config|
14
- # use all default provided by the library
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
+ }
15
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}", {}))
16
32
  end
17
33
 
18
34
  it "sets the headers" do
@@ -38,21 +54,56 @@ module SecureHeaders
38
54
  expect(env[CSP::HEADER_NAME]).to match("example.org")
39
55
  end
40
56
 
41
- context "cookies should be flagged" do
42
- it "flags cookies as secure" do
43
- Configuration.default { |config| config.secure_cookies = true }
44
- request = Rack::MockRequest.new(cookie_middleware)
45
- response = request.get '/'
46
- expect(response.headers['Set-Cookie']).to match(Middleware::SECURE_COOKIE_REGEXP)
57
+ context "secure_cookies" do
58
+ context "cookies should be flagged" do
59
+ it "flags cookies as secure" do
60
+ capture_warning do
61
+ Configuration.default { |config| config.secure_cookies = true }
62
+ end
63
+ request = Rack::Request.new("HTTPS" => "on")
64
+ _, env = cookie_middleware.call request.env
65
+ expect(env['Set-Cookie']).to eq("foo=bar; secure")
66
+ end
67
+ end
68
+
69
+ context "cookies should not be flagged" do
70
+ it "does not flags cookies as secure" do
71
+ capture_warning do
72
+ Configuration.default { |config| config.secure_cookies = false }
73
+ end
74
+ request = Rack::Request.new("HTTPS" => "on")
75
+ _, env = cookie_middleware.call request.env
76
+ expect(env['Set-Cookie']).to eq("foo=bar")
77
+ end
47
78
  end
48
79
  end
49
80
 
50
- context "cookies should not be flagged" do
51
- it "does not flags cookies as secure" do
52
- Configuration.default { |config| config.secure_cookies = false }
53
- request = Rack::MockRequest.new(cookie_middleware)
54
- response = request.get '/'
55
- expect(response.headers['Set-Cookie']).not_to match(Middleware::SECURE_COOKIE_REGEXP)
81
+ context "cookies" do
82
+ it "flags cookies from configuration" do
83
+ Configuration.default { |config| config.cookies = { secure: true, httponly: true } }
84
+ request = Rack::Request.new("HTTPS" => "on")
85
+ _, env = cookie_middleware.call request.env
86
+
87
+ expect(env['Set-Cookie']).to eq("foo=bar; secure; HttpOnly")
88
+ end
89
+
90
+ it "flags cookies with a combination of SameSite configurations" do
91
+ cookie_middleware = Middleware.new(lambda { |env| [200, env.merge("Set-Cookie" => ["_session=foobar", "_guest=true"]), "app"] })
92
+
93
+ Configuration.default { |config| config.cookies = { samesite: { lax: { except: ["_session"] }, strict: { only: ["_session"] } } } }
94
+ request = Rack::Request.new("HTTPS" => "on")
95
+ _, env = cookie_middleware.call request.env
96
+
97
+ expect(env['Set-Cookie']).to match("_session=foobar; SameSite=Strict")
98
+ expect(env['Set-Cookie']).to match("_guest=true; SameSite=Lax")
99
+ end
100
+
101
+ it "disables secure cookies for non-https requests" do
102
+ Configuration.default { |config| config.cookies = { secure: true } }
103
+
104
+ request = Rack::Request.new("HTTPS" => "off")
105
+ _, env = cookie_middleware.call request.env
106
+ expect(env['Set-Cookie']).to eq("foo=bar")
56
107
  end
57
108
  end
58
109
  end
@@ -0,0 +1,125 @@
1
+ require "spec_helper"
2
+ require "erb"
3
+
4
+ class Message < ERB
5
+ include SecureHeaders::ViewHelpers
6
+
7
+ def self.template
8
+ <<-TEMPLATE
9
+ <% hashed_javascript_tag(raise_error_on_unrecognized_hash = true) do %>
10
+ console.log(1)
11
+ <% end %>
12
+
13
+ <% hashed_style_tag do %>
14
+ body {
15
+ background-color: black;
16
+ }
17
+ <% end %>
18
+
19
+ <% nonced_javascript_tag do %>
20
+ body {
21
+ console.log(1)
22
+ }
23
+ <% end %>
24
+
25
+ <% nonced_style_tag do %>
26
+ body {
27
+ background-color: black;
28
+ }
29
+ <% end %>
30
+ <%= @name %>
31
+
32
+ TEMPLATE
33
+ end
34
+
35
+ def initialize(request, options = {})
36
+ @virtual_path = "/asdfs/index"
37
+ @_request = request
38
+ @template = self.class.template
39
+ super(@template)
40
+ end
41
+
42
+ def capture(*args)
43
+ yield(*args)
44
+ end
45
+
46
+ def content_tag(type, content = nil, options = nil, &block)
47
+ content = if block_given?
48
+ capture(block)
49
+ end
50
+
51
+ if options.is_a?(Hash)
52
+ options = options.map {|k,v| " #{k}=#{v}"}
53
+ end
54
+ "<#{type}#{options}>#{content}</#{type}>"
55
+ end
56
+
57
+ def result
58
+ super(binding)
59
+ end
60
+
61
+ def request
62
+ @_request
63
+ end
64
+ end
65
+
66
+ module SecureHeaders
67
+ describe ViewHelpers do
68
+ let(:app) { lambda { |env| [200, env, "app"] } }
69
+ let(:middleware) { Middleware.new(app) }
70
+ let(:request) { Rack::Request.new("HTTP_USER_AGENT" => USER_AGENTS[:chrome]) }
71
+ let(:filename) { "app/views/asdfs/index.html.erb" }
72
+
73
+ before(:all) do
74
+ Configuration.default do |config|
75
+ config.csp[:script_src] = %w('self')
76
+ config.csp[:style_src] = %w('self')
77
+ end
78
+ end
79
+
80
+ after(:each) do
81
+ Configuration.instance_variable_set(:@script_hashes, nil)
82
+ Configuration.instance_variable_set(:@style_hashes, nil)
83
+ end
84
+
85
+ it "raises an error when using hashed content without precomputed hashes" do
86
+ expect {
87
+ Message.new(request).result
88
+ }.to raise_error(ViewHelpers::UnexpectedHashedScriptException)
89
+ end
90
+
91
+ it "raises an error when using hashed content with precomputed hashes, but none for the given file" do
92
+ Configuration.instance_variable_set(:@script_hashes, filename.reverse => ["'sha256-123'"])
93
+ expect {
94
+ Message.new(request).result
95
+ }.to raise_error(ViewHelpers::UnexpectedHashedScriptException)
96
+ end
97
+
98
+ it "raises an error when using previously unknown hashed content with precomputed hashes for a given file" do
99
+ Configuration.instance_variable_set(:@script_hashes, filename => ["'sha256-123'"])
100
+ expect {
101
+ Message.new(request).result
102
+ }.to raise_error(ViewHelpers::UnexpectedHashedScriptException)
103
+ end
104
+
105
+ it "adds known hash values to the corresponding headers when the helper is used" do
106
+ begin
107
+ allow(SecureRandom).to receive(:base64).and_return("abc123")
108
+
109
+ expected_hash = "sha256-3/URElR9+3lvLIouavYD/vhoICSNKilh15CzI/nKqg8="
110
+ Configuration.instance_variable_set(:@script_hashes, filename => ["'#{expected_hash}'"])
111
+ expected_style_hash = "sha256-7oYK96jHg36D6BM042er4OfBnyUDTG3pH1L8Zso3aGc="
112
+ Configuration.instance_variable_set(:@style_hashes, filename => ["'#{expected_style_hash}'"])
113
+
114
+ # render erb that calls out to helpers.
115
+ Message.new(request).result
116
+ _, env = middleware.call request.env
117
+
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)}'/)
122
+ end
123
+ end
124
+ end
125
+ end
@@ -20,6 +20,13 @@ module SecureHeaders
20
20
  end.to raise_error(Configuration::NotYetConfiguredError)
21
21
  end
22
22
 
23
+ it "raises and ArgumentError when referencing an override that has not been set" do
24
+ expect do
25
+ Configuration.default
26
+ SecureHeaders.use_secure_headers_override(request, :missing)
27
+ end.to raise_error(ArgumentError)
28
+ end
29
+
23
30
  describe "#header_hash_for" do
24
31
  it "allows you to opt out of individual headers via API" do
25
32
  Configuration.default
@@ -305,6 +312,14 @@ module SecureHeaders
305
312
  end.to raise_error(XPCDPConfigError)
306
313
  end
307
314
 
315
+ it "validates your referrer_policy config upon configuration" do
316
+ expect do
317
+ Configuration.default do |config|
318
+ config.referrer_policy = "lol"
319
+ end
320
+ end.to raise_error(ReferrerPolicyConfigError)
321
+ end
322
+
308
323
  it "validates your hpkp config upon configuration" do
309
324
  expect do
310
325
  Configuration.default do |config|
@@ -312,6 +327,14 @@ module SecureHeaders
312
327
  end
313
328
  end.to raise_error(PublicKeyPinsConfigError)
314
329
  end
330
+
331
+ it "validates your cookies config upon configuration" do
332
+ expect do
333
+ Configuration.default do |config|
334
+ config.cookies = { secure: "lol" }
335
+ end
336
+ end.to raise_error(CookiesConfigError)
337
+ end
315
338
  end
316
339
  end
317
340
  end
data/spec/spec_helper.rb CHANGED
@@ -11,6 +11,7 @@ require File.join(File.dirname(__FILE__), '..', 'lib', 'secure_headers')
11
11
 
12
12
 
13
13
  USER_AGENTS = {
14
+ edge: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246",
14
15
  firefox: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1',
15
16
  chrome: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5',
16
17
  ie: 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/5.0)',
@@ -30,6 +31,7 @@ def expect_default_values(hash)
30
31
  expect(hash[SecureHeaders::XXssProtection::HEADER_NAME]).to eq(SecureHeaders::XXssProtection::DEFAULT_VALUE)
31
32
  expect(hash[SecureHeaders::XContentTypeOptions::HEADER_NAME]).to eq(SecureHeaders::XContentTypeOptions::DEFAULT_VALUE)
32
33
  expect(hash[SecureHeaders::XPermittedCrossDomainPolicies::HEADER_NAME]).to eq(SecureHeaders::XPermittedCrossDomainPolicies::DEFAULT_VALUE)
34
+ expect(hash[SecureHeaders::ReferrerPolicy::HEADER_NAME]).to be_nil
33
35
  end
34
36
 
35
37
  module SecureHeaders
@@ -45,3 +47,15 @@ end
45
47
  def reset_config
46
48
  SecureHeaders::Configuration.clear_configurations
47
49
  end
50
+
51
+ def capture_warning
52
+ begin
53
+ old_stderr = $stderr
54
+ $stderr = StringIO.new
55
+ yield
56
+ result = $stderr.string
57
+ ensure
58
+ $stderr = old_stderr
59
+ end
60
+ result
61
+ end
data/upgrading-to-3-0.md CHANGED
@@ -8,6 +8,7 @@ Changes
8
8
  | Global configuration | `SecureHeaders::Configuration.configure` block | `SecureHeaders::Configuration.default` block |
9
9
  | All headers besides HPKP and CSP | Accept hashes as config values | Must be strings (validated during configuration) |
10
10
  | CSP directive values | Accepted space delimited strings OR arrays of strings | Must be arrays of strings |
11
+ | CSP Nonce values in views | `@content_security_policy_nonce` | `content_security_policy_script_nonce` or `content_security_policy_style_nonce`
11
12
  | `self`/`none` source expressions | could be `self` / `none` / `'self'` / `'none'` | Must be `'self'` or `'none'` |
12
13
  | `inline` / `eval` source expressions | could be `inline`, `eval`, `'unsafe-inline'`, or `'unsafe-eval'` | Must be `'unsafe-eval'` or `'unsafe-inline'` |
13
14
  | Per-action configuration | override [`def secure_header_options_for(header, options)`](https://github.com/twitter/secureheaders/commit/bb9ebc6c12a677aad29af8e0f08ffd1def56efec#diff-04c6e90faac2675aa89e2176d2eec7d8R111) | Use [named overrides](https://github.com/twitter/secureheaders#named-overrides) or [per-action helpers](https://github.com/twitter/secureheaders#per-action-configuration) |
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: secure_headers
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.2
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Neil Matatall
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-30 00:00:00.000000000 Z
11
+ date: 2016-04-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -58,9 +58,12 @@ files:
58
58
  - Rakefile
59
59
  - lib/secure_headers.rb
60
60
  - lib/secure_headers/configuration.rb
61
+ - lib/secure_headers/hash_helper.rb
61
62
  - lib/secure_headers/headers/content_security_policy.rb
63
+ - lib/secure_headers/headers/cookie.rb
62
64
  - lib/secure_headers/headers/policy_management.rb
63
65
  - lib/secure_headers/headers/public_key_pins.rb
66
+ - lib/secure_headers/headers/referrer_policy.rb
64
67
  - lib/secure_headers/headers/strict_transport_security.rb
65
68
  - lib/secure_headers/headers/x_content_type_options.rb
66
69
  - lib/secure_headers/headers/x_download_options.rb
@@ -69,12 +72,16 @@ files:
69
72
  - lib/secure_headers/headers/x_xss_protection.rb
70
73
  - lib/secure_headers/middleware.rb
71
74
  - lib/secure_headers/railtie.rb
75
+ - lib/secure_headers/utils/cookies_config.rb
72
76
  - lib/secure_headers/view_helper.rb
77
+ - lib/tasks/tasks.rake
73
78
  - secure_headers.gemspec
74
79
  - spec/lib/secure_headers/configuration_spec.rb
75
80
  - spec/lib/secure_headers/headers/content_security_policy_spec.rb
81
+ - spec/lib/secure_headers/headers/cookie_spec.rb
76
82
  - spec/lib/secure_headers/headers/policy_management_spec.rb
77
83
  - spec/lib/secure_headers/headers/public_key_pins_spec.rb
84
+ - spec/lib/secure_headers/headers/referrer_policy_spec.rb
78
85
  - spec/lib/secure_headers/headers/strict_transport_security_spec.rb
79
86
  - spec/lib/secure_headers/headers/x_content_type_options_spec.rb
80
87
  - spec/lib/secure_headers/headers/x_download_options_spec.rb
@@ -82,6 +89,7 @@ files:
82
89
  - spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb
83
90
  - spec/lib/secure_headers/headers/x_xss_protection_spec.rb
84
91
  - spec/lib/secure_headers/middleware_spec.rb
92
+ - spec/lib/secure_headers/view_helpers_spec.rb
85
93
  - spec/lib/secure_headers_spec.rb
86
94
  - spec/spec_helper.rb
87
95
  - upgrading-to-3-0.md
@@ -113,8 +121,10 @@ summary: Add easily configured security headers to responses including content-s
113
121
  test_files:
114
122
  - spec/lib/secure_headers/configuration_spec.rb
115
123
  - spec/lib/secure_headers/headers/content_security_policy_spec.rb
124
+ - spec/lib/secure_headers/headers/cookie_spec.rb
116
125
  - spec/lib/secure_headers/headers/policy_management_spec.rb
117
126
  - spec/lib/secure_headers/headers/public_key_pins_spec.rb
127
+ - spec/lib/secure_headers/headers/referrer_policy_spec.rb
118
128
  - spec/lib/secure_headers/headers/strict_transport_security_spec.rb
119
129
  - spec/lib/secure_headers/headers/x_content_type_options_spec.rb
120
130
  - spec/lib/secure_headers/headers/x_download_options_spec.rb
@@ -122,5 +132,6 @@ test_files:
122
132
  - spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb
123
133
  - spec/lib/secure_headers/headers/x_xss_protection_spec.rb
124
134
  - spec/lib/secure_headers/middleware_spec.rb
135
+ - spec/lib/secure_headers/view_helpers_spec.rb
125
136
  - spec/lib/secure_headers_spec.rb
126
137
  - spec/spec_helper.rb