secure_headers 2.4.0 → 2.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bdc7e4cc47367102f2318244051b544e4bc5d611
4
- data.tar.gz: f4bb9651462b15c056ab9720a0c079283e9c2462
3
+ metadata.gz: 36b98616c83ec1049b8f1bcc25671738da8d8bed
4
+ data.tar.gz: 730108ec10da36aeeb59049e88122a99987d1dcc
5
5
  SHA512:
6
- metadata.gz: 888729b84ba1eb266c25c3bbc218c270f9e262bcbf490363a2daad6202803f4ba71a21f59c1a36e816a06c01ab8d28126ba81e05ae4f37ac36a53ee670af8420
7
- data.tar.gz: 26640f0a917396faf083eaff557316d4a376e6956ae95915cfb5c815de3269a6ec5cb9a9f6a7684d871d8ff634dc586bb6a6a20dd79ece6cee034d5ec8f20648
6
+ metadata.gz: 854d9e8c9de09d5515dad3449726b4b2308b8a25b5dc702ed80da47fd3f94f983119531a4089877b215ee07dccfa8fec2766107891b018bcca376ec16ed95e8f
7
+ data.tar.gz: f0ec419ac593cdeb556dc852d6e2a3b8f5fcfe83c07b34ac8392fcb912004bbe1117ea9e473c068230a38c44c96a5e036dfde630ae5e7397203c9aed8babd741
data/.travis.yml CHANGED
@@ -9,3 +9,4 @@ rvm:
9
9
 
10
10
  sudo: false
11
11
  cache: bundler
12
+ before_install: gem update bundler
data/Gemfile CHANGED
@@ -3,6 +3,7 @@ source 'https://rubygems.org'
3
3
  gemspec
4
4
 
5
5
  group :test do
6
+ gem "guard-rspec", platforms: [:ruby_19, :ruby_20, :ruby_21, :ruby_22]
6
7
  gem 'test-unit', '~> 3.0'
7
8
  gem 'rails', '3.2.22'
8
9
  gem 'sqlite3', :platforms => [:ruby, :mswin, :mingw]
data/Guardfile ADDED
@@ -0,0 +1,12 @@
1
+ guard :rspec, cmd: "bundle exec rspec", all_on_start: true, all_after_pass: true do
2
+ require "guard/rspec/dsl"
3
+ dsl = Guard::RSpec::Dsl.new(self)
4
+
5
+ # RSpec files
6
+ rspec = dsl.rspec
7
+ watch(rspec.spec_helper) { rspec.spec_dir }
8
+ watch(rspec.spec_support) { rspec.spec_dir }
9
+ watch(rspec.spec_files)
10
+
11
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
12
+ end
data/README.md CHANGED
@@ -8,7 +8,7 @@ The gem will automatically apply several headers that are related to security.
8
8
  - X-Content-Type-Options - [Prevent content type sniffing](http://msdn.microsoft.com/en-us/library/ie/gg622941\(v=vs.85\).aspx)
9
9
  - X-Download-Options - [Prevent file downloads opening](http://msdn.microsoft.com/en-us/library/ie/jj542450(v=vs.85).aspx)
10
10
  - X-Permitted-Cross-Domain-Policies - [Restrict Adobe Flash Player's access to data](https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html)
11
- - Public Key Pinning - Pin certificate fingerprints in the browser to prevent man-in-the-middle attacks due to compromised Certificate Authorities. [Public Key Pinnning Specification](https://tools.ietf.org/html/rfc7469)
11
+ - Public Key Pinning - Pin certificate fingerprints in the browser to prevent man-in-the-middle attacks due to compromised Certificate Authorities. [Public Key Pinning Specification](https://tools.ietf.org/html/rfc7469)
12
12
 
13
13
  ## Usage
14
14
 
@@ -61,6 +61,7 @@ The following methods are going to be called, unless they are provided in a `ski
61
61
  :form_action => "'self' github.com",
62
62
  :frame_ancestors => "'none'",
63
63
  :plugin_types => 'application/x-shockwave-flash',
64
+ :block_all_mixed_content => '' # see [http://www.w3.org/TR/mixed-content/]()
64
65
  :report_uri => '//example.com/uri-directive'
65
66
  }
66
67
  config.hpkp = {
@@ -99,7 +100,7 @@ Sometimes you need to override your content security policy for a given endpoint
99
100
  1. Override the `secure_header_options_for` class instance method. e.g.
100
101
 
101
102
  ```ruby
102
- class SomethingController < ApplicationController
103
+ class SomethingController < ApplicationController
103
104
  def wumbus
104
105
  # gets style-src override
105
106
  end
@@ -11,7 +11,9 @@ module SecureHeaders
11
11
  DEFAULT_CSP_HEADER = "default-src https: data: 'unsafe-inline' 'unsafe-eval'; frame-src https: about: javascript:; img-src data:"
12
12
  HEADER_NAME = "Content-Security-Policy"
13
13
  ENV_KEY = 'secure_headers.content_security_policy'
14
- DIRECTIVES = [
14
+ USER_AGENT_PARSER = UserAgentParser::Parser.new
15
+
16
+ DIRECTIVES_1_0 = [
15
17
  :default_src,
16
18
  :connect_src,
17
19
  :font_src,
@@ -19,20 +21,53 @@ module SecureHeaders
19
21
  :img_src,
20
22
  :media_src,
21
23
  :object_src,
24
+ :sandbox,
22
25
  :script_src,
23
26
  :style_src,
27
+ :report_uri
28
+ ].freeze
29
+
30
+ DIRECTIVES_2_0 = [
31
+ DIRECTIVES_1_0,
24
32
  :base_uri,
25
33
  :child_src,
26
34
  :form_action,
27
35
  :frame_ancestors,
28
36
  :plugin_types
29
- ]
37
+ ].flatten.freeze
30
38
 
31
- OTHER = [
32
- :report_uri
33
- ]
34
39
 
35
- ALL_DIRECTIVES = DIRECTIVES + OTHER
40
+ # All the directives currently under consideration for CSP level 3.
41
+ # https://w3c.github.io/webappsec/specs/CSP2/
42
+ DIRECTIVES_3_0 = [
43
+ DIRECTIVES_2_0,
44
+ :manifest_src,
45
+ :reflected_xss
46
+ ].flatten.freeze
47
+
48
+ # All the directives that are not currently in a formal spec, but have
49
+ # been implemented somewhere.
50
+ DIRECTIVES_DRAFT = [
51
+ :block_all_mixed_content,
52
+ ].freeze
53
+
54
+ SAFARI_DIRECTIVES = DIRECTIVES_1_0
55
+
56
+ FIREFOX_UNSUPPORTED_DIRECTIVES = [
57
+ :block_all_mixed_content,
58
+ :child_src,
59
+ :plugin_types
60
+ ].freeze
61
+
62
+ FIREFOX_DIRECTIVES = (
63
+ DIRECTIVES_2_0 - FIREFOX_UNSUPPORTED_DIRECTIVES
64
+ ).freeze
65
+
66
+ CHROME_DIRECTIVES = (
67
+ DIRECTIVES_2_0 + DIRECTIVES_DRAFT
68
+ ).freeze
69
+
70
+ ALL_DIRECTIVES = [DIRECTIVES_1_0 + DIRECTIVES_2_0 + DIRECTIVES_3_0 + DIRECTIVES_DRAFT].flatten.uniq.sort
36
71
  CONFIG_KEY = :csp
37
72
  end
38
73
 
@@ -102,10 +137,18 @@ module SecureHeaders
102
137
 
103
138
  # Config values can be string, array, or lamdba values
104
139
  @config = config.inject({}) do |hash, (key, value)|
105
- config_val = value.respond_to?(:call) ? value.call(@controller) : value
140
+ config_val = if value.respond_to?(:call)
141
+ warn "[DEPRECATION] secure_headers 3.x will not support procs as config values."
142
+ value.call(@controller)
143
+ else
144
+ value
145
+ end
106
146
 
107
- if DIRECTIVES.include?(key) # directives need to be normalized to arrays of strings
108
- config_val = config_val.split if config_val.is_a? String
147
+ if ALL_DIRECTIVES.include?(key.to_sym) # directives need to be normalized to arrays of strings
148
+ if config_val.is_a? String
149
+ warn "[DEPRECATION] A String was supplied for directive #{key}. secure_headers 3.x will require all directives to be arrays of strings."
150
+ config_val = config_val.split
151
+ end
109
152
  if config_val.is_a?(Array)
110
153
  config_val = config_val.map do |val|
111
154
  translate_dir_value(val)
@@ -118,14 +161,36 @@ module SecureHeaders
118
161
  end
119
162
 
120
163
  @http_additions = @config.delete(:http_additions)
121
- @app_name = @config.delete(:app_name)
122
- @report_uri = @config.delete(:report_uri)
123
- @enforce = !!@config.delete(:enforce)
124
164
  @disable_img_src_data_uri = !!@config.delete(:disable_img_src_data_uri)
125
165
  @tag_report_uri = !!@config.delete(:tag_report_uri)
126
166
  @script_hashes = @config.delete(:script_hashes) || []
167
+ @app_name = @config.delete(:app_name)
168
+ @app_name = @app_name.call(@controller) if @app_name.respond_to?(:call)
169
+ @enforce = @config.delete(:enforce)
170
+ @enforce = @enforce.call(@controller) if @enforce.respond_to?(:call)
171
+ @enforce = !!@enforce
172
+
173
+ # normalize and tag the report-uri
174
+ if @config[:report_uri]
175
+ @config[:report_uri] = @config[:report_uri].map do |report_uri|
176
+ if report_uri.start_with?('//')
177
+ report_uri = if @ssl_request
178
+ "https:" + report_uri
179
+ else
180
+ "http:" + report_uri
181
+ end
182
+ end
183
+
184
+ if @tag_report_uri
185
+ report_uri = "#{report_uri}?enforce=#{@enforce}"
186
+ report_uri += "&app_name=#{@app_name}" if @app_name
187
+ end
188
+ report_uri
189
+ end
190
+ end
127
191
 
128
192
  add_script_hashes if @script_hashes.any?
193
+ strip_unsupported_directives
129
194
  end
130
195
 
131
196
  ##
@@ -160,13 +225,20 @@ module SecureHeaders
160
225
 
161
226
  def to_json
162
227
  build_value
163
- @config.to_json.gsub(/(\w+)_src/, "\\1-src")
228
+ @config.inject({}) do |hash, (key, value)|
229
+ if ALL_DIRECTIVES.include?(key)
230
+ hash[key.to_s.gsub(/(\w+)_(\w+)/, "\\1-\\2")] = value
231
+ end
232
+ hash
233
+ end.to_json
164
234
  end
165
235
 
166
236
  def self.from_json(*json_configs)
167
237
  json_configs.inject({}) do |combined_config, one_config|
168
- one_config = one_config.gsub(/(\w+)-src/, "\\1_src")
169
- config = JSON.parse(one_config, :symbolize_names => true)
238
+ config = JSON.parse(one_config).inject({}) do |hash, (key, value)|
239
+ hash[key.gsub(/(\w+)-(\w+)/, "\\1_\\2").to_sym] = value
240
+ hash
241
+ end
170
242
  combined_config.merge(config) do |_, lhs, rhs|
171
243
  lhs | rhs
172
244
  end
@@ -182,10 +254,7 @@ module SecureHeaders
182
254
  def build_value
183
255
  raise "Expected to find default_src directive value" unless @config[:default_src]
184
256
  append_http_additions unless ssl_request?
185
- header_value = [
186
- generic_directives,
187
- report_uri_directive
188
- ].join.strip
257
+ generic_directives
189
258
  end
190
259
 
191
260
  def append_http_additions
@@ -198,13 +267,13 @@ module SecureHeaders
198
267
 
199
268
  def translate_dir_value val
200
269
  if %w{inline eval}.include?(val)
201
- warn "[DEPRECATION] using inline/eval may not be supported in the future. Instead use 'unsafe-inline'/'unsafe-eval' instead."
270
+ warn "[DEPRECATION] using inline/eval is not suppored in secure_headers 3.x. Instead use 'unsafe-inline'/'unsafe-eval' instead."
202
271
  val == 'inline' ? "'unsafe-inline'" : "'unsafe-eval'"
203
272
  elsif %{self none}.include?(val)
204
- warn "[DEPRECATION] using self/none may not be supported in the future. Instead use 'self'/'none' instead."
273
+ warn "[DEPRECATION] using self/none is not suppored in secure_headers 3.x. Instead use 'self'/'none' instead."
205
274
  "'#{val}'"
206
275
  elsif val == 'nonce'
207
- if supports_nonces?(@ua)
276
+ if supports_nonces?
208
277
  self.class.set_nonce(@controller, nonce)
209
278
  ["'nonce-#{nonce}'", "'unsafe-inline'"]
210
279
  else
@@ -215,27 +284,9 @@ module SecureHeaders
215
284
  end
216
285
  end
217
286
 
218
- def report_uri_directive
219
- return '' if @report_uri.nil?
220
-
221
- if @report_uri.start_with?('//')
222
- @report_uri = if @ssl_request
223
- "https:" + @report_uri
224
- else
225
- "http:" + @report_uri
226
- end
227
- end
228
-
229
- if @tag_report_uri
230
- @report_uri = "#{@report_uri}?enforce=#{@enforce}"
231
- @report_uri += "&app_name=#{@app_name}" if @app_name
232
- end
233
-
234
- "report-uri #{@report_uri};"
235
- end
236
-
287
+ # ensures defualt_src is first and report_uri is last
237
288
  def generic_directives
238
- header_value = ''
289
+ header_value = build_directive(:default_src)
239
290
  data_uri = @disable_img_src_data_uri ? [] : ["data:"]
240
291
  if @config[:img_src]
241
292
  @config[:img_src] = @config[:img_src] + data_uri unless @config[:img_src].include?('data:')
@@ -243,19 +294,40 @@ module SecureHeaders
243
294
  @config[:img_src] = @config[:default_src] + data_uri
244
295
  end
245
296
 
246
- DIRECTIVES.each do |directive_name|
247
- header_value += build_directive(directive_name) if @config[directive_name]
297
+ (ALL_DIRECTIVES - [:default_src, :report_uri]).each do |directive_name|
298
+ if @config[directive_name]
299
+ header_value += build_directive(directive_name)
300
+ end
248
301
  end
249
302
 
250
- header_value
303
+ header_value += build_directive(:report_uri) if @config[:report_uri]
304
+
305
+ header_value.strip
251
306
  end
252
307
 
253
308
  def build_directive(key)
254
309
  "#{self.class.symbol_to_hyphen_case(key)} #{@config[key].join(" ")}; "
255
310
  end
256
311
 
257
- def supports_nonces?(user_agent)
258
- parsed_ua = UserAgentParser.parse(user_agent)
312
+ def strip_unsupported_directives
313
+ @config.select! { |key, _| supported_directives.include?(key) }
314
+ end
315
+
316
+ def supported_directives
317
+ @supported_directives ||= case USER_AGENT_PARSER.parse(@ua).family
318
+ when "Chrome"
319
+ CHROME_DIRECTIVES
320
+ when "Safari"
321
+ SAFARI_DIRECTIVES
322
+ when "Firefox"
323
+ FIREFOX_DIRECTIVES
324
+ else
325
+ DIRECTIVES_1_0
326
+ end
327
+ end
328
+
329
+ def supports_nonces?
330
+ parsed_ua = USER_AGENT_PARSER.parse(@ua)
259
331
  ["Chrome", "Opera", "Firefox"].include?(parsed_ua.family)
260
332
  end
261
333
  end
@@ -41,6 +41,7 @@ module SecureHeaders
41
41
 
42
42
  def validate_config
43
43
  if @config.is_a? Hash
44
+ warn "[DEPRECATION] secure_headers 3.0 will only accept string values for StrictTransportSecurity config"
44
45
  if !@config[:max_age]
45
46
  raise STSBuildError.new("No max-age was supplied.")
46
47
  elsif @config[:max_age].to_s !~ /\A\d+\z/
@@ -25,6 +25,7 @@ module SecureHeaders
25
25
  when String
26
26
  @config
27
27
  else
28
+ warn "[DEPRECATION] secure_headers 3.0 will only accept string values for XContentTypeOptions config"
28
29
  @config[:value]
29
30
  end
30
31
  end
@@ -24,6 +24,7 @@ module SecureHeaders
24
24
  when String
25
25
  @config
26
26
  else
27
+ warn "[DEPRECATION] secure_headers 3.0 will only accept string values for XDownloadOptions config"
27
28
  @config[:value]
28
29
  end
29
30
  end
@@ -25,6 +25,7 @@ module SecureHeaders
25
25
  when String
26
26
  @config
27
27
  else
28
+ warn "[DEPRECATION] secure_headers 3.0 will only accept string values for XFrameOptions config"
28
29
  @config[:value]
29
30
  end
30
31
  end
@@ -25,6 +25,7 @@ module SecureHeaders
25
25
  when String
26
26
  @config
27
27
  else
28
+ warn "[DEPRECATION] secure_headers 3.0 will only accept string values for XPermittedCrossDomainPolicies config"
28
29
  @config[:value]
29
30
  end
30
31
  end
@@ -25,6 +25,7 @@ module SecureHeaders
25
25
  when String
26
26
  @config
27
27
  else
28
+ warn "[DEPRECATION] secure_headers 3.0 will only accept string values for XXssProtection config"
28
29
  value = @config[:value].to_s
29
30
  value += "; mode=#{@config[:mode]}" if @config[:mode]
30
31
  value += "; report=#{@config[:report_uri]}" if @config[:report_uri]
@@ -1,3 +1,3 @@
1
1
  module SecureHeaders
2
- VERSION = "2.4.0"
2
+ VERSION = "2.5.0"
3
3
  end
@@ -27,7 +27,6 @@ module SecureHeaders
27
27
  if raise_error_on_unrecognized_hash
28
28
  raise UnexpectedHashedScriptException.new(message)
29
29
  else
30
- puts message
31
30
  request.env[HASHES_ENV_KEY] = (request.env[HASHES_ENV_KEY] || []) << hash_value
32
31
  end
33
32
  end
@@ -33,12 +33,18 @@ module SecureHeaders
33
33
  :x_xss_protection, :csp, :x_download_options, :script_hashes,
34
34
  :x_permitted_cross_domain_policies, :hpkp
35
35
 
36
- def configure &block
36
+ # For preparation for the secure_headers 3.x change.
37
+ def default &block
37
38
  instance_eval &block
38
39
  if File.exists?(SCRIPT_HASH_CONFIG_FILE)
39
40
  ::SecureHeaders::Configuration.script_hashes = YAML.load(File.open(SCRIPT_HASH_CONFIG_FILE))
40
41
  end
41
42
  end
43
+
44
+ def configure &block
45
+ warn "[DEPRECATION] `configure` is removed in secure_headers 3.x. Instead use `default`."
46
+ default &block
47
+ end
42
48
  end
43
49
  end
44
50
 
@@ -52,21 +58,23 @@ module SecureHeaders
52
58
 
53
59
  def header_hash(options = nil)
54
60
  ALL_HEADER_CLASSES.inject({}) do |memo, klass|
55
- config = if options.is_a?(Hash) && options[klass::Constants::CONFIG_KEY]
61
+ # must use !options[key].nil? because 'false' represents opting out, nil
62
+ # represents use global default.
63
+ config = if options.is_a?(Hash) && !options[klass::Constants::CONFIG_KEY].nil?
56
64
  options[klass::Constants::CONFIG_KEY]
57
65
  else
58
66
  ::SecureHeaders::Configuration.send(klass::Constants::CONFIG_KEY)
59
67
  end
60
68
 
61
69
  unless klass == SecureHeaders::PublicKeyPins && !config.is_a?(Hash)
62
- header = get_a_header(klass::Constants::CONFIG_KEY, klass, config)
63
- memo[header.name] = header.value
70
+ header = get_a_header(klass, config)
71
+ memo[header.name] = header.value if header
64
72
  end
65
73
  memo
66
74
  end
67
75
  end
68
76
 
69
- def get_a_header(name, klass, options)
77
+ def get_a_header(klass, options)
70
78
  return if options == false
71
79
  klass.new(options)
72
80
  end
@@ -204,13 +212,14 @@ module SecureHeaders
204
212
  # we can't use ||= because I'm overloading false => disable, nil => default
205
213
  # both of which trigger the conditional assignment
206
214
  def secure_header_options_for(type, options)
215
+ warn "[DEPRECATION] secure_header_options_for will not be supported in secure_headers 3.x."
207
216
  options.nil? ? ::SecureHeaders::Configuration.send(type) : options
208
217
  end
209
218
 
210
219
  def set_a_header(name, klass, options=nil)
211
220
  options = secure_header_options_for(name, options)
212
221
  return if options == false
213
- set_header(SecureHeaders::get_a_header(name, klass, options))
222
+ set_header(SecureHeaders::get_a_header(klass, options))
214
223
  end
215
224
 
216
225
  def set_header(name_or_header, value=nil)
@@ -20,4 +20,5 @@ Gem::Specification.new do |gem|
20
20
  gem.require_paths = ["lib"]
21
21
  gem.add_development_dependency "rake"
22
22
  gem.add_dependency "user_agent_parser"
23
+ gem.post_install_message = "[DEPRECATION] Development has stopped on the 2.x line of secure_headers. It will be maintained, but new features will be added to the 3.x branch. A lot has changed in secure_headers 3.x. A migration guide can be found in the documentation: https://github.com/twitter/secureheaders/blob/master/upgrading-to-3-0.md."
23
24
  end
@@ -10,7 +10,6 @@ module SecureHeaders
10
10
 
11
11
  let(:default_config) do
12
12
  {
13
- :disable_fill_missing => true,
14
13
  :default_src => 'https://*',
15
14
  :report_uri => '/csp_report',
16
15
  :script_src => "'unsafe-inline' 'unsafe-eval' https://* data:",
@@ -5,9 +5,10 @@ module SecureHeaders
5
5
  let(:default_opts) do
6
6
  {
7
7
  :default_src => 'https:',
8
- :report_uri => '/csp_report',
8
+ :img_src => "https: data:",
9
9
  :script_src => "'unsafe-inline' 'unsafe-eval' https: data:",
10
- :style_src => "'unsafe-inline' https: about:"
10
+ :style_src => "'unsafe-inline' https: about:",
11
+ :report_uri => '/csp_report'
11
12
  }
12
13
  end
13
14
  let(:controller) { DummyClass.new }
@@ -58,7 +59,7 @@ module SecureHeaders
58
59
 
59
60
  it "exports a policy to JSON" do
60
61
  policy = ContentSecurityPolicy.new(default_opts)
61
- expected = %({"default-src":["https:"],"script-src":["'unsafe-inline'","'unsafe-eval'","https:","data:"],"style-src":["'unsafe-inline'","https:","about:"],"img-src":["https:","data:"]})
62
+ expected = %({"default-src":["https:"],"img-src":["https:","data:"],"script-src":["'unsafe-inline'","'unsafe-eval'","https:","data:"],"style-src":["'unsafe-inline'","https:","about:"],"report-uri":["/csp_report"]})
62
63
  expect(policy.to_json).to eq(expected)
63
64
  end
64
65
 
@@ -141,6 +142,35 @@ module SecureHeaders
141
142
  end
142
143
 
143
144
  describe "#value" do
145
+ it "does not mutate shared state" do
146
+ opts = default_opts.merge(enforce: true)
147
+ policy = ContentSecurityPolicy.new(opts, :request => request_for(CHROME))
148
+ expect(policy.name).to eq("Content-Security-Policy")
149
+ policy = ContentSecurityPolicy.new(opts, :request => request_for(CHROME))
150
+ expect(policy.name).to eq("Content-Security-Policy")
151
+ end
152
+
153
+ context "browser sniffing" do
154
+ let(:complex_opts) do
155
+ ALL_DIRECTIVES.inject({}) { |memo, directive| memo[directive] = "'self'"; memo }.merge(:block_all_mixed_content => '')
156
+ end
157
+
158
+ it "does not filter any directives for Chrome" do
159
+ policy = ContentSecurityPolicy.new(complex_opts, :request => request_for(CHROME))
160
+ expect(policy.value).to eq("default-src 'self'; base-uri 'self'; block-all-mixed-content ; child-src 'self'; connect-src 'self'; font-src 'self'; form-action 'self'; frame-ancestors 'self'; frame-src 'self'; img-src 'self' data:; media-src 'self'; object-src 'self'; plugin-types 'self'; sandbox 'self'; script-src 'self'; style-src 'self'; report-uri 'self';")
161
+ end
162
+
163
+ it "filters blocked-all-mixed-content, child-src, and plugin-types for firefox" do
164
+ policy = ContentSecurityPolicy.new(complex_opts, :request => request_for(FIREFOX))
165
+ expect(policy.value).to eq("default-src 'self'; base-uri 'self'; connect-src 'self'; font-src 'self'; form-action 'self'; frame-ancestors 'self'; frame-src 'self'; img-src 'self' data:; media-src 'self'; object-src 'self'; sandbox 'self'; script-src 'self'; style-src 'self'; report-uri 'self';")
166
+ end
167
+
168
+ it "filters base-uri, blocked-all-mixed-content, child-src, form-action, frame-ancestors, and plugin-types for safari" do
169
+ policy = ContentSecurityPolicy.new(complex_opts, :request => request_for(SAFARI))
170
+ expect(policy.value).to eq("default-src 'self'; connect-src 'self'; font-src 'self'; frame-src 'self'; img-src 'self' data:; media-src 'self'; object-src 'self'; sandbox 'self'; script-src 'self'; style-src 'self'; report-uri 'self';")
171
+ end
172
+ end
173
+
144
174
  it "raises an exception when default-src is missing" do
145
175
  csp = ContentSecurityPolicy.new({:script_src => 'anything'}, :request => request_for(CHROME))
146
176
  expect {
@@ -248,7 +278,7 @@ module SecureHeaders
248
278
 
249
279
  it "adds directive values for headers on http" do
250
280
  csp = ContentSecurityPolicy.new(options, :request => request_for(CHROME))
251
- expect(csp.value).to eq("default-src https:; frame-src http:; img-src http: data:; script-src 'unsafe-inline' 'unsafe-eval' https: data:; style-src 'unsafe-inline' https: about:; report-uri /csp_report;")
281
+ expect(csp.value).to eq("default-src https:; frame-src http:; img-src https: data: http:; script-src 'unsafe-inline' 'unsafe-eval' https: data:; style-src 'unsafe-inline' https: about:; report-uri /csp_report;")
252
282
  end
253
283
 
254
284
  it "does not add the directive values if requesting https" do
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  module SecureHeaders
2
4
  describe XContentTypeOptions do
3
5
  specify{ expect(XContentTypeOptions.new.name).to eq("X-Content-Type-Options") }
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  module SecureHeaders
2
4
  describe XXssProtection do
3
5
  specify { expect(XXssProtection.new.name).to eq(X_XSS_PROTECTION_HEADER_NAME)}
@@ -8,6 +8,7 @@ describe SecureHeaders do
8
8
  let(:request) {double(:ssl? => true, :url => 'https://example.com')}
9
9
 
10
10
  before(:each) do
11
+ reset_config
11
12
  stub_user_agent(nil)
12
13
  allow(headers).to receive(:[])
13
14
  allow(subject).to receive(:response).and_return(response)
@@ -19,7 +20,7 @@ describe SecureHeaders do
19
20
  end
20
21
 
21
22
  def reset_config
22
- ::SecureHeaders::Configuration.configure do |config|
23
+ ::SecureHeaders::Configuration.default do |config|
23
24
  config.hpkp = nil
24
25
  config.hsts = nil
25
26
  config.x_frame_options = nil
@@ -138,7 +139,7 @@ describe SecureHeaders do
138
139
 
139
140
  context "when disabled by configuration settings" do
140
141
  it "does not set any headers when disabled" do
141
- ::SecureHeaders::Configuration.configure do |config|
142
+ ::SecureHeaders::Configuration.default do |config|
142
143
  config.hsts = false
143
144
  config.hpkp = false
144
145
  config.x_frame_options = false
@@ -166,13 +167,30 @@ describe SecureHeaders do
166
167
  end
167
168
 
168
169
  it "produces a hash of headers given a hash as config" do
169
- hash = SecureHeaders::header_hash(:csp => {:default_src => "'none'", :img_src => "data:", :disable_fill_missing => true})
170
+ hash = SecureHeaders::header_hash(:csp => {:default_src => "'none'", :img_src => "data:"})
170
171
  expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'none'; img-src data:;")
171
172
  expect_default_values(hash)
172
173
  end
173
174
 
175
+ it "allows opting out" do
176
+ hash = SecureHeaders::header_hash(:csp => false, :hpkp => false)
177
+ expect(hash['Content-Security-Policy-Report-Only']).to be_nil
178
+ expect(hash['Content-Security-Policy']).to be_nil
179
+ end
180
+
181
+ it "allows opting out with config" do
182
+ ::SecureHeaders::Configuration.default do |config|
183
+ config.hsts = false
184
+ config.csp = false
185
+ end
186
+
187
+ hash = SecureHeaders::header_hash
188
+ expect(hash['Content-Security-Policy-Report-Only']).to be_nil
189
+ expect(hash['Content-Security-Policy']).to be_nil
190
+ end
191
+
174
192
  it "produces a hash with a mix of config values, override values, and default values" do
175
- ::SecureHeaders::Configuration.configure do |config|
193
+ ::SecureHeaders::Configuration.default do |config|
176
194
  config.hsts = { :max_age => '123456'}
177
195
  config.hpkp = {
178
196
  :enforce => true,
@@ -186,8 +204,8 @@ describe SecureHeaders do
186
204
  }
187
205
  end
188
206
 
189
- hash = SecureHeaders::header_hash(:csp => {:default_src => "'none'", :img_src => "data:", :disable_fill_missing => true})
190
- ::SecureHeaders::Configuration.configure do |config|
207
+ hash = SecureHeaders::header_hash(:csp => {:default_src => "'none'", :img_src => "data:"})
208
+ ::SecureHeaders::Configuration.default do |config|
191
209
  config.hsts = nil
192
210
  config.hpkp = nil
193
211
  end
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: 2.4.0
4
+ version: 2.5.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: 2015-10-01 00:00:00.000000000 Z
11
+ date: 2016-01-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -50,6 +50,7 @@ files:
50
50
  - ".ruby-version"
51
51
  - ".travis.yml"
52
52
  - Gemfile
53
+ - Guardfile
53
54
  - LICENSE
54
55
  - README.md
55
56
  - Rakefile
@@ -170,7 +171,10 @@ homepage: https://github.com/twitter/secureheaders
170
171
  licenses:
171
172
  - Apache Public License 2.0
172
173
  metadata: {}
173
- post_install_message:
174
+ post_install_message: "[DEPRECATION] Development has stopped on the 2.x line of secure_headers.
175
+ It will be maintained, but new features will be added to the 3.x branch. A lot has
176
+ changed in secure_headers 3.x. A migration guide can be found in the documentation:
177
+ https://github.com/twitter/secureheaders/blob/master/upgrading-to-3-0.md."
174
178
  rdoc_options: []
175
179
  require_paths:
176
180
  - lib