rack 1.5.3 → 1.5.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.rdoc +2 -0
- data/lib/rack.rb +1 -1
- data/lib/rack/multipart/parser.rb +8 -0
- data/lib/rack/utils.rb +17 -4
- data/rack.gemspec +1 -1
- data/test/spec_multipart.rb +23 -16
- data/test/spec_request.rb +17 -0
- data/test/spec_utils.rb +12 -0
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d55818e96ace1f944f520fbe49b80f007f6c32a8
|
4
|
+
data.tar.gz: 26648f0efcd058816df3d6eb69c126d33fab4067
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 54ec13d366e64034c71a6ea552b6a92d06c67c5737a7d7bcad36b24517ed9bf86e4a44895208ae268de2f973882972b6299bf73e55427a9145751868bab27b97
|
7
|
+
data.tar.gz: 4a19a800a7315faa9d809520cbaa94dd8ae5d3c9d7e6e2b082a9de88f24071a43e37974204cd653192964e1370fd0647ef85d17d61592ed2e8ff8235d611d509
|
data/README.rdoc
CHANGED
@@ -528,6 +528,8 @@ run on port 11211) and memcache-client installed.
|
|
528
528
|
* Fix a race condition that could result in overwritten pidfiles
|
529
529
|
* Various documentation additions
|
530
530
|
|
531
|
+
* Prevent extremely deep parameters from being parsed. CVE-2015-3225
|
532
|
+
|
531
533
|
== Contact
|
532
534
|
|
533
535
|
Please post bugs, suggestions and patches to
|
data/lib/rack.rb
CHANGED
@@ -2,6 +2,8 @@ require 'rack/utils'
|
|
2
2
|
|
3
3
|
module Rack
|
4
4
|
module Multipart
|
5
|
+
class MultipartLimitError < Errno::EMFILE; end
|
6
|
+
|
5
7
|
class Parser
|
6
8
|
BUFSIZE = 16384
|
7
9
|
|
@@ -14,7 +16,13 @@ module Rack
|
|
14
16
|
|
15
17
|
fast_forward_to_first_boundary
|
16
18
|
|
19
|
+
opened_files = 0
|
17
20
|
loop do
|
21
|
+
if Utils.multipart_part_limit > 0
|
22
|
+
raise MultipartLimitError, 'Maximum file multiparts in content reached' if opened_files >= Utils.multipart_part_limit
|
23
|
+
opened_files += 1
|
24
|
+
end
|
25
|
+
|
18
26
|
head, filename, content_type, name, body =
|
19
27
|
get_current_head_and_filename_and_content_type_and_name_and_body
|
20
28
|
|
data/lib/rack/utils.rb
CHANGED
@@ -52,12 +52,23 @@ module Rack
|
|
52
52
|
|
53
53
|
class << self
|
54
54
|
attr_accessor :key_space_limit
|
55
|
+
attr_accessor :param_depth_limit
|
56
|
+
attr_accessor :multipart_part_limit
|
55
57
|
end
|
56
58
|
|
57
59
|
# The default number of bytes to allow parameter keys to take up.
|
58
60
|
# This helps prevent a rogue client from flooding a Request.
|
59
61
|
self.key_space_limit = 65536
|
60
62
|
|
63
|
+
# Default depth at which the parameter parser will raise an exception for
|
64
|
+
# being too deep. This helps prevent SystemStackErrors
|
65
|
+
self.param_depth_limit = 100
|
66
|
+
#
|
67
|
+
# The maximum number of parts a request can contain. Accepting to many part
|
68
|
+
# can lead to the server running out of file handles.
|
69
|
+
# Set to `0` for no limit.
|
70
|
+
self.multipart_part_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || 128).to_i
|
71
|
+
|
61
72
|
# Stolen from Mongrel, with some small modifications:
|
62
73
|
# Parses a query string by breaking it up at the '&'
|
63
74
|
# and ';' characters. You can also use this to parse
|
@@ -100,7 +111,9 @@ module Rack
|
|
100
111
|
end
|
101
112
|
module_function :parse_nested_query
|
102
113
|
|
103
|
-
def normalize_params(params, name, v = nil)
|
114
|
+
def normalize_params(params, name, v = nil, depth = Utils.param_depth_limit)
|
115
|
+
raise RangeError if depth <= 0
|
116
|
+
|
104
117
|
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
|
105
118
|
k = $1 || ''
|
106
119
|
after = $' || ''
|
@@ -118,14 +131,14 @@ module Rack
|
|
118
131
|
params[k] ||= []
|
119
132
|
raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
120
133
|
if params_hash_type?(params[k].last) && !params[k].last.key?(child_key)
|
121
|
-
normalize_params(params[k].last, child_key, v)
|
134
|
+
normalize_params(params[k].last, child_key, v, depth - 1)
|
122
135
|
else
|
123
|
-
params[k] << normalize_params(params.class.new, child_key, v)
|
136
|
+
params[k] << normalize_params(params.class.new, child_key, v, depth - 1)
|
124
137
|
end
|
125
138
|
else
|
126
139
|
params[k] ||= params.class.new
|
127
140
|
raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params_hash_type?(params[k])
|
128
|
-
params[k] = normalize_params(params[k], after, v)
|
141
|
+
params[k] = normalize_params(params[k], after, v, depth - 1)
|
129
142
|
end
|
130
143
|
|
131
144
|
return params
|
data/rack.gemspec
CHANGED
data/test/spec_multipart.rb
CHANGED
@@ -364,22 +364,29 @@ Content-Type: image/jpeg\r
|
|
364
364
|
end
|
365
365
|
|
366
366
|
it "builds complete params with the chunk size of 16384 slicing exactly on boundary" do
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
367
|
+
begin
|
368
|
+
previous_limit = Rack::Utils.multipart_part_limit
|
369
|
+
Rack::Utils.multipart_part_limit = 256
|
370
|
+
|
371
|
+
data = File.open(multipart_file("fail_16384_nofile"), 'rb') { |f| f.read }.gsub(/\n/, "\r\n")
|
372
|
+
options = {
|
373
|
+
"CONTENT_TYPE" => "multipart/form-data; boundary=----WebKitFormBoundaryWsY0GnpbI5U7ztzo",
|
374
|
+
"CONTENT_LENGTH" => data.length.to_s,
|
375
|
+
:input => StringIO.new(data)
|
376
|
+
}
|
377
|
+
env = Rack::MockRequest.env_for("/", options)
|
378
|
+
params = Rack::Multipart.parse_multipart(env)
|
379
|
+
|
380
|
+
params.should.not.equal nil
|
381
|
+
params.keys.should.include "AAAAAAAAAAAAAAAAAAA"
|
382
|
+
params["AAAAAAAAAAAAAAAAAAA"].keys.should.include "PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"
|
383
|
+
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"].keys.should.include "new"
|
384
|
+
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"].keys.should.include "-2"
|
385
|
+
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"].keys.should.include "ba_unit_id"
|
386
|
+
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"]["ba_unit_id"].should.equal "1017"
|
387
|
+
ensure
|
388
|
+
Rack::Utils.multipart_part_limit = previous_limit
|
389
|
+
end
|
383
390
|
end
|
384
391
|
|
385
392
|
should "return nil if no UploadedFiles were used" do
|
data/test/spec_request.rb
CHANGED
@@ -2,6 +2,7 @@ require 'stringio'
|
|
2
2
|
require 'cgi'
|
3
3
|
require 'rack/request'
|
4
4
|
require 'rack/mock'
|
5
|
+
require 'securerandom'
|
5
6
|
|
6
7
|
describe Rack::Request do
|
7
8
|
should "wrap the rack variables" do
|
@@ -718,6 +719,22 @@ EOF
|
|
718
719
|
f[:tempfile].size.should.equal 76
|
719
720
|
end
|
720
721
|
|
722
|
+
should "MultipartLimitError when request has too many multipart parts if limit set" do
|
723
|
+
begin
|
724
|
+
data = 10000.times.map { "--AaB03x\r\nContent-Type: text/plain\r\nContent-Disposition: attachment; name=#{SecureRandom.hex(10)}; filename=#{SecureRandom.hex(10)}\r\n\r\ncontents\r\n" }.join("\r\n")
|
725
|
+
data += "--AaB03x--\r"
|
726
|
+
|
727
|
+
options = {
|
728
|
+
"CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
|
729
|
+
"CONTENT_LENGTH" => data.length.to_s,
|
730
|
+
:input => StringIO.new(data)
|
731
|
+
}
|
732
|
+
|
733
|
+
request = Rack::Request.new Rack::MockRequest.env_for("/", options)
|
734
|
+
lambda { request.POST }.should.raise(Rack::Multipart::MultipartLimitError)
|
735
|
+
end
|
736
|
+
end
|
737
|
+
|
721
738
|
should "parse big multipart form data" do
|
722
739
|
input = <<EOF
|
723
740
|
--AaB03x\r
|
data/test/spec_utils.rb
CHANGED
@@ -123,6 +123,18 @@ describe Rack::Utils do
|
|
123
123
|
Rack::Utils.parse_query(",foo=bar;,", ";,").should.equal "foo" => "bar"
|
124
124
|
end
|
125
125
|
|
126
|
+
should "raise an exception if the params are too deep" do
|
127
|
+
len = Rack::Utils.param_depth_limit
|
128
|
+
|
129
|
+
lambda {
|
130
|
+
Rack::Utils.parse_nested_query("foo#{"[a]" * len}=bar")
|
131
|
+
}.should.raise(RangeError)
|
132
|
+
|
133
|
+
lambda {
|
134
|
+
Rack::Utils.parse_nested_query("foo#{"[a]" * (len - 1)}=bar")
|
135
|
+
}.should.not.raise
|
136
|
+
end
|
137
|
+
|
126
138
|
should "parse nested query strings correctly" do
|
127
139
|
Rack::Utils.parse_nested_query("foo").
|
128
140
|
should.equal "foo" => nil
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.5.
|
4
|
+
version: 1.5.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christian Neukirchen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-06-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bacon
|
@@ -247,7 +247,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
247
247
|
version: '0'
|
248
248
|
requirements: []
|
249
249
|
rubyforge_project: rack
|
250
|
-
rubygems_version: 2.4.
|
250
|
+
rubygems_version: 2.4.5
|
251
251
|
signing_key:
|
252
252
|
specification_version: 4
|
253
253
|
summary: a modular Ruby webserver interface
|
@@ -299,4 +299,3 @@ test_files:
|
|
299
299
|
- test/spec_urlmap.rb
|
300
300
|
- test/spec_utils.rb
|
301
301
|
- test/spec_webrick.rb
|
302
|
-
has_rdoc:
|