secure_headers 2.2.4 → 2.3.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.
- checksums.yaml +4 -4
- data/README.md +30 -0
- data/lib/secure_headers/headers/content_security_policy.rb +2 -0
- data/lib/secure_headers/headers/public_key_pins.rb +1 -0
- data/lib/secure_headers/headers/strict_transport_security.rb +1 -0
- data/lib/secure_headers/headers/x_content_type_options.rb +2 -1
- data/lib/secure_headers/headers/x_download_options.rb +1 -0
- data/lib/secure_headers/headers/x_frame_options.rb +1 -0
- data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +1 -0
- data/lib/secure_headers/headers/x_xss_protection.rb +1 -0
- data/lib/secure_headers/version.rb +1 -1
- data/lib/secure_headers.rb +48 -20
- data/spec/lib/secure_headers_spec.rb +50 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2ea3335e72b8eaea53c9fc03a77c008b73fc674c
|
4
|
+
data.tar.gz: 4097b301e8e7f18174abda1c6412f9652cd4c39a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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)
|
@@ -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
|
data/lib/secure_headers.rb
CHANGED
@@ -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
|
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.
|
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-
|
11
|
+
date: 2015-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|