summary refs log tree commit
diff options
context:
space:
mode:
authorAaron Patterson <aaron.patterson@gmail.com>2015-06-16 11:00:29 -0700
committerAaron Patterson <aaron.patterson@gmail.com>2015-06-16 11:00:29 -0700
commit9baa7609ca408f25e8adbd34ca083dc87010da61 (patch)
tree0be3be42d506bf680d1df56017dd136c6a75f1c8
parentffea1210daf1268867c14836bf060a76cd70c6ca (diff)
parent90d7d2a8f7ab50cb80adcc05a7fcdd1dfa60f2ad (diff)
downloadrack-9baa7609ca408f25e8adbd34ca083dc87010da61.tar.gz
Merge branch '1-6-sec' into 1-6-stable
* 1-6-sec:
  update history
  bump to 1.6.2
  raise an exception if the parameters are too deep
-rw-r--r--HISTORY.md5
-rw-r--r--lib/rack.rb2
-rw-r--r--lib/rack/utils.rb15
-rw-r--r--rack.gemspec2
-rw-r--r--test/spec_utils.rb12
5 files changed, 29 insertions, 7 deletions
diff --git a/HISTORY.md b/HISTORY.md
index b78d8340..1b65179d 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,5 +1,8 @@
+Fri Jun 12 11:37:41 2015  Aaron Patterson <tenderlove@ruby-lang.org>
+
+        * Prevent extremely deep parameters from being parsed. CVE-2015-3225
+
 ### December 18th, Thirty sixth public release 1.6.0
-  - TODO
 
 ### February 7th, Thirty fifth public release 1.5.2
   - Fix CVE-2013-0263, timing attack against Rack::Session::Cookie
diff --git a/lib/rack.rb b/lib/rack.rb
index 4d49985c..a1fe34d1 100644
--- a/lib/rack.rb
+++ b/lib/rack.rb
@@ -20,7 +20,7 @@ module Rack
 
   # Return the Rack release as a dotted string.
   def self.release
-    "1.6.1"
+    "1.6.2"
   end
   PATH_INFO      = 'PATH_INFO'.freeze
   REQUEST_METHOD = 'REQUEST_METHOD'.freeze
diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb
index eea8c879..3b6f69f3 100644
--- a/lib/rack/utils.rb
+++ b/lib/rack/utils.rb
@@ -61,6 +61,7 @@ module Rack
 
     class << self
       attr_accessor :key_space_limit
+      attr_accessor :param_depth_limit
       attr_accessor :multipart_part_limit
     end
 
@@ -68,6 +69,10 @@ module Rack
     # This helps prevent a rogue client from flooding a Request.
     self.key_space_limit = 65536
 
+    # Default depth at which the parameter parser will raise an exception for
+    # being too deep.  This helps prevent SystemStackErrors
+    self.param_depth_limit = 100
+
     # The maximum number of parts a request can contain. Accepting too many part
     # can lead to the server running out of file handles.
     # Set to `0` for no limit.
@@ -126,7 +131,9 @@ module Rack
     # normalize_params recursively expands parameters into structural types. If
     # the structural types represented by two different parameter names are in
     # conflict, a ParameterTypeError is raised.
-    def normalize_params(params, name, v = nil)
+    def normalize_params(params, name, v = nil, depth = Utils.param_depth_limit)
+      raise RangeError if depth <= 0
+
       name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
       k = $1 || ''
       after = $' || ''
@@ -146,14 +153,14 @@ module Rack
         params[k] ||= []
         raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
         if params_hash_type?(params[k].last) && !params[k].last.key?(child_key)
-          normalize_params(params[k].last, child_key, v)
+          normalize_params(params[k].last, child_key, v, depth - 1)
         else
-          params[k] << normalize_params(params.class.new, child_key, v)
+          params[k] << normalize_params(params.class.new, child_key, v, depth - 1)
         end
       else
         params[k] ||= params.class.new
         raise ParameterTypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params_hash_type?(params[k])
-        params[k] = normalize_params(params[k], after, v)
+        params[k] = normalize_params(params[k], after, v, depth - 1)
       end
 
       return params
diff --git a/rack.gemspec b/rack.gemspec
index 32c6d797..c2554de1 100644
--- a/rack.gemspec
+++ b/rack.gemspec
@@ -1,6 +1,6 @@
 Gem::Specification.new do |s|
   s.name            = "rack"
-  s.version         = "1.6.1"
+  s.version         = "1.6.2"
   s.platform        = Gem::Platform::RUBY
   s.summary         = "a modular Ruby webserver interface"
   s.license         = "MIT"
diff --git a/test/spec_utils.rb b/test/spec_utils.rb
index 06ed5636..c2d479f9 100644
--- a/test/spec_utils.rb
+++ b/test/spec_utils.rb
@@ -134,6 +134,18 @@ describe Rack::Utils do
     }.should.not.raise
   end
 
+  should "raise an exception if the params are too deep" do
+    len = Rack::Utils.param_depth_limit
+
+    lambda {
+      Rack::Utils.parse_nested_query("foo#{"[a]" * len}=bar")
+    }.should.raise(RangeError)
+
+    lambda {
+      Rack::Utils.parse_nested_query("foo#{"[a]" * (len - 1)}=bar")
+    }.should.not.raise
+  end
+
   should "parse nested query strings correctly" do
     Rack::Utils.parse_nested_query("foo").
       should.equal "foo" => nil