secure_headers 3.0.3 → 3.1.2

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,66 @@ 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
- it "allows you to opt out of individual headers" do
24
+ it "allows you to opt out of individual headers via API" 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
32
+ end
33
+
34
+ it "Carries options over when using overrides" do
35
+ Configuration.default do |config|
36
+ config.x_download_options = OPT_OUT
37
+ config.x_permitted_cross_domain_policies = OPT_OUT
38
+ end
39
+
40
+ Configuration.override(:api) do |config|
41
+ config.x_frame_options = OPT_OUT
42
+ end
43
+
44
+ SecureHeaders.use_secure_headers_override(request, :api)
45
+ hash = SecureHeaders.header_hash_for(request)
46
+ expect(hash['X-Download-Options']).to be_nil
47
+ expect(hash['X-Permitted-Cross-Domain-Policies']).to be_nil
48
+ expect(hash['X-Frame-Options']).to be_nil
41
49
  end
42
50
 
43
51
  it "allows you to opt out entirely" do
44
52
  Configuration.default
45
- SecureHeaders.opt_out_of_all_protection(@request)
46
- hash = SecureHeaders.header_hash_for(@request)
53
+ SecureHeaders.opt_out_of_all_protection(request)
54
+ hash = SecureHeaders.header_hash_for(request)
47
55
  ALL_HEADER_CLASSES.each do |klass|
48
56
  expect(hash[klass::CONFIG_KEY]).to be_nil
49
57
  end
58
+ expect(hash.count).to eq(0)
50
59
  end
51
60
 
52
61
  it "allows you to override X-Frame-Options settings" do
53
62
  Configuration.default
54
- SecureHeaders.override_x_frame_options(@request, XFrameOptions::DENY)
55
- hash = SecureHeaders.header_hash_for(@request)
63
+ SecureHeaders.override_x_frame_options(request, XFrameOptions::DENY)
64
+ hash = SecureHeaders.header_hash_for(request)
56
65
  expect(hash[XFrameOptions::HEADER_NAME]).to eq(XFrameOptions::DENY)
57
66
  end
58
67
 
@@ -62,17 +71,36 @@ module SecureHeaders
62
71
  config.csp = OPT_OUT
63
72
  end
64
73
 
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'))
74
+ SecureHeaders.override_x_frame_options(request, XFrameOptions::SAMEORIGIN)
75
+ SecureHeaders.override_content_security_policy_directives(request, default_src: %w(https:), script_src: %w('self'))
67
76
 
68
- hash = SecureHeaders.header_hash_for(@request)
77
+ hash = SecureHeaders.header_hash_for(request)
69
78
  expect(hash[CSP::HEADER_NAME]).to eq("default-src https:; script-src 'self'")
70
79
  expect(hash[XFrameOptions::HEADER_NAME]).to eq(XFrameOptions::SAMEORIGIN)
71
80
  end
72
81
 
82
+ it "produces a UA-specific CSP when overriding (and busting the cache)" do
83
+ config = Configuration.default do |config|
84
+ config.csp = {
85
+ default_src: %w('self'),
86
+ child_src: %w('self'), #unsupported by firefox
87
+ frame_src: %w('self')
88
+ }
89
+ end
90
+ firefox_request = Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:firefox]))
91
+
92
+ # append an unsupported directive
93
+ SecureHeaders.override_content_security_policy_directives(firefox_request, plugin_types: %w(flash))
94
+ # append a supported directive
95
+ SecureHeaders.override_content_security_policy_directives(firefox_request, script_src: %w('self'))
96
+
97
+ hash = SecureHeaders.header_hash_for(firefox_request)
98
+ expect(hash[CSP::HEADER_NAME]).to eq("default-src 'self'; frame-src 'self'; script-src 'self'")
99
+ end
100
+
73
101
  it "produces a hash of headers with default config" do
74
102
  Configuration.default
75
- hash = SecureHeaders.header_hash_for(@request)
103
+ hash = SecureHeaders.header_hash_for(request)
76
104
  expect_default_values(hash)
77
105
  end
78
106
 
@@ -87,7 +115,15 @@ module SecureHeaders
87
115
  it "does not set the HPKP header if request is over HTTP" do
88
116
  plaintext_request = Rack::Request.new({})
89
117
  Configuration.default do |config|
90
- config.hpkp = example_hpkp_config
118
+ config.hpkp = {
119
+ max_age: 1_000_000,
120
+ include_subdomains: true,
121
+ report_uri: '//example.com/uri-directive',
122
+ pins: [
123
+ { sha256: 'abc' },
124
+ { sha256: '123' }
125
+ ]
126
+ }
91
127
  end
92
128
 
93
129
  expect(SecureHeaders.header_hash_for(plaintext_request)[PublicKeyPins::HEADER_NAME]).to be_nil
@@ -102,26 +138,67 @@ module SecureHeaders
102
138
  }
103
139
  end
104
140
 
105
- SecureHeaders.append_content_security_policy_directives(@request, script_src: %w(anothercdn.com))
106
- hash = SecureHeaders.header_hash_for(@request)
141
+ SecureHeaders.append_content_security_policy_directives(request, script_src: %w(anothercdn.com))
142
+ hash = SecureHeaders.header_hash_for(request)
107
143
  expect(hash[CSP::HEADER_NAME]).to eq("default-src 'self'; script-src mycdn.com 'unsafe-inline' anothercdn.com")
108
144
  end
109
145
 
146
+ it "dups global configuration just once when overriding n times and only calls idempotent_additions? once" do
147
+ Configuration.default do |config|
148
+ config.csp = {
149
+ default_src: %w('self')
150
+ }
151
+ end
152
+
153
+ expect(CSP).to receive(:idempotent_additions?).once
154
+
155
+ # before an override occurs, the env is empty
156
+ expect(request.env[SECURE_HEADERS_CONFIG]).to be_nil
157
+
158
+ SecureHeaders.append_content_security_policy_directives(request, script_src: %w(anothercdn.com))
159
+ new_config = SecureHeaders.config_for(request)
160
+ expect(new_config).to_not be(Configuration.get)
161
+
162
+ SecureHeaders.override_content_security_policy_directives(request, script_src: %w(yet.anothercdn.com))
163
+ current_config = SecureHeaders.config_for(request)
164
+ expect(current_config).to be(new_config)
165
+
166
+ SecureHeaders.header_hash_for(request)
167
+ end
168
+
169
+ it "doesn't allow you to muck with csp configs when a dynamic policy is in use" do
170
+ default_config = Configuration.default
171
+ expect { default_config.csp = {} }.to raise_error(NoMethodError)
172
+
173
+ # config is frozen
174
+ expect { default_config.send(:csp=, {}) }.to raise_error(RuntimeError)
175
+
176
+ SecureHeaders.append_content_security_policy_directives(request, script_src: %w(anothercdn.com))
177
+ new_config = SecureHeaders.config_for(request)
178
+ expect { new_config.send(:csp=, {}) }.to raise_error(Configuration::IllegalPolicyModificationError)
179
+
180
+ expect do
181
+ new_config.instance_eval do
182
+ new_config.csp = {}
183
+ end
184
+ end.to raise_error(Configuration::IllegalPolicyModificationError)
185
+ end
186
+
110
187
  it "overrides individual directives" do
111
188
  Configuration.default do |config|
112
189
  config.csp = {
113
190
  default_src: %w('self')
114
191
  }
115
192
  end
116
- SecureHeaders.override_content_security_policy_directives(@request, default_src: %w('none'))
117
- hash = SecureHeaders.header_hash_for(@request)
193
+ SecureHeaders.override_content_security_policy_directives(request, default_src: %w('none'))
194
+ hash = SecureHeaders.header_hash_for(request)
118
195
  expect(hash[CSP::HEADER_NAME]).to eq("default-src 'none'")
119
196
  end
120
197
 
121
198
  it "overrides non-existant directives" do
122
199
  Configuration.default
123
- SecureHeaders.override_content_security_policy_directives(@request, img_src: [ContentSecurityPolicy::DATA_PROTOCOL])
124
- hash = SecureHeaders.header_hash_for(@request)
200
+ SecureHeaders.override_content_security_policy_directives(request, img_src: [ContentSecurityPolicy::DATA_PROTOCOL])
201
+ hash = SecureHeaders.header_hash_for(request)
125
202
  expect(hash[CSP::HEADER_NAME]).to eq("default-src https:; img-src data:")
126
203
  end
127
204
 
@@ -134,9 +211,9 @@ module SecureHeaders
134
211
  }
135
212
  end
136
213
 
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)
214
+ safari_request = Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:safari5]))
215
+ nonce = SecureHeaders.content_security_policy_script_nonce(safari_request)
216
+ hash = SecureHeaders.header_hash_for(safari_request)
140
217
  expect(hash[CSP::HEADER_NAME]).to eq("default-src 'self'; script-src mycdn.com 'unsafe-inline'; style-src 'self'")
141
218
  end
142
219
 
@@ -149,15 +226,15 @@ module SecureHeaders
149
226
  }
150
227
  end
151
228
 
152
- request = Rack::Request.new(@request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:chrome]))
153
- nonce = SecureHeaders.content_security_policy_script_nonce(request)
229
+ chrome_request = Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:chrome]))
230
+ nonce = SecureHeaders.content_security_policy_script_nonce(chrome_request)
154
231
 
155
232
  # 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)
233
+ SecureHeaders.content_security_policy_script_nonce(chrome_request)
234
+ SecureHeaders.content_security_policy_script_nonce(chrome_request)
235
+ SecureHeaders.content_security_policy_script_nonce(chrome_request)
159
236
 
160
- hash = SecureHeaders.header_hash_for(request)
237
+ hash = SecureHeaders.header_hash_for(chrome_request)
161
238
  expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src mycdn.com 'nonce-#{nonce}'; style-src 'self'")
162
239
  end
163
240
  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',
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.3
4
+ version: 3.1.2
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-29 00:00:00.000000000 Z
11
+ date: 2016-03-30 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