secure_headers 2.4.1 → 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 +4 -4
- data/.travis.yml +1 -0
- data/Gemfile +1 -0
- data/Guardfile +12 -0
- data/README.md +1 -1
- data/lib/secure_headers/headers/content_security_policy.rb +26 -17
- data/lib/secure_headers/headers/strict_transport_security.rb +1 -0
- data/lib/secure_headers/headers/x_content_type_options.rb +1 -0
- 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 +15 -6
- data/secure_headers.gemspec +1 -0
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +8 -0
- data/spec/lib/secure_headers/headers/x_content_type_options_spec.rb +2 -0
- data/spec/lib/secure_headers/headers/x_xss_protection_spec.rb +2 -0
- data/spec/lib/secure_headers_spec.rb +22 -4
- metadata +7 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 36b98616c83ec1049b8f1bcc25671738da8d8bed
|
|
4
|
+
data.tar.gz: 730108ec10da36aeeb59049e88122a99987d1dcc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 854d9e8c9de09d5515dad3449726b4b2308b8a25b5dc702ed80da47fd3f94f983119531a4089877b215ee07dccfa8fec2766107891b018bcca376ec16ed95e8f
|
|
7
|
+
data.tar.gz: f0ec419ac593cdeb556dc852d6e2a3b8f5fcfe83c07b34ac8392fcb912004bbe1117ea9e473c068230a38c44c96a5e036dfde630ae5e7397203c9aed8babd741
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
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
|
|
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
|
|
|
@@ -11,6 +11,7 @@ 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
|
+
USER_AGENT_PARSER = UserAgentParser::Parser.new
|
|
14
15
|
|
|
15
16
|
DIRECTIVES_1_0 = [
|
|
16
17
|
:default_src,
|
|
@@ -133,34 +134,42 @@ module SecureHeaders
|
|
|
133
134
|
@ua = options[:ua]
|
|
134
135
|
@ssl_request = !!options.delete(:ssl)
|
|
135
136
|
@request_uri = options.delete(:request_uri)
|
|
136
|
-
@http_additions = config.delete(:http_additions)
|
|
137
|
-
@disable_img_src_data_uri = !!config.delete(:disable_img_src_data_uri)
|
|
138
|
-
@tag_report_uri = !!config.delete(:tag_report_uri)
|
|
139
|
-
@script_hashes = config.delete(:script_hashes) || []
|
|
140
|
-
@app_name = config.delete(:app_name)
|
|
141
|
-
@app_name = @app_name.call(@controller) if @app_name.respond_to?(:call)
|
|
142
|
-
@enforce = config.delete(:enforce)
|
|
143
|
-
@enforce = @enforce.call(@controller) if @enforce.respond_to?(:call)
|
|
144
|
-
@enforce = !!@enforce
|
|
145
137
|
|
|
146
138
|
# Config values can be string, array, or lamdba values
|
|
147
139
|
@config = config.inject({}) do |hash, (key, value)|
|
|
148
|
-
config_val = value.respond_to?(:call)
|
|
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
|
|
146
|
+
|
|
149
147
|
if ALL_DIRECTIVES.include?(key.to_sym) # directives need to be normalized to arrays of strings
|
|
150
|
-
|
|
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
|
|
151
152
|
if config_val.is_a?(Array)
|
|
152
153
|
config_val = config_val.map do |val|
|
|
153
154
|
translate_dir_value(val)
|
|
154
155
|
end.flatten.uniq
|
|
155
156
|
end
|
|
156
|
-
elsif key != :script_hash_middleware
|
|
157
|
-
raise ArgumentError.new("Unknown directive supplied: #{key}")
|
|
158
157
|
end
|
|
159
158
|
|
|
160
159
|
hash[key] = config_val
|
|
161
160
|
hash
|
|
162
161
|
end
|
|
163
162
|
|
|
163
|
+
@http_additions = @config.delete(:http_additions)
|
|
164
|
+
@disable_img_src_data_uri = !!@config.delete(:disable_img_src_data_uri)
|
|
165
|
+
@tag_report_uri = !!@config.delete(:tag_report_uri)
|
|
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
|
+
|
|
164
173
|
# normalize and tag the report-uri
|
|
165
174
|
if @config[:report_uri]
|
|
166
175
|
@config[:report_uri] = @config[:report_uri].map do |report_uri|
|
|
@@ -258,10 +267,10 @@ module SecureHeaders
|
|
|
258
267
|
|
|
259
268
|
def translate_dir_value val
|
|
260
269
|
if %w{inline eval}.include?(val)
|
|
261
|
-
warn "[DEPRECATION] using inline/eval
|
|
270
|
+
warn "[DEPRECATION] using inline/eval is not suppored in secure_headers 3.x. Instead use 'unsafe-inline'/'unsafe-eval' instead."
|
|
262
271
|
val == 'inline' ? "'unsafe-inline'" : "'unsafe-eval'"
|
|
263
272
|
elsif %{self none}.include?(val)
|
|
264
|
-
warn "[DEPRECATION] using self/none
|
|
273
|
+
warn "[DEPRECATION] using self/none is not suppored in secure_headers 3.x. Instead use 'self'/'none' instead."
|
|
265
274
|
"'#{val}'"
|
|
266
275
|
elsif val == 'nonce'
|
|
267
276
|
if supports_nonces?
|
|
@@ -305,7 +314,7 @@ module SecureHeaders
|
|
|
305
314
|
end
|
|
306
315
|
|
|
307
316
|
def supported_directives
|
|
308
|
-
@supported_directives ||= case
|
|
317
|
+
@supported_directives ||= case USER_AGENT_PARSER.parse(@ua).family
|
|
309
318
|
when "Chrome"
|
|
310
319
|
CHROME_DIRECTIVES
|
|
311
320
|
when "Safari"
|
|
@@ -318,7 +327,7 @@ module SecureHeaders
|
|
|
318
327
|
end
|
|
319
328
|
|
|
320
329
|
def supports_nonces?
|
|
321
|
-
parsed_ua =
|
|
330
|
+
parsed_ua = USER_AGENT_PARSER.parse(@ua)
|
|
322
331
|
["Chrome", "Opera", "Firefox"].include?(parsed_ua.family)
|
|
323
332
|
end
|
|
324
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 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]
|
data/lib/secure_headers.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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(
|
|
222
|
+
set_header(SecureHeaders::get_a_header(klass, options))
|
|
214
223
|
end
|
|
215
224
|
|
|
216
225
|
def set_header(name_or_header, value=nil)
|
data/secure_headers.gemspec
CHANGED
|
@@ -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
|
|
@@ -142,6 +142,14 @@ module SecureHeaders
|
|
|
142
142
|
end
|
|
143
143
|
|
|
144
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
|
+
|
|
145
153
|
context "browser sniffing" do
|
|
146
154
|
let(:complex_opts) do
|
|
147
155
|
ALL_DIRECTIVES.inject({}) { |memo, directive| memo[directive] = "'self'"; memo }.merge(:block_all_mixed_content => '')
|
|
@@ -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.
|
|
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.
|
|
142
|
+
::SecureHeaders::Configuration.default do |config|
|
|
142
143
|
config.hsts = false
|
|
143
144
|
config.hpkp = false
|
|
144
145
|
config.x_frame_options = false
|
|
@@ -171,8 +172,25 @@ describe SecureHeaders do
|
|
|
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.
|
|
193
|
+
::SecureHeaders::Configuration.default do |config|
|
|
176
194
|
config.hsts = { :max_age => '123456'}
|
|
177
195
|
config.hpkp = {
|
|
178
196
|
:enforce => true,
|
|
@@ -187,7 +205,7 @@ describe SecureHeaders do
|
|
|
187
205
|
end
|
|
188
206
|
|
|
189
207
|
hash = SecureHeaders::header_hash(:csp => {:default_src => "'none'", :img_src => "data:"})
|
|
190
|
-
::SecureHeaders::Configuration.
|
|
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
|
+
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:
|
|
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
|