rack 2.0.9.2 → 2.0.9.3
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/HISTORY.md +4 -0
- data/README.rdoc +17 -3
- data/lib/rack/multipart/parser.rb +16 -4
- data/lib/rack/utils.rb +15 -4
- data/lib/rack.rb +1 -1
- data/test/spec_multipart.rb +12 -0
- data/test/spec_request.rb +17 -1
- metadata +44 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 60eb430d14b80b4cc729d7a97bdf1a56196fa854ff5bd0a23f4fd21ecabd92ec
|
4
|
+
data.tar.gz: 373f537c5b38c9cf1f307dcc8876ff52e4dc67235bfcd554a0f6e35c854ee4c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d8d221546080d35fe8e9e4eb0810b02d0996b912874c408a7a5d270d6e26453430f0f05108ba41d8de40088b882db1de9f1efcac4e6b7e63a01431b6e4bd5f09
|
7
|
+
data.tar.gz: f4ba080150f27b7d2ffb4babc9707c0e539a7464bb4377df192122d8484158d188be57474ba9b5af3f72eaebb7257977f2ad43ba7effe3068decccba84caa0ea
|
data/HISTORY.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
Thu Mar 2 14:50:46 2023 Aaron Patterson <tenderlove@ruby-lang.org>
|
2
|
+
|
3
|
+
* [CVE-2023-27530] Introduce multipart_total_part_limit to limit total parts
|
4
|
+
|
1
5
|
Tue Jan 17 12:27:04 2023 Aaron Patterson <tenderlove@ruby-lang.org>
|
2
6
|
|
3
7
|
* [CVE-2022-44571] Fix ReDoS vulnerability in multipart parser
|
data/README.rdoc
CHANGED
@@ -189,16 +189,30 @@ This helps prevent a rogue client from flooding a Request.
|
|
189
189
|
|
190
190
|
Default to 65536 characters (4 kiB in worst case).
|
191
191
|
|
192
|
-
===
|
192
|
+
=== multipart_file_limit
|
193
193
|
|
194
|
-
The maximum number of parts a request can contain.
|
194
|
+
The maximum number of parts with a filename a request can contain.
|
195
195
|
Accepting too many part can lead to the server running out of file handles.
|
196
196
|
|
197
197
|
The default is 128, which means that a single request can't upload more than 128 files at once.
|
198
198
|
|
199
199
|
Set to 0 for no limit.
|
200
200
|
|
201
|
-
Can also be set via the
|
201
|
+
Can also be set via the +RACK_MULTIPART_FILE_LIMIT+ environment variable.
|
202
|
+
|
203
|
+
(This is also aliased as +multipart_part_limit+ and +RACK_MULTIPART_PART_LIMIT+ for compatibility)
|
204
|
+
|
205
|
+
=== multipart_total_part_limit
|
206
|
+
|
207
|
+
The maximum total number of parts a request can contain of any type, including
|
208
|
+
both file and non-file form fields.
|
209
|
+
|
210
|
+
The default is 4096, which means that a single request can't contain more than
|
211
|
+
4096 parts.
|
212
|
+
|
213
|
+
Set to 0 for no limit.
|
214
|
+
|
215
|
+
Can also be set via the +RACK_MULTIPART_TOTAL_PART_LIMIT+ environment variable.
|
202
216
|
|
203
217
|
== History
|
204
218
|
|
@@ -3,6 +3,7 @@ require 'rack/utils'
|
|
3
3
|
module Rack
|
4
4
|
module Multipart
|
5
5
|
class MultipartPartLimitError < Errno::EMFILE; end
|
6
|
+
class MultipartTotalPartLimitError < StandardError; end
|
6
7
|
|
7
8
|
class Parser
|
8
9
|
BUFSIZE = 16384
|
@@ -138,7 +139,8 @@ module Rack
|
|
138
139
|
end
|
139
140
|
|
140
141
|
@mime_parts[mime_index] = klass.new(body, head, filename, content_type, name)
|
141
|
-
|
142
|
+
|
143
|
+
check_part_limits
|
142
144
|
end
|
143
145
|
|
144
146
|
def on_mime_body mime_index, content
|
@@ -150,13 +152,23 @@ module Rack
|
|
150
152
|
|
151
153
|
private
|
152
154
|
|
153
|
-
def
|
154
|
-
|
155
|
-
|
155
|
+
def check_part_limits
|
156
|
+
file_limit = Utils.multipart_file_limit
|
157
|
+
part_limit = Utils.multipart_total_part_limit
|
158
|
+
|
159
|
+
if file_limit && file_limit > 0
|
160
|
+
if @open_files >= file_limit
|
156
161
|
@mime_parts.each(&:close)
|
157
162
|
raise MultipartPartLimitError, 'Maximum file multiparts in content reached'
|
158
163
|
end
|
159
164
|
end
|
165
|
+
|
166
|
+
if part_limit && part_limit > 0
|
167
|
+
if @mime_parts.size >= part_limit
|
168
|
+
@mime_parts.each(&:close)
|
169
|
+
raise MultipartTotalPartLimitError, 'Maximum total multiparts in content reached'
|
170
|
+
end
|
171
|
+
end
|
160
172
|
end
|
161
173
|
end
|
162
174
|
|
data/lib/rack/utils.rb
CHANGED
@@ -53,13 +53,24 @@ module Rack
|
|
53
53
|
module_function :unescape
|
54
54
|
|
55
55
|
class << self
|
56
|
-
attr_accessor :
|
56
|
+
attr_accessor :multipart_total_part_limit
|
57
|
+
|
58
|
+
attr_accessor :multipart_file_limit
|
59
|
+
|
60
|
+
# multipart_part_limit is the original name of multipart_file_limit, but
|
61
|
+
# the limit only counts parts with filenames.
|
62
|
+
alias multipart_part_limit multipart_file_limit
|
63
|
+
alias multipart_part_limit= multipart_file_limit=
|
57
64
|
end
|
58
65
|
|
59
|
-
# The maximum number of parts a request can contain. Accepting too
|
60
|
-
# can lead to the server running out of file handles.
|
66
|
+
# The maximum number of file parts a request can contain. Accepting too
|
67
|
+
# many parts can lead to the server running out of file handles.
|
61
68
|
# Set to `0` for no limit.
|
62
|
-
self.
|
69
|
+
self.multipart_file_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || ENV['RACK_MULTIPART_FILE_LIMIT'] || 128).to_i
|
70
|
+
|
71
|
+
# The maximum total number of parts a request can contain. Accepting too
|
72
|
+
# many can lead to excessive memory use and parsing time.
|
73
|
+
self.multipart_total_part_limit = (ENV['RACK_MULTIPART_TOTAL_PART_LIMIT'] || 4096).to_i
|
63
74
|
|
64
75
|
def self.param_depth_limit
|
65
76
|
default_query_parser.param_depth_limit
|
data/lib/rack.rb
CHANGED
data/test/spec_multipart.rb
CHANGED
@@ -548,6 +548,18 @@ Content-Type: image/jpeg\r
|
|
548
548
|
end
|
549
549
|
end
|
550
550
|
|
551
|
+
it "reach a multipart total limit" do
|
552
|
+
begin
|
553
|
+
previous_limit = Rack::Utils.multipart_total_part_limit
|
554
|
+
Rack::Utils.multipart_total_part_limit = 5
|
555
|
+
|
556
|
+
env = Rack::MockRequest.env_for '/', multipart_fixture(:three_files_three_fields)
|
557
|
+
lambda { Rack::Multipart.parse_multipart(env) }.must_raise Rack::Multipart::MultipartTotalPartLimitError
|
558
|
+
ensure
|
559
|
+
Rack::Utils.multipart_total_part_limit = previous_limit
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
551
563
|
it "return nil if no UploadedFiles were used" do
|
552
564
|
data = Rack::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => "contents"}])
|
553
565
|
data.must_be_nil
|
data/test/spec_request.rb
CHANGED
@@ -911,7 +911,7 @@ EOF
|
|
911
911
|
f[:tempfile].size.must_equal 76
|
912
912
|
end
|
913
913
|
|
914
|
-
it "MultipartPartLimitError when request has too many multipart parts if limit set" do
|
914
|
+
it "MultipartPartLimitError when request has too many multipart file parts if limit set" do
|
915
915
|
begin
|
916
916
|
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")
|
917
917
|
data += "--AaB03x--\r"
|
@@ -927,6 +927,22 @@ EOF
|
|
927
927
|
end
|
928
928
|
end
|
929
929
|
|
930
|
+
it "MultipartPartLimitError when request has too many multipart total parts if limit set" do
|
931
|
+
begin
|
932
|
+
data = 10000.times.map { "--AaB03x\r\ncontent-type: text/plain\r\ncontent-disposition: attachment; name=#{SecureRandom.hex(10)}\r\n\r\ncontents\r\n" }.join("\r\n")
|
933
|
+
data += "--AaB03x--\r"
|
934
|
+
|
935
|
+
options = {
|
936
|
+
"CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
|
937
|
+
"CONTENT_LENGTH" => data.length.to_s,
|
938
|
+
:input => StringIO.new(data)
|
939
|
+
}
|
940
|
+
|
941
|
+
request = make_request Rack::MockRequest.env_for("/", options)
|
942
|
+
lambda { request.POST }.must_raise Rack::Multipart::MultipartTotalPartLimitError
|
943
|
+
end
|
944
|
+
end
|
945
|
+
|
930
946
|
it 'closes tempfiles it created in the case of too many created' do
|
931
947
|
begin
|
932
948
|
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")
|
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: 2.0.9.
|
4
|
+
version: 2.0.9.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Leah Neukirchen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-03-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -275,60 +275,60 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
275
275
|
- !ruby/object:Gem::Version
|
276
276
|
version: '0'
|
277
277
|
requirements: []
|
278
|
-
rubygems_version: 3.1
|
278
|
+
rubygems_version: 3.4.1
|
279
279
|
signing_key:
|
280
280
|
specification_version: 4
|
281
281
|
summary: a modular Ruby webserver interface
|
282
282
|
test_files:
|
283
|
-
- test/
|
283
|
+
- test/spec_auth_basic.rb
|
284
|
+
- test/spec_auth_digest.rb
|
285
|
+
- test/spec_body_proxy.rb
|
286
|
+
- test/spec_builder.rb
|
287
|
+
- test/spec_cascade.rb
|
288
|
+
- test/spec_cgi.rb
|
289
|
+
- test/spec_chunked.rb
|
290
|
+
- test/spec_common_logger.rb
|
291
|
+
- test/spec_conditional_get.rb
|
292
|
+
- test/spec_config.rb
|
293
|
+
- test/spec_content_length.rb
|
294
|
+
- test/spec_content_type.rb
|
284
295
|
- test/spec_deflater.rb
|
285
|
-
- test/
|
286
|
-
- test/spec_session_cookie.rb
|
287
|
-
- test/spec_session_pool.rb
|
296
|
+
- test/spec_directory.rb
|
288
297
|
- test/spec_etag.rb
|
289
|
-
- test/
|
298
|
+
- test/spec_events.rb
|
299
|
+
- test/spec_fastcgi.rb
|
300
|
+
- test/spec_file.rb
|
290
301
|
- test/spec_handler.rb
|
291
|
-
- test/
|
292
|
-
- test/
|
293
|
-
- test/
|
294
|
-
- test/
|
295
|
-
- test/
|
302
|
+
- test/spec_head.rb
|
303
|
+
- test/spec_lint.rb
|
304
|
+
- test/spec_lobster.rb
|
305
|
+
- test/spec_lock.rb
|
306
|
+
- test/spec_logger.rb
|
296
307
|
- test/spec_media_type.rb
|
297
|
-
- test/spec_cgi.rb
|
298
308
|
- test/spec_method_override.rb
|
299
|
-
- test/
|
300
|
-
- test/
|
309
|
+
- test/spec_mime.rb
|
310
|
+
- test/spec_mock.rb
|
311
|
+
- test/spec_multipart.rb
|
312
|
+
- test/spec_null_logger.rb
|
313
|
+
- test/spec_recursive.rb
|
301
314
|
- test/spec_request.rb
|
302
|
-
- test/
|
303
|
-
- test/
|
315
|
+
- test/spec_response.rb
|
316
|
+
- test/spec_rewindable_input.rb
|
304
317
|
- test/spec_runtime.rb
|
305
|
-
- test/spec_session_persisted_secure_secure_session_hash.rb
|
306
|
-
- test/spec_fastcgi.rb
|
307
|
-
- test/spec_common_logger.rb
|
308
|
-
- test/spec_builder.rb
|
309
|
-
- test/spec_config.rb
|
310
|
-
- test/spec_utils.rb
|
311
318
|
- test/spec_sendfile.rb
|
312
|
-
- test/spec_lobster.rb
|
313
|
-
- test/spec_lint.rb
|
314
|
-
- test/spec_conditional_get.rb
|
315
|
-
- test/spec_tempfile_reaper.rb
|
316
|
-
- test/spec_mock.rb
|
317
319
|
- test/spec_server.rb
|
318
|
-
- test/
|
319
|
-
- test/
|
320
|
-
- test/
|
321
|
-
- test/
|
320
|
+
- test/spec_session_abstract_id.rb
|
321
|
+
- test/spec_session_abstract_session_hash.rb
|
322
|
+
- test/spec_session_cookie.rb
|
323
|
+
- test/spec_session_memcache.rb
|
324
|
+
- test/spec_session_persisted_secure_secure_session_hash.rb
|
325
|
+
- test/spec_session_pool.rb
|
326
|
+
- test/spec_show_exceptions.rb
|
322
327
|
- test/spec_show_status.rb
|
323
|
-
- test/
|
324
|
-
- test/
|
325
|
-
- test/
|
328
|
+
- test/spec_static.rb
|
329
|
+
- test/spec_tempfile_reaper.rb
|
330
|
+
- test/spec_thin.rb
|
326
331
|
- test/spec_urlmap.rb
|
327
|
-
- test/
|
328
|
-
- test/
|
329
|
-
- test/
|
330
|
-
- test/spec_head.rb
|
331
|
-
- test/spec_lock.rb
|
332
|
-
- test/spec_rewindable_input.rb
|
333
|
-
- test/spec_session_memcache.rb
|
334
|
-
- test/spec_content_length.rb
|
332
|
+
- test/spec_utils.rb
|
333
|
+
- test/spec_version.rb
|
334
|
+
- test/spec_webrick.rb
|