secure_headers 1.1.1 → 1.2.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 ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZDFmNDM2NjIwYzU4MjcyYWU0ODkwMjkxMjM0MDMwYTAyNjY3YWM2ZA==
5
+ data.tar.gz: !binary |-
6
+ N2VjN2M3MGMyY2Y5ODliOTVmZTIxZDgwOTdjYTM1NTg3MDZiMjdjNw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MTRiN2Y5NDFhYTE3ZmM2MjE4MThmMDQ3OTZmOWVhMWM5OTgxOThlNzFiOGNm
10
+ MzQ2ZmFiMjUyZjdmNWU4MWQwZTY3Yjc4YmM4NzVjMWNhZWE0YzI3YjlmMjcy
11
+ MDk3YmNiZmI3NGNmMzFmMDkxNjEyOTFkMmE5YjNlNjJlMDBkYWY=
12
+ data.tar.gz: !binary |-
13
+ NzRhM2Y5NTk0OTZhMmRlYjk0ZThkYTM3ZTQxYzc3Mjk3ZTY2MjQ4MjUwN2Rm
14
+ YzUwMGY4ZTdlY2Q2M2M4NDgyODQ1MTc2ZWM3ZmEyOWU5NWIwNWExMjBlMDBi
15
+ YWNlYjllMmZmN2M4MTU0NWE3NjIwOGNjMmQ5ODBlYzE4NzM1Njk=
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ secureheaders
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-1.9.3-p484
data/.travis.yml CHANGED
@@ -1,4 +1,6 @@
1
1
  rvm:
2
+ - "2.1.0"
3
+ - "2.0.0"
2
4
  - "1.9.3"
3
5
  - "1.8.7"
4
6
  # - jruby-19mode
data/Gemfile CHANGED
@@ -8,9 +8,7 @@ group :test do
8
8
  gem 'jdbc-sqlite3', :platform => :jruby
9
9
  gem 'rspec-rails'
10
10
  gem 'spork'
11
- gem 'pry'
12
11
  gem 'rspec'
13
- gem 'guard-spork', :platform => :ruby_19
14
12
  gem 'guard-rspec', :platform => :ruby_19
15
13
  gem 'growl'
16
14
  gem 'rb-fsevent'
data/Guardfile CHANGED
@@ -1,8 +1,4 @@
1
- guard 'spork', :aggressive_kill => false do
2
- watch('spec/spec_helper.rb') { :rspec }
3
- end
4
-
5
- guard 'rspec', :cli => "--color --drb --debug", :keep_failed => true, :all_after_pass => true, :focus_on_failed => true do
1
+ guard 'rspec' do
6
2
  watch(%r{^spec/.+_spec\.rb$})
7
3
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
8
4
  watch(%r{^app/controllers/(.+)\.rb$}) { |m| "spec/controllers/#{m[1]}_spec.rb" }
data/HISTORY.md CHANGED
@@ -1,3 +1,19 @@
1
+ 1.2.0
2
+ ======
3
+
4
+ Add support for procs (anything that respond_to?(:call)) as config values.
5
+
6
+ csp = {
7
+ :default_src => 'self',
8
+ :report_uri => lambda {
9
+ if FeatureToggle.available?(:new_csp_endpoint)
10
+ '//example.com/new_csp'
11
+ else
12
+ '//example.com/old_csp'
13
+ end
14
+ }
15
+ }
16
+
1
17
  1.1.1
2
18
  ======
3
19
 
data/README.md CHANGED
@@ -85,9 +85,10 @@ Or simply add it to application controller
85
85
 
86
86
  ```ruby
87
87
  ensure_security_headers(
88
- :hsts => {:include_subdomains, :x_frame_options => false},
88
+ :hsts => {:include_subdomains => true, :max_age => 20.years.to_i},
89
89
  :x_frame_options => 'DENY',
90
- :csp => false)
90
+ :csp => false
91
+ )
91
92
  ```
92
93
 
93
94
  ## Options for ensure\_security\_headers
@@ -117,6 +118,9 @@ and [Mozilla CSP specification](https://wiki.mozilla.org/Security/CSP/Specificat
117
118
 
118
119
  ```ruby
119
120
  :csp => {
121
+
122
+ # All values can be a String of space-delimited values, an Array of Strings, or procs.
123
+
120
124
  :enforce => false, # sets header to report-only, by default
121
125
  # default_src is required!
122
126
  :default_src => nil, # sets the default-src/allow+options directives
@@ -162,13 +166,6 @@ and [Mozilla CSP specification](https://wiki.mozilla.org/Security/CSP/Specificat
162
166
  :img_src => 'http://mycdn.example.com'
163
167
  }
164
168
  }
165
-
166
- # script-nonce is an experimental feature of CSP 1.1 available in Chrome. It allows
167
- # you to whitelist inline script blocks. For more information, see
168
- # https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html#script-nonce
169
- :script_nonce => lambda { @script_nonce = SecureRandom.hex }
170
- # which can be used to whitelist a script block:
171
- # script_tag :nonce = @script_nonce { inline_script_call() }
172
169
  }
173
170
  ```
174
171
 
@@ -196,17 +193,23 @@ and [Mozilla CSP specification](https://wiki.mozilla.org/Security/CSP/Specificat
196
193
  :csp => {
197
194
  :default_src => 'self',
198
195
  :img_src => '*',
196
+ :connect_src => 'none'
199
197
  :object_src => ['media1.com', 'media2.com', '*.cdn.com'],
200
- # alternatively (NOT csv) :object_src => 'media1.com media2.com *.cdn.com'
201
- :script_src => 'trustedscripts.example.com'
198
+ # alternatively :object_src => 'media1.com media2.com *.cdn.com'
199
+ :script_src => 'trustedscripts.example.com',
200
+ :report_uri => lambda {
201
+ if FeatureToggle.available?(:new_csp_endpoint)
202
+ '//example.com/new_csp'
203
+ else
204
+ '//example.com/old_csp'
205
+ end
206
+ }
202
207
  }
203
- "default-src 'self'; img-src *; object-src media1.com media2.com *.cdn.com; script-src trustedscripts.example.com;"
208
+ "default-src 'self'; connect-src 'none'; img-src *; object-src media1.com media2.com *.cdn.com; script-src trustedscripts.example.com; report-uri [one of the two values in the example]"
204
209
  ```
205
210
 
206
211
  ## Note on Firefox handling of CSP
207
212
 
208
- Currently, Firefox does not support the w3c draft standard. So there are a few steps taken to make the two interchangeable.
209
-
210
213
  * CSP reports will not POST cross\-origin. This sets up an internal endpoint in the application that will forward the request. Set the `forward_endpoint` value in the CSP section if you need to post cross origin for firefox. The internal endpoint that receives the initial request will forward the request to `forward_endpoint`
211
214
 
212
215
  ### Adding the Firefox report forwarding endpoint
@@ -304,7 +307,7 @@ end
304
307
  * Node.js (express) [helmet](https://github.com/evilpacket/helmet) and [hood](https://github.com/seanmonstar/hood)
305
308
  * J2EE Servlet >= 3.0 [highlines](https://github.com/sourceclear/headlines)
306
309
  * ASP.NET - [NWebsec](http://nwebsec.codeplex.com/)
307
- * Python - [django-csp](https://github.com/mozilla/django-csp/tree/master/csp) + [commonware](https://github.com/jsocol/commonware/tree/master/commonware/request)
310
+ * Python - [django-csp](https://github.com/mozilla/django-csp/) + [commonware](https://github.com/jsocol/commonware/)
308
311
  * Go - [secureheader](https://github.com/kr/secureheader)
309
312
 
310
313
  ## Authors
@@ -1,44 +1,39 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe OtherThingsController do
3
+ describe OtherThingsController, :type => :controller do
4
4
  describe "headers" do
5
- before(:each) do
6
- # Chrome
7
- request.env['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5'
8
- end
9
-
10
5
  it "sets the X-XSS-Protection header" do
11
6
  get :index
12
- response.headers['X-XSS-Protection'].should == '1; mode=block'
7
+ expect(response.headers['X-XSS-Protection']).to eq('1; mode=block')
13
8
  end
14
9
 
15
10
  it "sets the X-Frame-Options header" do
16
11
  get :index
17
- response.headers['X-Frame-Options'].should == 'SAMEORIGIN'
12
+ expect(response.headers['X-Frame-Options']).to eq('SAMEORIGIN')
18
13
  end
19
14
 
20
15
  it "sets the X-WebKit-CSP header" do
21
16
  get :index
22
- response.headers['Content-Security-Policy-Report-Only'].should == "default-src 'self'; img-src data:; report-uri somewhere;"
17
+ expect(response.headers['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; img-src data:; report-uri somewhere;")
23
18
  end
24
19
 
25
20
  #mock ssl
26
21
  it "sets the Strict-Transport-Security header" do
27
22
  request.env['HTTPS'] = 'on'
28
23
  get :index
29
- response.headers['Strict-Transport-Security'].should == "max-age=315576000"
24
+ expect(response.headers['Strict-Transport-Security']).to eq("max-age=315576000")
30
25
  end
31
26
 
32
27
  it "sets the X-Content-Type-Options header" do
33
28
  get :index
34
- response.headers['X-Content-Type-Options'].should == "nosniff"
29
+ expect(response.headers['X-Content-Type-Options']).to eq("nosniff")
35
30
  end
36
31
 
37
32
  context "using IE" do
38
33
  it "sets the X-Content-Type-Options header" do
39
34
  request.env['HTTP_USER_AGENT'] = "Mozilla/5.0 (compatible; MSIE 10.6; Windows NT 6.1; Trident/5.0; InfoPath.2; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727) 3gpp-gba UNTRUSTED/1.0"
40
35
  get :index
41
- response.headers['X-Content-Type-Options'].should == "nosniff"
36
+ expect(response.headers['X-Content-Type-Options']).to eq("nosniff")
42
37
  end
43
38
  end
44
39
  end
@@ -4,45 +4,40 @@ require 'spec_helper'
4
4
  # all values are defaulted because no initializer is configured, and the values in app controller
5
5
  # only provide csp => false
6
6
 
7
- describe ThingsController do
7
+ describe ThingsController, :type => :controller do
8
8
  describe "headers" do
9
- before(:each) do
10
- # Chrome
11
- request.env['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5'
12
- end
13
-
14
9
  it "sets the X-XSS-Protection header" do
15
10
  get :index
16
- response.headers['X-XSS-Protection'].should == '1; mode=block'
11
+ expect(response.headers['X-XSS-Protection']).to eq('1; mode=block')
17
12
  end
18
13
 
19
14
  it "sets the X-Frame-Options header" do
20
15
  get :index
21
- response.headers['X-Frame-Options'].should == 'SAMEORIGIN'
16
+ expect(response.headers['X-Frame-Options']).to eq('SAMEORIGIN')
22
17
  end
23
18
 
24
19
  it "sets the X-WebKit-CSP header" do
25
20
  get :index
26
- response.headers['Content-Security-Policy-Report-Only'].should == nil
21
+ expect(response.headers['Content-Security-Policy-Report-Only']).to eq(nil)
27
22
  end
28
23
 
29
24
  #mock ssl
30
25
  it "sets the Strict-Transport-Security header" do
31
26
  request.env['HTTPS'] = 'on'
32
27
  get :index
33
- response.headers['Strict-Transport-Security'].should == "max-age=315576000"
28
+ expect(response.headers['Strict-Transport-Security']).to eq("max-age=315576000")
34
29
  end
35
30
 
36
31
  it "sets the X-Content-Type-Options header" do
37
32
  get :index
38
- response.headers['X-Content-Type-Options'].should == "nosniff"
33
+ expect(response.headers['X-Content-Type-Options']).to eq("nosniff")
39
34
  end
40
35
 
41
36
  context "using IE" do
42
37
  it "sets the X-Content-Type-Options header" do
43
38
  request.env['HTTP_USER_AGENT'] = "Mozilla/5.0 (compatible; MSIE 10.6; Windows NT 6.1; Trident/5.0; InfoPath.2; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727) 3gpp-gba UNTRUSTED/1.0"
44
39
  get :index
45
- response.headers['X-Content-Type-Options'].should == "nosniff"
40
+ expect(response.headers['X-Content-Type-Options']).to eq("nosniff")
46
41
  end
47
42
  end
48
43
  end
@@ -11,7 +11,6 @@ require 'spork'
11
11
  ENV["RAILS_ENV"] ||= 'test'
12
12
  require File.expand_path("../../config/environment", __FILE__)
13
13
  require 'rspec/rails'
14
- require 'rspec/autorun'
15
14
  # end
16
15
 
17
16
  Spork.each_run do
@@ -1,44 +1,39 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe OtherThingsController do
3
+ describe OtherThingsController, :type => :controller do
4
4
  describe "headers" do
5
- before(:each) do
6
- # Chrome
7
- request.env['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5'
8
- end
9
-
10
5
  it "sets the X-XSS-Protection header" do
11
6
  get :index
12
- response.headers['X-XSS-Protection'].should == SecureHeaders::XXssProtection::Constants::DEFAULT_VALUE
7
+ expect(response.headers['X-XSS-Protection']).to eq(SecureHeaders::XXssProtection::Constants::DEFAULT_VALUE)
13
8
  end
14
9
 
15
10
  it "sets the X-Frame-Options header" do
16
11
  get :index
17
- response.headers['X-Frame-Options'].should == SecureHeaders::XFrameOptions::Constants::DEFAULT_VALUE
12
+ expect(response.headers['X-Frame-Options']).to eq(SecureHeaders::XFrameOptions::Constants::DEFAULT_VALUE)
18
13
  end
19
14
 
20
15
  it "sets the X-WebKit-CSP header" do
21
16
  get :index
22
- response.headers['Content-Security-Policy-Report-Only'].should == "default-src 'self'; img-src data:;"
17
+ expect(response.headers['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; img-src data:;")
23
18
  end
24
19
 
25
20
  #mock ssl
26
21
  it "sets the Strict-Transport-Security header" do
27
22
  request.env['HTTPS'] = 'on'
28
23
  get :index
29
- response.headers['Strict-Transport-Security'].should == SecureHeaders::StrictTransportSecurity::Constants::DEFAULT_VALUE
24
+ expect(response.headers['Strict-Transport-Security']).to eq(SecureHeaders::StrictTransportSecurity::Constants::DEFAULT_VALUE)
30
25
  end
31
26
 
32
27
  it "sets the X-Content-Type-Options header" do
33
28
  get :index
34
- response.headers['X-Content-Type-Options'].should == SecureHeaders::XContentTypeOptions::Constants::DEFAULT_VALUE
29
+ expect(response.headers['X-Content-Type-Options']).to eq(SecureHeaders::XContentTypeOptions::Constants::DEFAULT_VALUE)
35
30
  end
36
31
 
37
32
  context "using IE" do
38
33
  it "sets the X-Content-Type-Options header" do
39
34
  request.env['HTTP_USER_AGENT'] = "Mozilla/5.0 (compatible; MSIE 10.6; Windows NT 6.1; Trident/5.0; InfoPath.2; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727) 3gpp-gba UNTRUSTED/1.0"
40
35
  get :index
41
- response.headers['X-Content-Type-Options'].should == SecureHeaders::XContentTypeOptions::Constants::DEFAULT_VALUE
36
+ expect(response.headers['X-Content-Type-Options']).to eq(SecureHeaders::XContentTypeOptions::Constants::DEFAULT_VALUE)
42
37
  end
43
38
  end
44
39
  end
@@ -4,45 +4,40 @@ require 'spec_helper'
4
4
  # all values are defaulted because no initializer is configured, and the values in app controller
5
5
  # only provide csp => false
6
6
 
7
- describe ThingsController do
7
+ describe ThingsController, :type => :controller do
8
8
  describe "headers" do
9
- before(:each) do
10
- # Chrome
11
- request.env['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5'
12
- end
13
-
14
9
  it "sets the X-XSS-Protection header" do
15
10
  get :index
16
- response.headers['X-XSS-Protection'].should == SecureHeaders::XXssProtection::Constants::DEFAULT_VALUE
11
+ expect(response.headers['X-XSS-Protection']).to eq(SecureHeaders::XXssProtection::Constants::DEFAULT_VALUE)
17
12
  end
18
13
 
19
14
  it "sets the X-Frame-Options header" do
20
15
  get :index
21
- response.headers['X-Frame-Options'].should == SecureHeaders::XFrameOptions::Constants::DEFAULT_VALUE
16
+ expect(response.headers['X-Frame-Options']).to eq(SecureHeaders::XFrameOptions::Constants::DEFAULT_VALUE)
22
17
  end
23
18
 
24
19
  it "sets the X-WebKit-CSP header" do
25
20
  get :index
26
- response.headers['Content-Security-Policy-Report-Only'].should == nil
21
+ expect(response.headers['Content-Security-Policy-Report-Only']).to eq(nil)
27
22
  end
28
23
 
29
24
  #mock ssl
30
25
  it "sets the Strict-Transport-Security header" do
31
26
  request.env['HTTPS'] = 'on'
32
27
  get :index
33
- response.headers['Strict-Transport-Security'].should == SecureHeaders::StrictTransportSecurity::Constants::DEFAULT_VALUE
28
+ expect(response.headers['Strict-Transport-Security']).to eq(SecureHeaders::StrictTransportSecurity::Constants::DEFAULT_VALUE)
34
29
  end
35
30
 
36
31
  it "sets the X-Content-Type-Options header" do
37
32
  get :index
38
- response.headers['X-Content-Type-Options'].should == SecureHeaders::XContentTypeOptions::Constants::DEFAULT_VALUE
33
+ expect(response.headers['X-Content-Type-Options']).to eq(SecureHeaders::XContentTypeOptions::Constants::DEFAULT_VALUE)
39
34
  end
40
35
 
41
36
  context "using IE" do
42
37
  it "sets the X-Content-Type-Options header" do
43
38
  request.env['HTTP_USER_AGENT'] = "Mozilla/5.0 (compatible; MSIE 10.6; Windows NT 6.1; Trident/5.0; InfoPath.2; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727) 3gpp-gba UNTRUSTED/1.0"
44
39
  get :index
45
- response.headers['X-Content-Type-Options'].should == SecureHeaders::XContentTypeOptions::Constants::DEFAULT_VALUE
40
+ expect(response.headers['X-Content-Type-Options']).to eq(SecureHeaders::XContentTypeOptions::Constants::DEFAULT_VALUE)
46
41
  end
47
42
  end
48
43
  end
@@ -11,7 +11,6 @@ Spork.prefork do
11
11
  ENV["RAILS_ENV"] ||= 'test'
12
12
  require File.expand_path("../../config/environment", __FILE__)
13
13
  require 'rspec/rails'
14
- require 'rspec/autorun'
15
14
  # require 'ruby-debug'
16
15
  end
17
16
 
@@ -58,9 +58,6 @@ module SecureHeaders
58
58
  self.send("#{meta}=", @config.delete(meta))
59
59
  end
60
60
 
61
- @report_uri = @config.delete(:report_uri)
62
- @script_nonce = @config.delete(:script_nonce)
63
-
64
61
  normalize_csp_options
65
62
  normalize_reporting_endpoint
66
63
  fill_directives unless disable_fill_missing?
@@ -92,8 +89,7 @@ module SecureHeaders
92
89
  # ensure default-src is first
93
90
  build_directive(:default_src),
94
91
  generic_directives(@config),
95
- report_uri_directive,
96
- script_nonce_directive,
92
+ report_uri_directive
97
93
  ].join
98
94
 
99
95
  #store the value for next time
@@ -123,12 +119,23 @@ module SecureHeaders
123
119
  end
124
120
 
125
121
  def normalize_csp_options
126
- @config.each do |k,v|
127
- @config[k] = v.split if v.is_a? String
128
- @config[k] = @config[k].map do |val|
122
+ @config = @config.inject({}) do |hash, (k, v)|
123
+ config_val = if v.respond_to?(:call)
124
+ v.call
125
+ else
126
+ v
127
+ end
128
+
129
+ config_val = config_val.split if config_val.is_a? String
130
+ config_val = config_val.map do |val|
129
131
  translate_dir_value(val)
130
132
  end
133
+
134
+ hash[k] = config_val
135
+ hash
131
136
  end
137
+
138
+ @report_uri = @config.delete(:report_uri).join(" ") if @config[:report_uri]
132
139
  end
133
140
 
134
141
  # translates 'inline','self', 'none' and 'eval' to their respective impl-specific values.
@@ -180,18 +187,6 @@ module SecureHeaders
180
187
  "report-uri #{@report_uri};"
181
188
  end
182
189
 
183
- def script_nonce_directive
184
- return '' if @script_nonce.nil?
185
- nonce_value = if @script_nonce.is_a?(String)
186
- @script_nonce
187
- elsif @controller
188
- @controller.instance_exec(&@script_nonce)
189
- else
190
- @script_nonce.call
191
- end
192
- "script-nonce #{nonce_value};"
193
- end
194
-
195
190
  def generic_directives(config)
196
191
  header_value = ''
197
192
  if config[:img_src]
@@ -1,3 +1,3 @@
1
1
  module SecureHeaders
2
- VERSION = "1.1.1"
2
+ VERSION = "1.2.0"
3
3
  end