From fa15479e232663b2b5b048155b8e74228ab75d7e Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 20 Jan 2015 14:30:13 -0800 Subject: [PATCH] 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 561e46e..a163c49 100644 --- a/lib/rack/utils.rb +++ b/lib/rack/utils.rb @@ -52,12 +52,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 @@ -100,7 +105,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 = $' || '' @@ -118,14 +125,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 622b8ff..c1a2207 100644 --- a/test/spec_utils.rb +++ b/test/spec_utils.rb @@ -123,6 +123,18 @@ describe Rack::Utils do Rack::Utils.parse_query(",foo=bar;,", ";,").should.equal "foo" => "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 -- 2.2.1