librack-ruby update for CVE-2015-3225
Hi,
I've used the attached debdiff to fix CVE-2015-3225. Any review is
welcome!
I've put packages to test here[1] in case anybody wants to try them.
Besides verifying that the test suite still works with tests added to
detect the issue I've used the package on a puppetmaster for testing.
Cheers,
-- Guido
[1] http://honk.sigxcpu.org/projects/debian-lts/snapshots/
diff --git a/debian/README.Debian b/debian/README.Debian
new file mode 100644
index 0000000..a16803b
--- /dev/null
+++ b/debian/README.Debian
@@ -0,0 +1,5 @@
+To run the tests use e.g.:
+
+ RUBYLIB=lib rspec test/spec_rack_utils.rb
+
+with rspec + ruby-test-spec installed.
diff --git a/debian/changelog b/debian/changelog
index 643e6d1..a108214 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+librack-ruby (1.1.0-4+squeeze3) debian-lts; urgency=medium
+
+ * CVE-2015-3225: Avoid infinite depth param normalization
+ Patch based on
+ http://seclists.org/oss-sec/2015/q2/729
+
+ -- Guido Günther <agx@sigxcpu.org> Fri, 19 Jun 2015 17:52:46 +0200
+
librack-ruby (1.1.0-4+squeeze2) oldstable-security; urgency=high
* Team upload.
diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb
index 3fb1663..72100fd 100644
--- a/lib/rack/utils.rb
+++ b/lib/rack/utils.rb
@@ -30,12 +30,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
@@ -98,7 +103,8 @@ 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
if v and v =~ /^("|')(.*)\1$/
v = $2.gsub('\\'+$1, $1)
end
@@ -119,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[k].last.is_a?(Hash) && !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({}, child_key, v)
+ params[k] << normalize_params({}, child_key, v, depth - 1)
end
else
params[k] ||= {}
raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash)
- 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_rack_utils.rb b/test/spec_rack_utils.rb
index 98c848c..7304f16 100644
--- a/test/spec_rack_utils.rb
+++ b/test/spec_rack_utils.rb
@@ -43,6 +43,18 @@ context "Rack::Utils" do
Rack::Utils.parse_query("foo%3Dbaz=bar").should.equal "foo=baz" => "bar"
end
+ specify "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
+
specify "should parse nested query strings correctly" do
Rack::Utils.parse_nested_query("foo").
should.equal "foo" => nil
Reply to: