secure_headers 3.0.3 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of secure_headers might be problematic. Click here for more details.

@@ -4,7 +4,7 @@ module SecureHeaders
4
4
  describe StrictTransportSecurity do
5
5
  describe "#value" do
6
6
  specify { expect(StrictTransportSecurity.make_header).to eq([StrictTransportSecurity::HEADER_NAME, StrictTransportSecurity::DEFAULT_VALUE]) }
7
- specify { expect(StrictTransportSecurity.make_header("max-age=1234")).to eq([StrictTransportSecurity::HEADER_NAME, "max-age=1234"]) }
7
+ specify { expect(StrictTransportSecurity.make_header("max-age=1234; includeSubdomains; preload")).to eq([StrictTransportSecurity::HEADER_NAME, "max-age=1234; includeSubdomains; preload"]) }
8
8
 
9
9
  context "with an invalid configuration" do
10
10
  context "with a string argument" do
@@ -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,48 +2,39 @@ 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
@@ -51,8 +42,8 @@ module SecureHeaders
51
42
 
52
43
  it "allows you to override X-Frame-Options settings" do
53
44
  Configuration.default
54
- SecureHeaders.override_x_frame_options(@request, XFrameOptions::DENY)
55
- hash = SecureHeaders.header_hash_for(@request)
45
+ SecureHeaders.override_x_frame_options(request, XFrameOptions::DENY)
46
+ hash = SecureHeaders.header_hash_for(request)
56
47
  expect(hash[XFrameOptions::HEADER_NAME]).to eq(XFrameOptions::DENY)
57
48
  end
58
49
 
@@ -62,17 +53,36 @@ module SecureHeaders
62
53
  config.csp = OPT_OUT
63
54
  end
64
55
 
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'))
56
+ SecureHeaders.override_x_frame_options(request, XFrameOptions::SAMEORIGIN)
57
+ SecureHeaders.override_content_security_policy_directives(request, default_src: %w(https:), script_src: %w('self'))
67
58
 
68
- hash = SecureHeaders.header_hash_for(@request)
59
+ hash = SecureHeaders.header_hash_for(request)
69
60
  expect(hash[CSP::HEADER_NAME]).to eq("default-src https:; script-src 'self'")
70
61
  expect(hash[XFrameOptions::HEADER_NAME]).to eq(XFrameOptions::SAMEORIGIN)
71
62
  end
72
63
 
64
+ it "produces a UA-specific CSP when overriding (and busting the cache)" do
65
+ config = Configuration.default do |config|
66
+ config.csp = {
67
+ default_src: %w('self'),
68
+ child_src: %w('self'), #unsupported by firefox
69
+ frame_src: %w('self')
70
+ }
71
+ end
72
+ firefox_request = Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:firefox]))
73
+
74
+ # append an unsupported directive
75
+ SecureHeaders.override_content_security_policy_directives(firefox_request, plugin_types: %w(flash))
76
+ # append a supported directive
77
+ SecureHeaders.override_content_security_policy_directives(firefox_request, script_src: %w('self'))
78
+
79
+ hash = SecureHeaders.header_hash_for(firefox_request)
80
+ expect(hash[CSP::HEADER_NAME]).to eq("default-src 'self'; frame-src 'self'; script-src 'self'")
81
+ end
82
+
73
83
  it "produces a hash of headers with default config" do
74
84
  Configuration.default
75
- hash = SecureHeaders.header_hash_for(@request)
85
+ hash = SecureHeaders.header_hash_for(request)
76
86
  expect_default_values(hash)
77
87
  end
78
88
 
@@ -87,7 +97,15 @@ module SecureHeaders
87
97
  it "does not set the HPKP header if request is over HTTP" do
88
98
  plaintext_request = Rack::Request.new({})
89
99
  Configuration.default do |config|
90
- config.hpkp = example_hpkp_config
100
+ config.hpkp = {
101
+ max_age: 1_000_000,
102
+ include_subdomains: true,
103
+ report_uri: '//example.com/uri-directive',
104
+ pins: [
105
+ { sha256: 'abc' },
106
+ { sha256: '123' }
107
+ ]
108
+ }
91
109
  end
92
110
 
93
111
  expect(SecureHeaders.header_hash_for(plaintext_request)[PublicKeyPins::HEADER_NAME]).to be_nil
@@ -102,26 +120,67 @@ module SecureHeaders
102
120
  }
103
121
  end
104
122
 
105
- SecureHeaders.append_content_security_policy_directives(@request, script_src: %w(anothercdn.com))
106
- hash = SecureHeaders.header_hash_for(@request)
123
+ SecureHeaders.append_content_security_policy_directives(request, script_src: %w(anothercdn.com))
124
+ hash = SecureHeaders.header_hash_for(request)
107
125
  expect(hash[CSP::HEADER_NAME]).to eq("default-src 'self'; script-src mycdn.com 'unsafe-inline' anothercdn.com")
108
126
  end
109
127
 
128
+ it "dups global configuration just once when overriding n times and only calls idempotent_additions? once" do
129
+ Configuration.default do |config|
130
+ config.csp = {
131
+ default_src: %w('self')
132
+ }
133
+ end
134
+
135
+ expect(CSP).to receive(:idempotent_additions?).once
136
+
137
+ # before an override occurs, the env is empty
138
+ expect(request.env[SECURE_HEADERS_CONFIG]).to be_nil
139
+
140
+ SecureHeaders.append_content_security_policy_directives(request, script_src: %w(anothercdn.com))
141
+ new_config = SecureHeaders.config_for(request)
142
+ expect(new_config).to_not be(Configuration.get)
143
+
144
+ SecureHeaders.override_content_security_policy_directives(request, script_src: %w(yet.anothercdn.com))
145
+ current_config = SecureHeaders.config_for(request)
146
+ expect(current_config).to be(new_config)
147
+
148
+ SecureHeaders.header_hash_for(request)
149
+ end
150
+
151
+ it "doesn't allow you to muck with csp configs when a dynamic policy is in use" do
152
+ default_config = Configuration.default
153
+ expect { default_config.csp = {} }.to raise_error(NoMethodError)
154
+
155
+ # config is frozen
156
+ expect { default_config.send(:csp=, {}) }.to raise_error(RuntimeError)
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.send(:csp=, {}) }.to raise_error(Configuration::IllegalPolicyModificationError)
161
+
162
+ expect do
163
+ new_config.instance_eval do
164
+ new_config.csp = {}
165
+ end
166
+ end.to raise_error(Configuration::IllegalPolicyModificationError)
167
+ end
168
+
110
169
  it "overrides individual directives" do
111
170
  Configuration.default do |config|
112
171
  config.csp = {
113
172
  default_src: %w('self')
114
173
  }
115
174
  end
116
- SecureHeaders.override_content_security_policy_directives(@request, default_src: %w('none'))
117
- hash = SecureHeaders.header_hash_for(@request)
175
+ SecureHeaders.override_content_security_policy_directives(request, default_src: %w('none'))
176
+ hash = SecureHeaders.header_hash_for(request)
118
177
  expect(hash[CSP::HEADER_NAME]).to eq("default-src 'none'")
119
178
  end
120
179
 
121
180
  it "overrides non-existant directives" do
122
181
  Configuration.default
123
- SecureHeaders.override_content_security_policy_directives(@request, img_src: [ContentSecurityPolicy::DATA_PROTOCOL])
124
- hash = SecureHeaders.header_hash_for(@request)
182
+ SecureHeaders.override_content_security_policy_directives(request, img_src: [ContentSecurityPolicy::DATA_PROTOCOL])
183
+ hash = SecureHeaders.header_hash_for(request)
125
184
  expect(hash[CSP::HEADER_NAME]).to eq("default-src https:; img-src data:")
126
185
  end
127
186
 
@@ -134,9 +193,9 @@ module SecureHeaders
134
193
  }
135
194
  end
136
195
 
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)
196
+ safari_request = Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:safari5]))
197
+ nonce = SecureHeaders.content_security_policy_script_nonce(safari_request)
198
+ hash = SecureHeaders.header_hash_for(safari_request)
140
199
  expect(hash[CSP::HEADER_NAME]).to eq("default-src 'self'; script-src mycdn.com 'unsafe-inline'; style-src 'self'")
141
200
  end
142
201
 
@@ -149,15 +208,15 @@ module SecureHeaders
149
208
  }
150
209
  end
151
210
 
152
- request = Rack::Request.new(@request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:chrome]))
153
- nonce = SecureHeaders.content_security_policy_script_nonce(request)
211
+ chrome_request = Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:chrome]))
212
+ nonce = SecureHeaders.content_security_policy_script_nonce(chrome_request)
154
213
 
155
214
  # 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)
215
+ SecureHeaders.content_security_policy_script_nonce(chrome_request)
216
+ SecureHeaders.content_security_policy_script_nonce(chrome_request)
217
+ SecureHeaders.content_security_policy_script_nonce(chrome_request)
159
218
 
160
- hash = SecureHeaders.header_hash_for(request)
219
+ hash = SecureHeaders.header_hash_for(chrome_request)
161
220
  expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src mycdn.com 'nonce-#{nonce}'; style-src 'self'")
162
221
  end
163
222
  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.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-02-29 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