From: Aaron Patterson <aaron.patterson () gmail com>
Date: Tue, 20 Jan 2015 14:30:13 -0800
Subject: raise an exception if the parameters are too deep

CVE-2015-3225

Conflicts:
	lib/rack/utils.rb
	test/spec_utils.rb
---
 lib/rack/utils.rb  | 15 +++++++++++----
 test/spec_utils.rb | 12 ++++++++++++
 2 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb
index 6576dd2..4656f4a 100644
--- a/lib/rack/utils.rb
+++ b/lib/rack/utils.rb
@@ -49,12 +49,17 @@ module Rack
 
     class << self
       attr_accessor :key_space_limit
+      attr_accessor :param_depth_limit
     end
 
     # The default number of bytes to allow parameter keys to take up.
     # 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
+
     # Stolen from Mongrel, with some small modifications:
     # Parses a query string by breaking it up at the '&'
     # and ';' characters.  You can also use this to parse
@@ -94,7 +99,9 @@ module Rack
     end
     module_function :parse_nested_query
 
-    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 = $' || ''
@@ -112,14 +119,14 @@ module Rack
         params[k] ||= []
         raise TypeError, "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 TypeError, "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/test/spec_utils.rb b/test/spec_utils.rb
index 69e3fbb..ac1e003 100644
--- a/test/spec_utils.rb
+++ b/test/spec_utils.rb
@@ -114,6 +114,18 @@ describe Rack::Utils do
     Rack::Utils.parse_query("foo%3Dbaz=bar").should.equal "foo=baz" => "bar"
   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
