secure_headers 3.0.0 → 3.1.1

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.
@@ -2,11 +2,11 @@ require "spec_helper"
2
2
 
3
3
  module SecureHeaders
4
4
  describe Middleware do
5
- let(:app) { ->(env) { [200, env, "app"] } }
5
+ let(:app) { lambda { |env| [200, env, "app"] } }
6
+ let(:cookie_app) { lambda { |env| [200, env.merge("Set-Cookie" => "foo=bar"), "app"] } }
6
7
 
7
- let :middleware do
8
- Middleware.new(app)
9
- end
8
+ let(:middleware) { Middleware.new(app) }
9
+ let(:cookie_middleware) { Middleware.new(cookie_app) }
10
10
 
11
11
  before(:each) do
12
12
  reset_config
@@ -33,8 +33,27 @@ module SecureHeaders
33
33
  end
34
34
  request = Rack::Request.new({})
35
35
  SecureHeaders.use_secure_headers_override(request, "my_custom_config")
36
+ expect(request.env[SECURE_HEADERS_CONFIG]).to be(Configuration.get("my_custom_config"))
36
37
  _, env = middleware.call request.env
37
38
  expect(env[CSP::HEADER_NAME]).to match("example.org")
38
39
  end
40
+
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)
47
+ end
48
+ end
49
+
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)
56
+ end
57
+ end
39
58
  end
40
59
  end
@@ -2,57 +2,49 @@ require 'spec_helper'
2
2
 
3
3
  module SecureHeaders
4
4
  describe SecureHeaders do
5
- example_hpkp_config = {
6
- max_age: 1_000_000,
7
- include_subdomains: true,
8
- report_uri: '//example.com/uri-directive',
9
- pins: [
10
- { sha256: 'abc' },
11
- { sha256: '123' }
12
- ]
13
- }
14
-
15
- example_hpkp_config_value = %(max-age=1000000; pin-sha256="abc"; pin-sha256="123"; report-uri="//example.com/uri-directive"; includeSubDomains)
16
-
17
5
  before(:each) do
18
6
  reset_config
19
- @request = Rack::Request.new("HTTP_X_FORWARDED_SSL" => "on")
20
7
  end
21
8
 
9
+ let(:request) { Rack::Request.new("HTTP_X_FORWARDED_SSL" => "on") }
10
+
22
11
  it "raises a NotYetConfiguredError if default has not been set" do
23
12
  expect do
24
- SecureHeaders.header_hash_for(@request)
13
+ SecureHeaders.header_hash_for(request)
25
14
  end.to raise_error(Configuration::NotYetConfiguredError)
26
15
  end
27
16
 
28
17
  it "raises a NotYetConfiguredError if trying to opt-out of unconfigured headers" do
29
18
  expect do
30
- SecureHeaders.opt_out_of_header(@request, CSP::CONFIG_KEY)
19
+ SecureHeaders.opt_out_of_header(request, CSP::CONFIG_KEY)
31
20
  end.to raise_error(Configuration::NotYetConfiguredError)
32
21
  end
33
22
 
34
23
  describe "#header_hash_for" do
35
24
  it "allows you to opt out of individual headers" do
36
25
  Configuration.default
37
- SecureHeaders.opt_out_of_header(@request, CSP::CONFIG_KEY)
38
- hash = SecureHeaders.header_hash_for(@request)
26
+ SecureHeaders.opt_out_of_header(request, CSP::CONFIG_KEY)
27
+ SecureHeaders.opt_out_of_header(request, XContentTypeOptions::CONFIG_KEY)
28
+ hash = SecureHeaders.header_hash_for(request)
39
29
  expect(hash['Content-Security-Policy-Report-Only']).to be_nil
40
30
  expect(hash['Content-Security-Policy']).to be_nil
31
+ expect(hash['X-Content-Type-Options']).to be_nil
41
32
  end
42
33
 
43
34
  it "allows you to opt out entirely" do
44
35
  Configuration.default
45
- SecureHeaders.opt_out_of_all_protection(@request)
46
- hash = SecureHeaders.header_hash_for(@request)
36
+ SecureHeaders.opt_out_of_all_protection(request)
37
+ hash = SecureHeaders.header_hash_for(request)
47
38
  ALL_HEADER_CLASSES.each do |klass|
48
39
  expect(hash[klass::CONFIG_KEY]).to be_nil
49
40
  end
41
+ expect(hash.count).to eq(0)
50
42
  end
51
43
 
52
44
  it "allows you to override X-Frame-Options settings" do
53
45
  Configuration.default
54
- SecureHeaders.override_x_frame_options(@request, XFrameOptions::DENY)
55
- hash = SecureHeaders.header_hash_for(@request)
46
+ SecureHeaders.override_x_frame_options(request, XFrameOptions::DENY)
47
+ hash = SecureHeaders.header_hash_for(request)
56
48
  expect(hash[XFrameOptions::HEADER_NAME]).to eq(XFrameOptions::DENY)
57
49
  end
58
50
 
@@ -62,17 +54,36 @@ module SecureHeaders
62
54
  config.csp = OPT_OUT
63
55
  end
64
56
 
65
- SecureHeaders.override_x_frame_options(@request, XFrameOptions::SAMEORIGIN)
66
- SecureHeaders.override_content_security_policy_directives(@request, default_src: %w(https:), script_src: %w('self'))
57
+ SecureHeaders.override_x_frame_options(request, XFrameOptions::SAMEORIGIN)
58
+ SecureHeaders.override_content_security_policy_directives(request, default_src: %w(https:), script_src: %w('self'))
67
59
 
68
- hash = SecureHeaders.header_hash_for(@request)
60
+ hash = SecureHeaders.header_hash_for(request)
69
61
  expect(hash[CSP::HEADER_NAME]).to eq("default-src https:; script-src 'self'")
70
62
  expect(hash[XFrameOptions::HEADER_NAME]).to eq(XFrameOptions::SAMEORIGIN)
71
63
  end
72
64
 
65
+ it "produces a UA-specific CSP when overriding (and busting the cache)" do
66
+ config = Configuration.default do |config|
67
+ config.csp = {
68
+ default_src: %w('self'),
69
+ child_src: %w('self'), #unsupported by firefox
70
+ frame_src: %w('self')
71
+ }
72
+ end
73
+ firefox_request = Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:firefox]))
74
+
75
+ # append an unsupported directive
76
+ SecureHeaders.override_content_security_policy_directives(firefox_request, plugin_types: %w(flash))
77
+ # append a supported directive
78
+ SecureHeaders.override_content_security_policy_directives(firefox_request, script_src: %w('self'))
79
+
80
+ hash = SecureHeaders.header_hash_for(firefox_request)
81
+ expect(hash[CSP::HEADER_NAME]).to eq("default-src 'self'; frame-src 'self'; script-src 'self'")
82
+ end
83
+
73
84
  it "produces a hash of headers with default config" do
74
85
  Configuration.default
75
- hash = SecureHeaders.header_hash_for(@request)
86
+ hash = SecureHeaders.header_hash_for(request)
76
87
  expect_default_values(hash)
77
88
  end
78
89
 
@@ -87,7 +98,15 @@ module SecureHeaders
87
98
  it "does not set the HPKP header if request is over HTTP" do
88
99
  plaintext_request = Rack::Request.new({})
89
100
  Configuration.default do |config|
90
- config.hpkp = example_hpkp_config
101
+ config.hpkp = {
102
+ max_age: 1_000_000,
103
+ include_subdomains: true,
104
+ report_uri: '//example.com/uri-directive',
105
+ pins: [
106
+ { sha256: 'abc' },
107
+ { sha256: '123' }
108
+ ]
109
+ }
91
110
  end
92
111
 
93
112
  expect(SecureHeaders.header_hash_for(plaintext_request)[PublicKeyPins::HEADER_NAME]).to be_nil
@@ -102,26 +121,67 @@ module SecureHeaders
102
121
  }
103
122
  end
104
123
 
105
- SecureHeaders.append_content_security_policy_directives(@request, script_src: %w(anothercdn.com))
106
- hash = SecureHeaders.header_hash_for(@request)
124
+ SecureHeaders.append_content_security_policy_directives(request, script_src: %w(anothercdn.com))
125
+ hash = SecureHeaders.header_hash_for(request)
107
126
  expect(hash[CSP::HEADER_NAME]).to eq("default-src 'self'; script-src mycdn.com 'unsafe-inline' anothercdn.com")
108
127
  end
109
128
 
129
+ it "dups global configuration just once when overriding n times and only calls idempotent_additions? once" do
130
+ Configuration.default do |config|
131
+ config.csp = {
132
+ default_src: %w('self')
133
+ }
134
+ end
135
+
136
+ expect(CSP).to receive(:idempotent_additions?).once
137
+
138
+ # before an override occurs, the env is empty
139
+ expect(request.env[SECURE_HEADERS_CONFIG]).to be_nil
140
+
141
+ SecureHeaders.append_content_security_policy_directives(request, script_src: %w(anothercdn.com))
142
+ new_config = SecureHeaders.config_for(request)
143
+ expect(new_config).to_not be(Configuration.get)
144
+
145
+ SecureHeaders.override_content_security_policy_directives(request, script_src: %w(yet.anothercdn.com))
146
+ current_config = SecureHeaders.config_for(request)
147
+ expect(current_config).to be(new_config)
148
+
149
+ SecureHeaders.header_hash_for(request)
150
+ end
151
+
152
+ it "doesn't allow you to muck with csp configs when a dynamic policy is in use" do
153
+ default_config = Configuration.default
154
+ expect { default_config.csp = {} }.to raise_error(NoMethodError)
155
+
156
+ # config is frozen
157
+ expect { default_config.send(:csp=, {}) }.to raise_error(RuntimeError)
158
+
159
+ SecureHeaders.append_content_security_policy_directives(request, script_src: %w(anothercdn.com))
160
+ new_config = SecureHeaders.config_for(request)
161
+ expect { new_config.send(:csp=, {}) }.to raise_error(Configuration::IllegalPolicyModificationError)
162
+
163
+ expect do
164
+ new_config.instance_eval do
165
+ new_config.csp = {}
166
+ end
167
+ end.to raise_error(Configuration::IllegalPolicyModificationError)
168
+ end
169
+
110
170
  it "overrides individual directives" do
111
171
  Configuration.default do |config|
112
172
  config.csp = {
113
173
  default_src: %w('self')
114
174
  }
115
175
  end
116
- SecureHeaders.override_content_security_policy_directives(@request, default_src: %w('none'))
117
- hash = SecureHeaders.header_hash_for(@request)
176
+ SecureHeaders.override_content_security_policy_directives(request, default_src: %w('none'))
177
+ hash = SecureHeaders.header_hash_for(request)
118
178
  expect(hash[CSP::HEADER_NAME]).to eq("default-src 'none'")
119
179
  end
120
180
 
121
181
  it "overrides non-existant directives" do
122
182
  Configuration.default
123
- SecureHeaders.override_content_security_policy_directives(@request, img_src: [ContentSecurityPolicy::DATA_PROTOCOL])
124
- hash = SecureHeaders.header_hash_for(@request)
183
+ SecureHeaders.override_content_security_policy_directives(request, img_src: [ContentSecurityPolicy::DATA_PROTOCOL])
184
+ hash = SecureHeaders.header_hash_for(request)
125
185
  expect(hash[CSP::HEADER_NAME]).to eq("default-src https:; img-src data:")
126
186
  end
127
187
 
@@ -134,9 +194,9 @@ module SecureHeaders
134
194
  }
135
195
  end
136
196
 
137
- request = Rack::Request.new(@request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:safari5]))
138
- nonce = SecureHeaders.content_security_policy_script_nonce(request)
139
- hash = SecureHeaders.header_hash_for(request)
197
+ safari_request = Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:safari5]))
198
+ nonce = SecureHeaders.content_security_policy_script_nonce(safari_request)
199
+ hash = SecureHeaders.header_hash_for(safari_request)
140
200
  expect(hash[CSP::HEADER_NAME]).to eq("default-src 'self'; script-src mycdn.com 'unsafe-inline'; style-src 'self'")
141
201
  end
142
202
 
@@ -149,15 +209,15 @@ module SecureHeaders
149
209
  }
150
210
  end
151
211
 
152
- request = Rack::Request.new(@request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:chrome]))
153
- nonce = SecureHeaders.content_security_policy_script_nonce(request)
212
+ chrome_request = Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:chrome]))
213
+ nonce = SecureHeaders.content_security_policy_script_nonce(chrome_request)
154
214
 
155
215
  # simulate the nonce being used multiple times in a request:
156
- SecureHeaders.content_security_policy_script_nonce(request)
157
- SecureHeaders.content_security_policy_script_nonce(request)
158
- SecureHeaders.content_security_policy_script_nonce(request)
216
+ SecureHeaders.content_security_policy_script_nonce(chrome_request)
217
+ SecureHeaders.content_security_policy_script_nonce(chrome_request)
218
+ SecureHeaders.content_security_policy_script_nonce(chrome_request)
159
219
 
160
- hash = SecureHeaders.header_hash_for(request)
220
+ hash = SecureHeaders.header_hash_for(chrome_request)
161
221
  expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src mycdn.com 'nonce-#{nonce}'; style-src 'self'")
162
222
  end
163
223
  end
data/spec/spec_helper.rb CHANGED
@@ -3,9 +3,12 @@ require 'rspec'
3
3
  require 'rack'
4
4
  require 'pry-nav'
5
5
 
6
+ require 'coveralls'
7
+ Coveralls.wear!
8
+
6
9
  require File.join(File.dirname(__FILE__), '..', 'lib', 'secure_headers')
7
10
 
8
- ENV["RAILS_ENV"] = "test"
11
+
9
12
 
10
13
  USER_AGENTS = {
11
14
  firefox: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1',
data/upgrading-to-3-0.md CHANGED
@@ -20,6 +20,7 @@ Migrating to 3.x from <= 2.x
20
20
  1. Convert all instances of `self`/`none`/`eval`/`inline` to the corresponding values in the above table.
21
21
  1. Convert all CSP space-delimited directives to an array of strings.
22
22
  1. Convert all `enforce: true|false` to `report_only: true|false`.
23
+ 1. Remove `ensure_security_headers` from controllers (3.x uses a middleware instead).
23
24
 
24
25
  Everything is terrible, why should I upgrade?
25
26
  ==
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.0.0
4
+ version: 3.1.1
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-02-18 00:00:00.000000000 Z
11
+ date: 2016-03-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -59,6 +59,7 @@ files:
59
59
  - lib/secure_headers.rb
60
60
  - lib/secure_headers/configuration.rb
61
61
  - lib/secure_headers/headers/content_security_policy.rb
62
+ - lib/secure_headers/headers/policy_management.rb
62
63
  - lib/secure_headers/headers/public_key_pins.rb
63
64
  - lib/secure_headers/headers/strict_transport_security.rb
64
65
  - lib/secure_headers/headers/x_content_type_options.rb
@@ -67,12 +68,12 @@ files:
67
68
  - lib/secure_headers/headers/x_permitted_cross_domain_policies.rb
68
69
  - lib/secure_headers/headers/x_xss_protection.rb
69
70
  - lib/secure_headers/middleware.rb
70
- - lib/secure_headers/padrino.rb
71
71
  - lib/secure_headers/railtie.rb
72
72
  - lib/secure_headers/view_helper.rb
73
73
  - secure_headers.gemspec
74
74
  - spec/lib/secure_headers/configuration_spec.rb
75
75
  - spec/lib/secure_headers/headers/content_security_policy_spec.rb
76
+ - spec/lib/secure_headers/headers/policy_management_spec.rb
76
77
  - spec/lib/secure_headers/headers/public_key_pins_spec.rb
77
78
  - spec/lib/secure_headers/headers/strict_transport_security_spec.rb
78
79
  - spec/lib/secure_headers/headers/x_content_type_options_spec.rb
@@ -83,7 +84,6 @@ files:
83
84
  - spec/lib/secure_headers/middleware_spec.rb
84
85
  - spec/lib/secure_headers_spec.rb
85
86
  - spec/spec_helper.rb
86
- - travis.sh
87
87
  - upgrading-to-3-0.md
88
88
  homepage: https://github.com/twitter/secureheaders
89
89
  licenses:
@@ -113,6 +113,7 @@ summary: Add easily configured security headers to responses including content-s
113
113
  test_files:
114
114
  - spec/lib/secure_headers/configuration_spec.rb
115
115
  - spec/lib/secure_headers/headers/content_security_policy_spec.rb
116
+ - spec/lib/secure_headers/headers/policy_management_spec.rb
116
117
  - spec/lib/secure_headers/headers/public_key_pins_spec.rb
117
118
  - spec/lib/secure_headers/headers/strict_transport_security_spec.rb
118
119
  - spec/lib/secure_headers/headers/x_content_type_options_spec.rb
@@ -1,13 +0,0 @@
1
- module SecureHeaders
2
- module Padrino
3
- class << self
4
- ##
5
- # Main class that register this extension.
6
- #
7
- def registered(app)
8
- app.helpers SecureHeaders::InstanceMethods
9
- end
10
- alias_method :included, :registered
11
- end
12
- end
13
- end
data/travis.sh DELETED
@@ -1,10 +0,0 @@
1
- #! /bin/sh
2
-
3
- bundle install >> /dev/null &&
4
- bundle exec rspec --format progress spec &&
5
- cd fixtures/rails_3_2_12 &&
6
- bundle install >> /dev/null &&
7
- bundle exec rspec --format progress spec &&
8
- cd ../../fixtures/rails_3_2_12_no_init &&
9
- bundle install >> /dev/null &&
10
- bundle exec rspec spec