secure_headers 0.4.0 → 0.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.
data/HISTORY.md CHANGED
@@ -1,10 +1,33 @@
1
+ 0.5.0
2
+ ======
3
+
4
+ X-Content-Type-Options also applied to Chrome requests
5
+
6
+ 0.4.3
7
+ ======
8
+
9
+ Safari 5 is just completely broken when CSP is used, both mobile and desktop versions
10
+
11
+ 0.4.2
12
+ ======
13
+
14
+ - Stupid bug where Fixnums couldn't be used for config values
15
+ - Doc updates
16
+
17
+ 0.4.1
18
+ ======
19
+
20
+ - Allow strings or ints in the HSTS max-age (@reedloden)
21
+
1
22
  0.4.0
2
23
  =======
24
+
3
25
  - Treat each header as it's own before_filter. This allows you to `skip_before_filter :set_X_header, :only => :bad_idea
4
26
  - Should be backwards compatible, but it is a change to the API.
5
27
 
6
28
  0.3.0
7
29
  =======
30
+
8
31
  - Greatly reduce the need to use the forward_endpoint attribute. If you are posting from your site to a host that matches TLD+1 (e.g. translate.twitter.com matches twitter.com), use a protocol relative value for report-uri. This will alleviate the need to use forwarding. If your host doesn't match, you still need to use forwarding due to host mismatches for Firefox.
9
32
 
10
33
  0.2.3
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  The gem will automatically apply several headers that are related to security. This includes:
4
4
  - Content Security Policy (CSP) - Helps detect/prevent XSS, mixed-content, and other classes of attack. [CSP 1.1 Specification](https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html)
5
5
  - HTTP Strict Transport Security (HSTS) - Ensures the browser never visits the http version of a website. Protects from SSLStrip/Firesheep attacks. [HSTS Specification](https://tools.ietf.org/html/rfc6797)
6
- - X-Frame-Options (XFO) - Prevents your content from being framed and potentially clickjacked. [X-Frame-Options draft](https://tools.ietf.org/html/draft-ietf-websec-x-frame-options-00)
6
+ - X-Frame-Options (XFO) - Prevents your content from being framed and potentially clickjacked. [X-Frame-Options draft](https://tools.ietf.org/html/draft-ietf-websec-x-frame-options-02)
7
7
  - X-XSS-Protection - [Cross site scripting heuristic filter for IE/Chrome](http://msdn.microsoft.com/en-us/library/dd565647\(v=vs.85\).aspx)
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
 
@@ -41,7 +41,7 @@ By default, it will set all of the headers listed in the options section below u
41
41
 
42
42
  Use the standard `skip_before_filter :filter_name, options` mechanism. e.g. `skip_before_filter :set_csp_header, :only => :tinymce_page`
43
43
 
44
- The following methods are going to be called, unles they are provided in a `skip_before_filter` block.
44
+ The following methods are going to be called, unless they are provided in a `skip_before_filter` block.
45
45
 
46
46
  * `:set_csp_header`
47
47
  * `:set_hsts_header`
@@ -53,10 +53,10 @@ The following methods are going to be called, unles they are provided in a `skip
53
53
 
54
54
  This gem makes a few assumptions about how you will use some features. For example:
55
55
 
56
- * It adds 'chrome-extension:' to your CSP directives by default. This helps drastically reduce the amount of reports, but you can also disable this feature by supplying :disable_chrome_extension => true.
57
- * It fills any blank directives with the value in :default_src Getting a default\-src report is pretty useless. This way, you will always know what type of violation occurred. You can disable this feature by supplying :disable_fill_missing => true.
56
+ * It adds 'chrome-extension:' to your CSP directives by default. This helps drastically reduce the amount of reports, but you can also disable this feature by supplying `:disable_chrome_extension => true`.
57
+ * It fills any blank directives with the value in `:default_src` Getting a default\-src report is pretty useless. This way, you will always know what type of violation occurred. You can disable this feature by supplying `:disable_fill_missing => true`.
58
58
  * It copies the connect\-src value to xhr\-src for AJAX requests when using Firefox.
59
- * Firefox does not support cross\-origin CSP reports. If we are using Firefox, AND the value for :report_uri does not satisfy the same\-origin requirements, we will instead forward to an internal endpoint (`FF_CSP_ENDPOINT`). This is also the case if :report_uri only contains a path, which we assume will be cross host. This endpoint will in turn forward the request to the value in :forward_endpoint without restriction. More information can be found in the "Note on Firefox handling of CSP" section.
59
+ * Firefox does not support cross\-origin CSP reports. If we are using Firefox, AND the value for `:report_uri` does not satisfy the same\-origin requirements, we will instead forward to an internal endpoint (`FF_CSP_ENDPOINT`). This is also the case if `:report_uri` only contains a path, which we assume will be cross host. This endpoint will in turn forward the request to the value in `:forward_endpoint` without restriction. More information can be found in the "Note on Firefox handling of CSP" section.
60
60
 
61
61
 
62
62
  ## Configuration
@@ -77,7 +77,7 @@ This gem makes a few assumptions about how you will use some features. For exam
77
77
  }
78
78
  end
79
79
 
80
- # and then simply include
80
+ # and then simply include this in application_controller
81
81
  ensure_security_headers
82
82
  ```
83
83
 
@@ -101,7 +101,7 @@ header will be constructed using the supplied options.
101
101
  ### Widely supported
102
102
 
103
103
  ```ruby
104
- :hsts => {:max_age => 631138519, :include_subdomain => true}
104
+ :hsts => {:max_age => 631138519, :include_subdomains => true}
105
105
  :x_frame_options => {:value => 'SAMEORIGIN'}
106
106
  :x_xss_protection => {:value => 1, :mode => false} # set the :mode option to 'block' to enforce the browser's xss filter
107
107
  ```
@@ -159,6 +159,16 @@ and [Mozilla CSP specification](https://wiki.mozilla.org/Security/CSP/Specificat
159
159
  :img_src => 'http://mycdn.example.com'
160
160
  }
161
161
  }
162
+
163
+ # script-nonce is an experimental feature of CSP 1.1 available in Chrome. It allows
164
+ # you to whitelist inline script blocks. For more information, see
165
+ # https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html#script-nonce
166
+ :script_nonce => { 'abc123' }
167
+
168
+ # you can also use lambdas to use dynamically generated nonces
169
+ :script_nonce => lambda { @script_nonce] = 'something' }
170
+ # which can be used to whitelist a script block:
171
+ # script_tag :nonce = @script_nonce { inline_script_call() }
162
172
  }
163
173
  ```
164
174
 
@@ -2,7 +2,7 @@
2
2
  config.hsts = { :max_age => 10.years.to_i.to_s, :include_subdomains => false }
3
3
  config.x_frame_options = 'SAMEORIGIN'
4
4
  config.x_content_type_options = "nosniff"
5
- config.x_xss_protection = {:value => 1, :mode => 'BLOCK'}
5
+ config.x_xss_protection = {:value => 1, :mode => 'block'}
6
6
  csp = {
7
7
  :default_src => "self",
8
8
  :disable_chrome_extension => true,
@@ -12,4 +12,4 @@
12
12
  }
13
13
 
14
14
  config.csp = csp
15
- end
15
+ end
@@ -7,14 +7,14 @@ describe OtherThingsController do
7
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
8
  end
9
9
 
10
- it "sets the X-XSS-PROTECTION header" do
10
+ it "sets the X-XSS-Protection header" do
11
11
  get :index
12
- response.headers['X-XSS-Protection'].should == '1; mode=BLOCK'
12
+ response.headers['X-XSS-Protection'].should == '1; mode=block'
13
13
  end
14
14
 
15
- it "sets the X-FRAME-OPTIONS header" do
15
+ it "sets the X-Frame-Options header" do
16
16
  get :index
17
- response.headers['X-FRAME-OPTIONS'].should == 'SAMEORIGIN'
17
+ response.headers['X-Frame-Options'].should == 'SAMEORIGIN'
18
18
  end
19
19
 
20
20
  it "sets the X-WebKit-CSP header" do
@@ -23,14 +23,19 @@ describe OtherThingsController do
23
23
  end
24
24
 
25
25
  #mock ssl
26
- it "sets the STRICT-TRANSPORT-SECURITY header" do
26
+ it "sets the Strict-Transport-Security header" do
27
27
  request.env['HTTPS'] = 'on'
28
28
  get :index
29
29
  response.headers['Strict-Transport-Security'].should == "max-age=315576000"
30
30
  end
31
31
 
32
+ it "sets the X-Content-Type-Options header" do
33
+ get :index
34
+ response.headers['X-Content-Type-Options'].should == "nosniff"
35
+ end
36
+
32
37
  context "using IE" do
33
- it "sets the X-CONTENT-TYPE-OPTIONS header" do
38
+ it "sets the X-Content-Type-Options header" do
34
39
  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"
35
40
  get :index
36
41
  response.headers['X-Content-Type-Options'].should == "nosniff"
@@ -11,14 +11,14 @@ describe ThingsController do
11
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
12
  end
13
13
 
14
- it "sets the X-XSS-PROTECTION header" do
14
+ it "sets the X-XSS-Protection header" do
15
15
  get :index
16
- response.headers['X-XSS-Protection'].should == '1; mode=BLOCK'
16
+ response.headers['X-XSS-Protection'].should == '1; mode=block'
17
17
  end
18
18
 
19
- it "sets the X-FRAME-OPTIONS header" do
19
+ it "sets the X-Frame-Options header" do
20
20
  get :index
21
- response.headers['X-FRAME-OPTIONS'].should == 'SAMEORIGIN'
21
+ response.headers['X-Frame-Options'].should == 'SAMEORIGIN'
22
22
  end
23
23
 
24
24
  it "sets the X-WebKit-CSP header" do
@@ -27,14 +27,19 @@ describe ThingsController do
27
27
  end
28
28
 
29
29
  #mock ssl
30
- it "sets the STRICT-TRANSPORT-SECURITY header" do
30
+ it "sets the Strict-Transport-Security header" do
31
31
  request.env['HTTPS'] = 'on'
32
32
  get :index
33
33
  response.headers['Strict-Transport-Security'].should == "max-age=315576000"
34
34
  end
35
35
 
36
+ it "sets the X-Content-Type-Options header" do
37
+ get :index
38
+ response.headers['X-Content-Type-Options'].should == "nosniff"
39
+ end
40
+
36
41
  context "using IE" do
37
- it "sets the X-CONTENT-TYPE-OPTIONS header" do
42
+ it "sets the X-Content-Type-Options header" do
38
43
  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"
39
44
  get :index
40
45
  response.headers['X-Content-Type-Options'].should == "nosniff"
@@ -44,4 +49,4 @@ describe ThingsController do
44
49
  end
45
50
 
46
51
 
47
- # response.headers['X-WebKit-CSP-Report-Only'].should == "default-src 'self'; report-uri somewhere"
52
+ # response.headers['X-WebKit-CSP-Report-Only'].should == "default-src 'self'; report-uri somewhere"
@@ -7,14 +7,14 @@ describe OtherThingsController do
7
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
8
  end
9
9
 
10
- it "sets the X-XSS-PROTECTION header" do
10
+ it "sets the X-XSS-Protection header" do
11
11
  get :index
12
12
  response.headers['X-XSS-Protection'].should == SecureHeaders::XXssProtection::Constants::DEFAULT_VALUE
13
13
  end
14
14
 
15
- it "sets the X-FRAME-OPTIONS header" do
15
+ it "sets the X-Frame-Options header" do
16
16
  get :index
17
- response.headers['X-FRAME-OPTIONS'].should == SecureHeaders::XFrameOptions::Constants::DEFAULT_VALUE
17
+ response.headers['X-Frame-Options'].should == SecureHeaders::XFrameOptions::Constants::DEFAULT_VALUE
18
18
  end
19
19
 
20
20
  it "sets the X-WebKit-CSP header" do
@@ -23,17 +23,22 @@ describe OtherThingsController do
23
23
  end
24
24
 
25
25
  #mock ssl
26
- it "sets the STRICT-TRANSPORT-SECURITY header" do
26
+ it "sets the Strict-Transport-Security header" do
27
27
  request.env['HTTPS'] = 'on'
28
28
  get :index
29
29
  response.headers['Strict-Transport-Security'].should == SecureHeaders::StrictTransportSecurity::Constants::DEFAULT_VALUE
30
30
  end
31
31
 
32
+ it "sets the X-Content-Type-Options header" do
33
+ get :index
34
+ response.headers['X-Content-Type-Options'].should == SecureHeaders::XContentTypeOptions::Constants::DEFAULT_VALUE
35
+ end
36
+
32
37
  context "using IE" do
33
- it "sets the X-CONTENT-TYPE-OPTIONS header" do
38
+ it "sets the X-Content-Type-Options header" do
34
39
  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"
35
40
  get :index
36
- response.headers['X-Content-Type-Options'].should == "nosniff"
41
+ response.headers['X-Content-Type-Options'].should == SecureHeaders::XContentTypeOptions::Constants::DEFAULT_VALUE
37
42
  end
38
43
  end
39
44
  end
@@ -11,14 +11,14 @@ describe ThingsController do
11
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
12
  end
13
13
 
14
- it "sets the X-XSS-PROTECTION header" do
14
+ it "sets the X-XSS-Protection header" do
15
15
  get :index
16
16
  response.headers['X-XSS-Protection'].should == SecureHeaders::XXssProtection::Constants::DEFAULT_VALUE
17
17
  end
18
18
 
19
- it "sets the X-FRAME-OPTIONS header" do
19
+ it "sets the X-Frame-Options header" do
20
20
  get :index
21
- response.headers['X-FRAME-OPTIONS'].should == SecureHeaders::XFrameOptions::Constants::DEFAULT_VALUE
21
+ response.headers['X-Frame-Options'].should == SecureHeaders::XFrameOptions::Constants::DEFAULT_VALUE
22
22
  end
23
23
 
24
24
  it "sets the X-WebKit-CSP header" do
@@ -27,17 +27,22 @@ describe ThingsController do
27
27
  end
28
28
 
29
29
  #mock ssl
30
- it "sets the STRICT-TRANSPORT-SECURITY header" do
30
+ it "sets the Strict-Transport-Security header" do
31
31
  request.env['HTTPS'] = 'on'
32
32
  get :index
33
33
  response.headers['Strict-Transport-Security'].should == SecureHeaders::StrictTransportSecurity::Constants::DEFAULT_VALUE
34
34
  end
35
35
 
36
+ it "sets the X-Content-Type-Options header" do
37
+ get :index
38
+ response.headers['X-Content-Type-Options'].should == SecureHeaders::XContentTypeOptions::Constants::DEFAULT_VALUE
39
+ end
40
+
36
41
  context "using IE" do
37
- it "sets the X-CONTENT-TYPE-OPTIONS header" do
42
+ it "sets the X-Content-Type-Options header" do
38
43
  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"
39
44
  get :index
40
- response.headers['X-Content-Type-Options'].should == "nosniff"
45
+ response.headers['X-Content-Type-Options'].should == SecureHeaders::XContentTypeOptions::Constants::DEFAULT_VALUE
41
46
  end
42
47
  end
43
48
  end
@@ -36,6 +36,7 @@ module SecureHeaders
36
36
  # :report used to determine what :ssl_request, :ua, and :request_uri are set to
37
37
  def initialize(config=nil, options={})
38
38
  @experimental = !!options.delete(:experimental)
39
+ @controller = options.delete(:controller)
39
40
  if options[:request]
40
41
  parse_request(options[:request])
41
42
  else
@@ -65,6 +66,7 @@ module SecureHeaders
65
66
  end
66
67
 
67
68
  @report_uri = @config.delete(:report_uri)
69
+ @script_nonce = @config.delete(:script_nonce)
68
70
 
69
71
  normalize_csp_options
70
72
  normalize_reporting_endpoint
@@ -103,7 +105,8 @@ module SecureHeaders
103
105
  header_value = [
104
106
  build_impl_specific_directives,
105
107
  generic_directives(@config),
106
- report_uri_directive
108
+ report_uri_directive,
109
+ script_nonce_directive,
107
110
  ].join
108
111
 
109
112
  #store the value for next time
@@ -208,6 +211,18 @@ module SecureHeaders
208
211
  "report-uri #{@report_uri};"
209
212
  end
210
213
 
214
+ def script_nonce_directive
215
+ return '' if @script_nonce.nil?
216
+ nonce_value = if @script_nonce.is_a?(String)
217
+ @script_nonce
218
+ elsif @controller
219
+ @controller.instance_exec(&@script_nonce)
220
+ else
221
+ @script_nonce.call
222
+ end
223
+ "script-nonce #{nonce_value};"
224
+ end
225
+
211
226
  def generic_directives(config)
212
227
  header_value = ''
213
228
  if config[:img_src]
@@ -29,7 +29,7 @@ module SecureHeaders
29
29
  end
30
30
 
31
31
  max_age = @config.fetch(:max_age, HSTS_MAX_AGE)
32
- value = "max-age=" + max_age
32
+ value = "max-age=" + max_age.to_s
33
33
  value += "; includeSubdomains" if @config[:include_subdomains]
34
34
 
35
35
  value
@@ -41,7 +41,7 @@ module SecureHeaders
41
41
  if @config.is_a? Hash
42
42
  if !@config[:max_age]
43
43
  raise STSBuildError.new("No max-age was supplied.")
44
- elsif @config[:max_age] !~ /\A\d+\z/
44
+ elsif @config[:max_age].to_s !~ /\A\d+\z/
45
45
  raise STSBuildError.new("max-age must be a number. #{@config[:max_age]} was supplied.")
46
46
  end
47
47
  else
@@ -2,9 +2,9 @@ module SecureHeaders
2
2
  class XFOBuildError < StandardError; end
3
3
  class XFrameOptions
4
4
  module Constants
5
- XFO_HEADER_NAME = "X-FRAME-OPTIONS"
5
+ XFO_HEADER_NAME = "X-Frame-Options"
6
6
  DEFAULT_VALUE = 'SAMEORIGIN'
7
- VALID_XFO_HEADER = /\A(SAMEORIGIN\z|DENY\z|ALLOW-FROM:)/i
7
+ VALID_XFO_HEADER = /\A(SAMEORIGIN\z|DENY\z|ALLOW-FROM[:\s])/i
8
8
  end
9
9
  include Constants
10
10
 
@@ -37,4 +37,4 @@ module SecureHeaders
37
37
  end
38
38
  end
39
39
  end
40
- end
40
+ end
@@ -1,6 +1,5 @@
1
1
  module SecureHeaders
2
2
  class XXssProtectionBuildError < StandardError; end
3
- # IE only
4
3
  class XXssProtection
5
4
  module Constants
6
5
  X_XSS_PROTECTION_HEADER_NAME = 'X-XSS-Protection'
@@ -51,4 +50,4 @@ module SecureHeaders
51
50
  end
52
51
  end
53
52
  end
54
- end
53
+ end
@@ -1,3 +1,3 @@
1
1
  module SecureHeaders
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -72,10 +72,10 @@ module SecureHeaders
72
72
  options = self.class.options_for :csp, options
73
73
  return if options == false
74
74
 
75
- header = ContentSecurityPolicy.new(options, :request => request)
75
+ header = ContentSecurityPolicy.new(options, :request => request, :controller => self)
76
76
  set_header(header.name, header.value)
77
77
  if options && options[:experimental] && options[:enforce]
78
- header = ContentSecurityPolicy.new(options, :experimental => true, :request => request)
78
+ header = ContentSecurityPolicy.new(options, :experimental => true, :request => request, :controller => self)
79
79
  set_header(header.name, header.value)
80
80
  end
81
81
  end
@@ -85,7 +85,7 @@ module SecureHeaders
85
85
  end
86
86
 
87
87
  def set_x_content_type_options_header(options=self.class.secure_headers_options[:x_content_type_options])
88
- return unless brwsr.ie?
88
+ return unless brwsr.ie? || brwsr.chrome?
89
89
  set_a_header(:x_content_type_options, XContentTypeOptions, options)
90
90
  end
91
91
 
@@ -113,8 +113,7 @@ module SecureHeaders
113
113
  end
114
114
 
115
115
  def broken_implementation?(browser)
116
- #IOS 5 sometimes refuses to load external resources even when whitelisted with CSP
117
- return browser.ios5?
116
+ return browser.ios5? || (browser.safari? && browser.version == 5)
118
117
  end
119
118
  end
120
119
  end
@@ -397,6 +397,38 @@ module SecureHeaders
397
397
  end
398
398
  end
399
399
  end
400
+
401
+ context "when supplying a script nonce callback" do
402
+ let(:options) {
403
+ default_opts.merge({
404
+ :script_nonce => "random",
405
+ })
406
+ }
407
+
408
+ it "uses the value in the X-Webkit-CSP" do
409
+ csp = ContentSecurityPolicy.new(options, :request => request_for(CHROME))
410
+ csp.value.should match "script-nonce random;"
411
+ end
412
+
413
+ it "uses the value in the X-Content-Security-Policy" do
414
+ csp = ContentSecurityPolicy.new(options, :request => request_for(FIREFOX))
415
+ csp.value.should match "script-nonce random;"
416
+ end
417
+
418
+ it "runs a dynamic nonce generator" do
419
+ options[:script_nonce] = lambda { 'something' }
420
+ csp = ContentSecurityPolicy.new(options, :request => request_for(CHROME))
421
+ csp.value.should match "script-nonce something;"
422
+ end
423
+
424
+ it "runs against the given controller context" do
425
+ fake_params = {}
426
+ options[:script_nonce] = lambda { params[:script_nonce] = 'something' }
427
+ csp = ContentSecurityPolicy.new(options, :request => request_for(CHROME), :controller => double(:params => fake_params))
428
+ csp.value.should match "script-nonce something;"
429
+ fake_params.should == {:script_nonce => 'something'}
430
+ end
431
+ end
400
432
  end
401
433
  end
402
434
  end
@@ -5,29 +5,26 @@ module SecureHeaders
5
5
  specify{ StrictTransportSecurity.new.name.should == "Strict-Transport-Security" }
6
6
 
7
7
  describe "#value" do
8
- it "sets Strict Transport Security headers" do
9
- s = StrictTransportSecurity.new
10
- s.value.should == StrictTransportSecurity::Constants::DEFAULT_VALUE
11
- end
12
-
13
- it "allows you to specify includeSubdomains" do
14
- s = StrictTransportSecurity.new(:max_age => HSTS_MAX_AGE, :include_subdomains => true)
15
- s.value.should == "max-age=#{HSTS_MAX_AGE}; includeSubdomains"
16
- end
17
-
18
- it "accepts a string value and returns verbatim" do
19
- s = StrictTransportSecurity.new('max-age=1234')
20
- s.value.should == "max-age=1234"
21
- end
22
-
23
- it "allows you to specify max-age" do
24
- age = '8675309'
25
- s = StrictTransportSecurity.new(:max_age => age)
26
- s.value.should == "max-age=#{age}"
27
- end
8
+ specify { StrictTransportSecurity.new.value.should == StrictTransportSecurity::Constants::DEFAULT_VALUE}
9
+ specify { StrictTransportSecurity.new("max-age=1234").value.should == "max-age=1234"}
10
+ specify { StrictTransportSecurity.new(:max_age => '1234').value.should == "max-age=1234"}
11
+ specify { StrictTransportSecurity.new(:max_age => 1234).value.should == "max-age=1234"}
12
+ specify { StrictTransportSecurity.new(:max_age => HSTS_MAX_AGE, :include_subdomains => true).value.should == "max-age=#{HSTS_MAX_AGE}; includeSubdomains"}
28
13
 
29
14
  context "with an invalid configuration" do
30
15
  context "with a hash argument" do
16
+ it "should allow string values for max-age" do
17
+ lambda {
18
+ StrictTransportSecurity.new(:max_age => '1234')
19
+ }.should_not raise_error
20
+ end
21
+
22
+ it "should allow integer values for max-age" do
23
+ lambda {
24
+ StrictTransportSecurity.new(:max_age => 1234)
25
+ }.should_not raise_error
26
+ end
27
+
31
28
  it "raises an exception with an invalid max-age" do
32
29
  lambda {
33
30
  StrictTransportSecurity.new(:max_age => 'abc123')
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  module SecureHeaders
4
4
  describe XFrameOptions do
5
- specify{ XFrameOptions.new.name.should == "X-FRAME-OPTIONS" }
5
+ specify{ XFrameOptions.new.name.should == "X-Frame-Options" }
6
6
 
7
7
  describe "#value" do
8
8
  specify { XFrameOptions.new.value.should == XFrameOptions::Constants::DEFAULT_VALUE}
@@ -24,7 +24,10 @@ describe SecureHeaders do
24
24
  :chrome => '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',
25
25
  :ie => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/5.0)',
26
26
  :opera => 'Opera/9.80 (Windows NT 6.1; U; es-ES) Presto/2.9.181 Version/12.00',
27
- :ios5 => "Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3"
27
+ :ios5 => "Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3",
28
+ :ios6 => "Mozilla/5.0 (iPhone; CPU iPhone OS 614 like Mac OS X) AppleWebKit/536.26 (KHTML like Gecko) Version/6.0 Mobile/10B350 Safari/8536.25",
29
+ :safari5 => "Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko ) Version/5.1 Mobile/9B176 Safari/7534.48.3",
30
+ :safari6 => "Mozilla/5.0 (Macintosh; Intel Mac OS X 1084) AppleWebKit/536.30.1 (KHTML like Gecko) Version/6.0.5 Safari/536.30.1"
28
31
  }
29
32
 
30
33
  def should_assign_header name, value
@@ -77,12 +80,10 @@ describe SecureHeaders do
77
80
  it "sets all default headers for #{name} (smoke test)" do
78
81
  stub_user_agent(useragent)
79
82
  number_of_headers = case name
80
- when :ie
83
+ when :ie, :chrome
81
84
  5
82
- when :opera
83
- 4
84
- when :ios5
85
- 3 # csp is disabled for ios5
85
+ when :ios5, :safari5
86
+ 3 # csp breaks these browsers
86
87
  else
87
88
  4
88
89
  end
@@ -96,29 +97,28 @@ describe SecureHeaders do
96
97
  end
97
98
  end
98
99
 
99
- it "does not set the X-Content-Type-Options when disabled" do
100
+ it "does not set the X-Content-Type-Options header if disabled" do
100
101
  stub_user_agent(USER_AGENTS[:ie])
101
102
  should_not_assign_header(X_CONTENT_TYPE_OPTIONS_HEADER_NAME)
102
103
  subject.set_x_content_type_options_header(false)
103
104
  end
104
105
 
105
- it "does not set the X-XSS-PROTECTION when disabled" do
106
- stub_user_agent(USER_AGENTS[:ie])
106
+ it "does not set the X-XSS-Protection header if disabled" do
107
107
  should_not_assign_header(X_XSS_PROTECTION_HEADER_NAME)
108
108
  subject.set_x_xss_protection_header(false)
109
109
  end
110
110
 
111
- it "does not set the X-FRAME-OPTIONS header if disabled" do
111
+ it "does not set the X-Frame-Options header if disabled" do
112
112
  should_not_assign_header(XFO_HEADER_NAME)
113
113
  subject.set_x_frame_options_header(false)
114
114
  end
115
115
 
116
- it "does not set the hsts header if disabled" do
116
+ it "does not set the HSTS header if disabled" do
117
117
  should_not_assign_header(HSTS_HEADER_NAME)
118
118
  subject.set_hsts_header(false)
119
119
  end
120
120
 
121
- it "does not set the hsts header the request is over HTTP" do
121
+ it "does not set the HSTS header if request is over HTTP" do
122
122
  subject.stub_chain(:request, :ssl?).and_return(false)
123
123
  should_not_assign_header(HSTS_HEADER_NAME)
124
124
  subject.set_hsts_header({:include_subdomains => true})
@@ -139,7 +139,7 @@ describe SecureHeaders do
139
139
  end
140
140
 
141
141
  context "when disabled by configuration settings" do
142
- it "does not set the X-Content-Type-Options when disabled" do
142
+ it "does not set any headers when disabled" do
143
143
  ::SecureHeaders::Configuration.configure do |config|
144
144
  config.hsts = false
145
145
  config.x_frame_options = false
@@ -155,48 +155,67 @@ describe SecureHeaders do
155
155
  end
156
156
 
157
157
  describe "#set_x_frame_options_header" do
158
- it "sets the X-FRAME-OPTIONS header" do
158
+ it "sets the X-Frame-Options header" do
159
159
  should_assign_header(XFO_HEADER_NAME, SecureHeaders::XFrameOptions::Constants::DEFAULT_VALUE)
160
160
  subject.set_x_frame_options_header
161
161
  end
162
162
 
163
- it "allows a custom X-FRAME-OPTIONS header" do
163
+ it "allows a custom X-Frame-Options header" do
164
164
  should_assign_header(XFO_HEADER_NAME, "DENY")
165
165
  subject.set_x_frame_options_header(:value => 'DENY')
166
166
  end
167
167
  end
168
168
 
169
- context "when using IE" do
170
- before(:each) do
171
- stub_user_agent(USER_AGENTS[:ie])
169
+ describe "#set_strict_transport_security" do
170
+ it "sets the Strict-Transport-Security header" do
171
+ should_assign_header(HSTS_HEADER_NAME, SecureHeaders::StrictTransportSecurity::Constants::DEFAULT_VALUE)
172
+ subject.set_hsts_header
172
173
  end
173
174
 
174
- describe "#set_x_xss_protection" do
175
- it "sets the XSS protection header" do
176
- should_assign_header(X_XSS_PROTECTION_HEADER_NAME, '1')
177
- subject.set_x_xss_protection_header
178
- end
175
+ it "allows you to specific a custom max-age value" do
176
+ should_assign_header(HSTS_HEADER_NAME, 'max-age=1234')
177
+ subject.set_hsts_header(:max_age => 1234)
178
+ end
179
179
 
180
- it "sets a custom X-XSS-PROTECTION header" do
181
- should_assign_header(X_XSS_PROTECTION_HEADER_NAME, '0')
182
- subject.set_x_xss_protection_header("0")
183
- end
180
+ it "allows you to specify includeSubdomains" do
181
+ should_assign_header(HSTS_HEADER_NAME, "max-age=#{HSTS_MAX_AGE}; includeSubdomains")
182
+ subject.set_hsts_header(:max_age => HSTS_MAX_AGE, :include_subdomains => true)
183
+ end
184
+ end
184
185
 
185
- it "sets the block flag" do
186
- should_assign_header(X_XSS_PROTECTION_HEADER_NAME, '1; mode=block')
187
- subject.set_x_xss_protection_header(:mode => 'block', :value => 1)
188
- end
186
+ describe "#set_x_xss_protection" do
187
+ it "sets the X-XSS-Protection header" do
188
+ should_assign_header(X_XSS_PROTECTION_HEADER_NAME, SecureHeaders::XXssProtection::Constants::DEFAULT_VALUE)
189
+ subject.set_x_xss_protection_header
189
190
  end
190
191
 
191
- describe "#set_x_content_type_options" do
192
- it "sets the X-Content-Type-Options" do
193
- should_assign_header(X_CONTENT_TYPE_OPTIONS_HEADER_NAME, 'nosniff')
194
- subject.set_x_content_type_options_header
195
- end
192
+ it "sets a custom X-XSS-Protection header" do
193
+ should_assign_header(X_XSS_PROTECTION_HEADER_NAME, '0')
194
+ subject.set_x_xss_protection_header("0")
195
+ end
196
+
197
+ it "sets the block flag" do
198
+ should_assign_header(X_XSS_PROTECTION_HEADER_NAME, '1; mode=block')
199
+ subject.set_x_xss_protection_header(:mode => 'block', :value => 1)
200
+ end
201
+ end
196
202
 
197
- it "lets you override X-Content-Type-Options" do
198
- should_assign_header(X_CONTENT_TYPE_OPTIONS_HEADER_NAME, 'nosniff')
199
- subject.set_x_content_type_options_header(:value => 'nosniff')
203
+ describe "#set_x_content_type_options" do
204
+ [:ie, :chrome].each do |useragent|
205
+ context "when using #{useragent}" do
206
+ before(:each) do
207
+ stub_user_agent(USER_AGENTS[useragent])
208
+ end
209
+
210
+ it "sets the X-Content-Type-Options header" do
211
+ should_assign_header(X_CONTENT_TYPE_OPTIONS_HEADER_NAME, SecureHeaders::XContentTypeOptions::Constants::DEFAULT_VALUE)
212
+ subject.set_x_content_type_options_header
213
+ end
214
+
215
+ it "lets you override X-Content-Type-Options" do
216
+ should_assign_header(X_CONTENT_TYPE_OPTIONS_HEADER_NAME, 'nosniff')
217
+ subject.set_x_content_type_options_header(:value => 'nosniff')
218
+ end
200
219
  end
201
220
  end
202
221
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: secure_headers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-08 00:00:00.000000000 Z
12
+ date: 2013-05-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: brwsr
@@ -190,7 +190,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
190
190
  version: '0'
191
191
  requirements: []
192
192
  rubyforge_project:
193
- rubygems_version: 1.8.23
193
+ rubygems_version: 1.8.25
194
194
  signing_key:
195
195
  specification_version: 3
196
196
  summary: Add easily configured browser headers to responses including content security