secure_headers 3.0.3 → 3.1.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.

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