secure_headers 2.2.4 → 2.3.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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8a0931db8855224044856cad01f2226dfb325c93
4
- data.tar.gz: bc3520f802677b2c7967d9e7ae928c5d6ef4d2ca
3
+ metadata.gz: 2ea3335e72b8eaea53c9fc03a77c008b73fc674c
4
+ data.tar.gz: 4097b301e8e7f18174abda1c6412f9652cd4c39a
5
5
  SHA512:
6
- metadata.gz: 41b4ee3848c1261162c5a5e7f80931a1e7891a08ac0d1f49b5ebfae5ae819f0e8a5b3577499d4cd8d62eaf71d36218bb7bf97cd263d220a67a72e14c8a49dac6
7
- data.tar.gz: acae6ac54a26a925e3fd74a2b24c50a00349cce8be8d9dca8d81ee1dbae68008defa925bb3a10f99996476687de0b632fca07cfc6d40bd7511dd8acc1cc7a0c1
6
+ metadata.gz: 53db0ad5f6d9e7a85bde9aa167a927e3bda91351ce2f57af0f505d1b6c0856ca50b295ad87e26c1b25fa0142a95d8ef8b11f12f562377e60a0b398454abdd5b9
7
+ data.tar.gz: a6c3c2366fcf08ed3749f6c6e9146e2fbc33fb167a504404e195bd9813c709b0d497c2c2fc64f138a482a5fcc68eb4d212895761722ae0beed4b49ccea481e81
data/README.md CHANGED
@@ -415,8 +415,38 @@ def before_load
415
415
  end
416
416
  ```
417
417
 
418
+ ### Using in rack middleware
419
+
420
+ The `SecureHeaders::header_hash` generates a hash of all header values, which is useful for merging with rack middleware values.
421
+
422
+ ```ruby
423
+ class MySecureHeaders
424
+ include SecureHeaders
425
+ def initialize(app)
426
+ @app = app
427
+ end
428
+
429
+ def call(env)
430
+ status, headers, response = @app.call(env)
431
+ security_headers = if override?
432
+ SecureHeaders::header_hash(:csp => false) # uses global config, but overrides CSP config
433
+ else
434
+ SecureHeaders::header_hash # uses global config
435
+ end
436
+ [status, headers.merge(security_headers), [response.body]]
437
+ end
438
+ end
439
+
440
+ module Testapp
441
+ class Application < Rails::Application
442
+ config.middleware.use MySecureHeaders
443
+ end
444
+ end
445
+ ```
446
+
418
447
  ## Similar libraries
419
448
 
449
+ * Rack [rack-secure_headers](https://github.com/harmoni/rack-secure_headers)
420
450
  * Node.js (express) [helmet](https://github.com/evilpacket/helmet) and [hood](https://github.com/seanmonstar/hood)
421
451
  * Node.js (hapi) [blankie](https://github.com/nlf/blankie)
422
452
  * J2EE Servlet >= 3.0 [headlines](https://github.com/sourceclear/headlines)
@@ -40,7 +40,9 @@ module SecureHeaders
40
40
  SOURCE_DIRECTIVES = DIRECTIVES + NON_DEFAULT_SOURCES
41
41
 
42
42
  ALL_DIRECTIVES = DIRECTIVES + NON_DEFAULT_SOURCES + OTHER
43
+ CONFIG_KEY = :csp
43
44
  end
45
+
44
46
  include Constants
45
47
 
46
48
  attr_reader :disable_fill_missing, :ssl_request
@@ -6,6 +6,7 @@ module SecureHeaders
6
6
  ENV_KEY = 'secure_headers.public_key_pins'
7
7
  HASH_ALGORITHMS = [:sha256]
8
8
  DIRECTIVES = [:max_age]
9
+ CONFIG_KEY = :hpkp
9
10
  end
10
11
  class << self
11
12
  def symbol_to_hyphen_case sym
@@ -8,6 +8,7 @@ module SecureHeaders
8
8
  DEFAULT_VALUE = "max-age=" + HSTS_MAX_AGE
9
9
  VALID_STS_HEADER = /\Amax-age=\d+(; includeSubdomains)?(; preload)?\z/i
10
10
  MESSAGE = "The config value supplied for the HSTS header was invalid."
11
+ CONFIG_KEY = :hsts
11
12
  end
12
13
  include Constants
13
14
 
@@ -5,6 +5,7 @@ module SecureHeaders
5
5
  module Constants
6
6
  X_CONTENT_TYPE_OPTIONS_HEADER_NAME = "X-Content-Type-Options"
7
7
  DEFAULT_VALUE = "nosniff"
8
+ CONFIG_KEY = :x_content_type_options
8
9
  end
9
10
  include Constants
10
11
 
@@ -37,4 +38,4 @@ module SecureHeaders
37
38
  end
38
39
  end
39
40
  end
40
- end
41
+ end
@@ -4,6 +4,7 @@ module SecureHeaders
4
4
  module Constants
5
5
  XDO_HEADER_NAME = "X-Download-Options"
6
6
  DEFAULT_VALUE = 'noopen'
7
+ CONFIG_KEY = :x_download_options
7
8
  end
8
9
  include Constants
9
10
 
@@ -5,6 +5,7 @@ module SecureHeaders
5
5
  XFO_HEADER_NAME = "X-Frame-Options"
6
6
  DEFAULT_VALUE = 'SAMEORIGIN'
7
7
  VALID_XFO_HEADER = /\A(SAMEORIGIN\z|DENY\z|ALLOW-FROM[:\s])/i
8
+ CONFIG_KEY = :x_frame_options
8
9
  end
9
10
  include Constants
10
11
 
@@ -5,6 +5,7 @@ module SecureHeaders
5
5
  XPCDP_HEADER_NAME = "X-Permitted-Cross-Domain-Policies"
6
6
  DEFAULT_VALUE = 'none'
7
7
  VALID_POLICIES = %w(all none master-only by-content-type by-ftp-filename)
8
+ CONFIG_KEY = :x_permitted_cross_domain_policies
8
9
  end
9
10
  include Constants
10
11
 
@@ -5,6 +5,7 @@ module SecureHeaders
5
5
  X_XSS_PROTECTION_HEADER_NAME = 'X-XSS-Protection'
6
6
  DEFAULT_VALUE = "1"
7
7
  VALID_X_XSS_HEADER = /\A[01](; mode=block)?(; report=.*)?\z/i
8
+ CONFIG_KEY = :x_xss_protection
8
9
  end
9
10
  include Constants
10
11
 
@@ -1,3 +1,3 @@
1
1
  module SecureHeaders
2
- VERSION = "2.2.4"
2
+ VERSION = "2.3.0"
3
3
  end
@@ -1,7 +1,32 @@
1
+ require "secure_headers/version"
2
+ require "secure_headers/header"
3
+ require "secure_headers/headers/public_key_pins"
4
+ require "secure_headers/headers/content_security_policy"
5
+ require "secure_headers/headers/x_frame_options"
6
+ require "secure_headers/headers/strict_transport_security"
7
+ require "secure_headers/headers/x_xss_protection"
8
+ require "secure_headers/headers/x_content_type_options"
9
+ require "secure_headers/headers/x_download_options"
10
+ require "secure_headers/headers/x_permitted_cross_domain_policies"
11
+ require "secure_headers/railtie"
12
+ require "secure_headers/hash_helper"
13
+ require "secure_headers/view_helper"
14
+
1
15
  module SecureHeaders
2
16
  SCRIPT_HASH_CONFIG_FILE = 'config/script_hashes.yml'
3
17
  HASHES_ENV_KEY = 'secure_headers.script_hashes'
4
18
 
19
+ ALL_HEADER_CLASSES = [
20
+ SecureHeaders::ContentSecurityPolicy,
21
+ SecureHeaders::StrictTransportSecurity,
22
+ SecureHeaders::PublicKeyPins,
23
+ SecureHeaders::XContentTypeOptions,
24
+ SecureHeaders::XDownloadOptions,
25
+ SecureHeaders::XFrameOptions,
26
+ SecureHeaders::XPermittedCrossDomainPolicies,
27
+ SecureHeaders::XXssProtection
28
+ ]
29
+
5
30
  module Configuration
6
31
  class << self
7
32
  attr_accessor :hsts, :x_frame_options, :x_content_type_options,
@@ -24,6 +49,27 @@ module SecureHeaders
24
49
  include InstanceMethods
25
50
  end
26
51
  end
52
+
53
+ def header_hash(options = nil)
54
+ ALL_HEADER_CLASSES.inject({}) do |memo, klass|
55
+ config = if options.is_a?(Hash) && options[klass::Constants::CONFIG_KEY]
56
+ options[klass::Constants::CONFIG_KEY]
57
+ else
58
+ ::SecureHeaders::Configuration.send(klass::Constants::CONFIG_KEY)
59
+ end
60
+
61
+ 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
64
+ end
65
+ memo
66
+ end
67
+ end
68
+
69
+ def get_a_header(name, klass, options)
70
+ return if options == false
71
+ klass.new(options)
72
+ end
27
73
  end
28
74
 
29
75
  module ClassMethods
@@ -161,13 +207,10 @@ module SecureHeaders
161
207
  options.nil? ? ::SecureHeaders::Configuration.send(type) : options
162
208
  end
163
209
 
164
-
165
210
  def set_a_header(name, klass, options=nil)
166
- options = secure_header_options_for name, options
211
+ options = secure_header_options_for(name, options)
167
212
  return if options == false
168
-
169
- header = klass.new(options)
170
- set_header(header)
213
+ set_header(SecureHeaders::get_a_header(name, klass, options))
171
214
  end
172
215
 
173
216
  def set_header(name_or_header, value=nil)
@@ -180,18 +223,3 @@ module SecureHeaders
180
223
  end
181
224
  end
182
225
  end
183
-
184
-
185
- require "secure_headers/version"
186
- require "secure_headers/header"
187
- require "secure_headers/headers/public_key_pins"
188
- require "secure_headers/headers/content_security_policy"
189
- require "secure_headers/headers/x_frame_options"
190
- require "secure_headers/headers/strict_transport_security"
191
- require "secure_headers/headers/x_xss_protection"
192
- require "secure_headers/headers/x_content_type_options"
193
- require "secure_headers/headers/x_download_options"
194
- require "secure_headers/headers/x_permitted_cross_domain_policies"
195
- require "secure_headers/railtie"
196
- require "secure_headers/hash_helper"
197
- require "secure_headers/view_helper"
@@ -159,6 +159,56 @@ describe SecureHeaders do
159
159
  end
160
160
  end
161
161
 
162
+ describe "SecureHeaders#header_hash" do
163
+ def expect_default_values(hash)
164
+ expect(hash[XFO_HEADER_NAME]).to eq(SecureHeaders::XFrameOptions::Constants::DEFAULT_VALUE)
165
+ expect(hash[XDO_HEADER_NAME]).to eq(SecureHeaders::XDownloadOptions::Constants::DEFAULT_VALUE)
166
+ expect(hash[HSTS_HEADER_NAME]).to eq(SecureHeaders::StrictTransportSecurity::Constants::DEFAULT_VALUE)
167
+ expect(hash[X_XSS_PROTECTION_HEADER_NAME]).to eq(SecureHeaders::XXssProtection::Constants::DEFAULT_VALUE)
168
+ expect(hash[X_CONTENT_TYPE_OPTIONS_HEADER_NAME]).to eq(SecureHeaders::XContentTypeOptions::Constants::DEFAULT_VALUE)
169
+ expect(hash[XPCDP_HEADER_NAME]).to eq(SecureHeaders::XPermittedCrossDomainPolicies::Constants::DEFAULT_VALUE)
170
+ end
171
+
172
+ it "produces a hash of headers given a hash as config" do
173
+ hash = SecureHeaders::header_hash(:csp => {:default_src => 'none', :img_src => "data:", :disable_fill_missing => true})
174
+ expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'none'; img-src data:;")
175
+ expect_default_values(hash)
176
+ end
177
+
178
+ it "produces a hash with a mix of config values, override values, and default values" do
179
+ ::SecureHeaders::Configuration.configure do |config|
180
+ config.hsts = { :max_age => '123456'}
181
+ config.hpkp = {
182
+ :enforce => true,
183
+ :max_age => 1000000,
184
+ :include_subdomains => true,
185
+ :report_uri => '//example.com/uri-directive',
186
+ :pins => [
187
+ {:sha256 => 'abc'},
188
+ {:sha256 => '123'}
189
+ ]
190
+ }
191
+ end
192
+
193
+ hash = SecureHeaders::header_hash(:csp => {:default_src => 'none', :img_src => "data:", :disable_fill_missing => true})
194
+ ::SecureHeaders::Configuration.configure do |config|
195
+ config.hsts = nil
196
+ config.hpkp = nil
197
+ end
198
+
199
+ expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'none'; img-src data:;")
200
+ expect(hash[XFO_HEADER_NAME]).to eq(SecureHeaders::XFrameOptions::Constants::DEFAULT_VALUE)
201
+ expect(hash[HSTS_HEADER_NAME]).to eq("max-age=123456")
202
+ expect(hash[HPKP_HEADER_NAME]).to eq(%{max-age=1000000; pin-sha256="abc"; pin-sha256="123"; report-uri="//example.com/uri-directive"; includeSubDomains})
203
+ end
204
+
205
+ it "produces a hash of headers with default config" do
206
+ hash = SecureHeaders::header_hash
207
+ expect(hash['Content-Security-Policy-Report-Only']).to eq(SecureHeaders::ContentSecurityPolicy::Constants::DEFAULT_CSP_HEADER)
208
+ expect_default_values(hash)
209
+ end
210
+ end
211
+
162
212
  describe "#set_x_frame_options_header" do
163
213
  it "sets the X-Frame-Options header" do
164
214
  should_assign_header(XFO_HEADER_NAME, SecureHeaders::XFrameOptions::Constants::DEFAULT_VALUE)
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.2.4
4
+ version: 2.3.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-08-26 00:00:00.000000000 Z
11
+ date: 2015-09-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake