-
-
Save evanphx/52bbc6b9cc19ce330829 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
commit b71a96c2e2d223f5a9228ee06461ab47db6109b0 | |
Author: Evan Phoenix <evan@fallingsnow.net> | |
Date: Tue Dec 13 10:18:48 2011 -0800 | |
Limit the size of parameter keys | |
diff --git a/lib/rack/multipart/parser.rb b/lib/rack/multipart/parser.rb | |
index 6eee64e..2b55cf9 100644 | |
--- a/lib/rack/multipart/parser.rb | |
+++ b/lib/rack/multipart/parser.rb | |
@@ -14,6 +14,9 @@ module Rack | |
fast_forward_to_first_boundary | |
+ max_key_space = Utils.key_space_limit | |
+ bytes = 0 | |
+ | |
loop do | |
head, filename, content_type, name, body = | |
get_current_head_and_filename_and_content_type_and_name_and_body | |
@@ -28,6 +31,13 @@ module Rack | |
filename, data = get_data(filename, body, content_type, name, head) | |
+ if name | |
+ bytes += name.size | |
+ if bytes > max_key_space | |
+ raise RangeError, "exceeded available parameter key space" | |
+ end | |
+ end | |
+ | |
Utils.normalize_params(@params, name, data) unless data.nil? | |
# break if we're at the end of a buffer, but not if it is the end of a field | |
diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb | |
index 45f4b75..e29f1c8 100644 | |
--- a/lib/rack/utils.rb | |
+++ b/lib/rack/utils.rb | |
@@ -47,6 +47,14 @@ module Rack | |
DEFAULT_SEP = /[&;] */n | |
+ class << self | |
+ attr_accessor :key_space_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 | |
+ | |
# 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 | |
@@ -55,8 +63,19 @@ module Rack | |
def parse_query(qs, d = nil) | |
params = {} | |
+ max_key_space = Utils.key_space_limit | |
+ bytes = 0 | |
+ | |
(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p| | |
k, v = p.split('=', 2).map { |x| unescape(x) } | |
+ | |
+ if k | |
+ bytes += k.size | |
+ if bytes > max_key_space | |
+ raise RangeError, "exceeded available parameter key space" | |
+ end | |
+ end | |
+ | |
if cur = params[k] | |
if cur.class == Array | |
params[k] << v | |
@@ -75,8 +94,19 @@ module Rack | |
def parse_nested_query(qs, d = nil) | |
params = {} | |
+ max_key_space = Utils.key_space_limit | |
+ bytes = 0 | |
+ | |
(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p| | |
k, v = p.split('=', 2).map { |s| unescape(s) } | |
+ | |
+ if k | |
+ bytes += k.size | |
+ if bytes > max_key_space | |
+ raise RangeError, "exceeded available parameter key space" | |
+ end | |
+ end | |
+ | |
normalize_params(params, k, v) | |
end | |
diff --git a/test/spec_multipart.rb b/test/spec_multipart.rb | |
index 4ecd2de..1dc2f4d 100644 | |
--- a/test/spec_multipart.rb | |
+++ b/test/spec_multipart.rb | |
@@ -30,6 +30,17 @@ describe Rack::Multipart do | |
params["text"].should.equal "contents" | |
end | |
+ should "raise RangeError if the key space is exhausted" do | |
+ env = Rack::MockRequest.env_for("/", multipart_fixture(:content_type_and_no_filename)) | |
+ | |
+ old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1 | |
+ begin | |
+ lambda { Rack::Multipart.parse_multipart(env) }.should.raise(RangeError) | |
+ ensure | |
+ Rack::Utils.key_space_limit = old | |
+ end | |
+ end | |
+ | |
should "parse multipart form webkit style" do | |
env = Rack::MockRequest.env_for '/', multipart_fixture(:webkit) | |
env['CONTENT_TYPE'] = "multipart/form-data; boundary=----WebKitFormBoundaryWLHCs9qmcJJoyjKR" | |
diff --git a/test/spec_request.rb b/test/spec_request.rb | |
index b7adfd7..6aa49f7 100644 | |
--- a/test/spec_request.rb | |
+++ b/test/spec_request.rb | |
@@ -125,6 +125,18 @@ describe Rack::Request do | |
req.params.should.equal "foo" => "bar", "quux" => "bla" | |
end | |
+ should "limit the keys from the GET query string" do | |
+ env = Rack::MockRequest.env_for("/?foo=bar") | |
+ | |
+ old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1 | |
+ begin | |
+ req = Rack::Request.new(env) | |
+ lambda { req.GET }.should.raise(RangeError) | |
+ ensure | |
+ Rack::Utils.key_space_limit = old | |
+ end | |
+ end | |
+ | |
should "not unify GET and POST when calling params" do | |
mr = Rack::MockRequest.env_for("/?foo=quux", | |
"REQUEST_METHOD" => 'POST', | |
@@ -157,6 +169,20 @@ describe Rack::Request do | |
req.params.should.equal "foo" => "bar", "quux" => "bla" | |
end | |
+ should "limit the keys from the POST form data" do | |
+ env = Rack::MockRequest.env_for("", | |
+ "REQUEST_METHOD" => 'POST', | |
+ :input => "foo=bar&quux=bla") | |
+ | |
+ old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1 | |
+ begin | |
+ req = Rack::Request.new(env) | |
+ lambda { req.POST }.should.raise(RangeError) | |
+ ensure | |
+ Rack::Utils.key_space_limit = old | |
+ end | |
+ end | |
+ | |
should "parse POST data with explicit content type regardless of method" do | |
req = Rack::Request.new \ | |
Rack::MockRequest.env_for("/", |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment