rack 3.1.14 → 3.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 558fa2ba3e85c5e5c3775d72153132d6f667aa7390421fb6769b6cc26dd3bd72
4
- data.tar.gz: 135df43165c5e6fa3a69cd4857ee2e514cfe7841ed3728889e61185268e86531
3
+ metadata.gz: 14e5b5eb8c607ee4b9a8904f54237a1e09bd849e4c92f8654def11d9979d8e92
4
+ data.tar.gz: d2d6df191d236142e93d984bfd81a2c6cc7ec9904660a8974424d6c7f133fc49
5
5
  SHA512:
6
- metadata.gz: 3543b51083a592a6609ac760ee6b4a692b442496258470c3d70cd94ada81c22439c21e5637599e6d85fd0c25983fc0819883f2449466cfb3c41ed8154287b221
7
- data.tar.gz: a1da3c54d64e956c1b4b02065da3deaff9ef9e212e2c5366260691caef698bea993ceb9b26e0a26d72f37481fb0f0a91803fe99e864baffbbfa10e31ca4e79cc
6
+ metadata.gz: c69d56a89d655e1de73db0096ceb060c4c70da8d074988a233f2b3102cfe1b2e2ed0eb3e4cb08ddf83ed0179ee25e91d43f609088d4ad46c5cd469e27ed02421
7
+ data.tar.gz: 81de2ad1d0e1838791934f46ec3c144f4253c99e34d60276003fe382febcbdecb5c253553d5972521044342404e9805591a8223b3f3ba6ee8f027e33631e929c
data/CHANGELOG.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. For info on how to format all future additions to this file please reference [Keep A Changelog](https://keepachangelog.com/en/1.0.0/).
4
4
 
5
+ ## [3.1.17] - 2025-10-07
6
+
7
+ ### Security
8
+
9
+ - [CVE-2025-61772](https://github.com/advisories/GHSA-wpv5-97wm-hp9c) Multipart parser buffers unbounded per-part headers, enabling DoS (memory exhaustion)
10
+ - [CVE-2025-61771](https://github.com/advisories/GHSA-w9pc-fmgc-vxvw) Multipart parser buffers large non‑file fields entirely in memory, enabling DoS (memory exhaustion)
11
+ - [CVE-2025-61770](https://github.com/advisories/GHSA-p543-xpfm-54cp) Unbounded multipart preamble buffering enables DoS (memory exhaustion)
12
+
13
+ ## [3.1.16] - 2025-06-04
14
+
15
+ ### Security
16
+
17
+ - [CVE-2025-49007](https://github.com/advisories/GHSA-47m2-26rw-j2jw) Fix ReDoS in multipart request.
18
+
19
+ ## [3.1.15] - 2025-05-18
20
+
21
+ - Optional support for `CGI::Cookie` if not available. ([#2327](https://github.com/rack/rack/pull/2327), [#2333](https://github.com/rack/rack/pull/2333), [@earlopain])
22
+
5
23
  ## [3.1.14] - 2025-05-06
6
24
 
7
25
  ### Security
@@ -139,6 +157,14 @@ Rack v3.1 is primarily a maintenance release that removes features deprecated in
139
157
 
140
158
  - In `Rack::Files`, ignore the `Range` header if served file is 0 bytes. ([#2159](https://github.com/rack/rack/pull/2159), [@zarqman])
141
159
 
160
+ ## [3.0.18] - 2025-05-22
161
+
162
+ - Fix incorrect backport of optional `CGI::Cookie` support. ([#2335](https://github.com/rack/rack/pull/2335), [@jeremyevans])
163
+
164
+ ## [3.0.17] - 2025-05-18
165
+
166
+ - Optional support for `CGI::Cookie` if not available. ([#2327](https://github.com/rack/rack/pull/2327), [#2333](https://github.com/rack/rack/pull/2333), [@earlopain])
167
+
142
168
  ## [3.0.16] - 2025-05-06
143
169
 
144
170
  ### Security
@@ -161,6 +187,10 @@ Rack v3.1 is primarily a maintenance release that removes features deprecated in
161
187
 
162
188
  - [CVE-2025-27111](https://github.com/rack/rack/security/advisories/GHSA-8cgq-6mh2-7j6v) Possible Log Injection in `Rack::Sendfile`.
163
189
 
190
+ ### Fixed
191
+
192
+ - Remove autoloads for constants no longer shipped with Rack. ([#2269](https://github.com/rack/rack/pull/2269), [@ccutrer](https://github.com/ccutrer))
193
+
164
194
  ## [3.0.12] - 2025-02-12
165
195
 
166
196
  ### Security
@@ -295,7 +325,7 @@ Rack v3.1 is primarily a maintenance release that removes features deprecated in
295
325
  - Remove deprecated Rack::Request::SCHEME_WHITELIST. ([@jeremyevans])
296
326
  - Remove internal cookie deletion using pattern matching, there are very few practical cases where it would be useful and browsers handle it correctly without us doing anything special. ([#1844](https://github.com/rack/rack/pull/1844), [@ioquatix])
297
327
  - Remove `rack.version` as it comes too late to be useful. ([#1938](https://github.com/rack/rack/pull/1938), [@ioquatix])
298
- - Extract `rackup` command, `Rack::Server`, `Rack::Handler`, `Rack::Lobster` and related code into a separate gem. ([#1937](https://github.com/rack/rack/pull/1937), [@ioquatix])
328
+ - Extract `rackup` command, `Rack::Server`, `Rack::Handler` and related code into a separate gem. ([#1937](https://github.com/rack/rack/pull/1937), [@ioquatix])
299
329
 
300
330
  ### Added
301
331
 
@@ -343,6 +373,32 @@ Rack v3.1 is primarily a maintenance release that removes features deprecated in
343
373
  - Fix multipart filename generation for filenames that contain spaces. Encode spaces as "%20" instead of "+" which will be decoded properly by the multipart parser. ([#1736](https://github.com/rack/rack/pull/1645), [@muirdm](https://github.com/muirdm))
344
374
  - `Rack::Request#scheme` returns `ws` or `wss` when one of the `X-Forwarded-Scheme` / `X-Forwarded-Proto` headers is set to `ws` or `wss`, respectively. ([#1730](https://github.com/rack/rack/issues/1730), [@erwanst](https://github.com/erwanst))
345
375
 
376
+ ## [2.2.19] - 2025-10-07
377
+
378
+ ### Security
379
+
380
+ - [CVE-2025-61772](https://github.com/advisories/GHSA-wpv5-97wm-hp9c) Multipart parser buffers unbounded per-part headers, enabling DoS (memory exhaustion)
381
+ - [CVE-2025-61771](https://github.com/advisories/GHSA-w9pc-fmgc-vxvw) Multipart parser buffers large non‑file fields entirely in memory, enabling DoS (memory exhaustion)
382
+ - [CVE-2025-61770](https://github.com/advisories/GHSA-p543-xpfm-54cp) Unbounded multipart preamble buffering enables DoS (memory exhaustion)
383
+
384
+ ## [2.2.18] - 2025-09-25
385
+
386
+ ### Security
387
+
388
+ - [CVE-2025-59830](https://github.com/rack/rack/security/advisories/GHSA-625h-95r8-8xpm) Unbounded parameter parsing in `Rack::QueryParser` can lead to memory exhaustion via semicolon-separated parameters.
389
+
390
+ ## [2.2.17] - 2025-06-03
391
+
392
+ - Backport `Rack::MediaType#params` now handles parameters without values. ([#2263](https://github.com/rack/rack/pull/2263), [@AllyMarthaJ](https://github.com/AllyMarthaJ))
393
+
394
+ ## [2.2.16] - 2025-05-22
395
+
396
+ - Fix incorrect backport of optional `CGI::Cookie` support. ([#2335](https://github.com/rack/rack/pull/2335), [@jeremyevans])
397
+
398
+ ## [2.2.15] - 2025-05-18
399
+
400
+ - Optional support for `CGI::Cookie` if not available. ([#2327](https://github.com/rack/rack/pull/2327), [#2333](https://github.com/rack/rack/pull/2333), [@earlopain])
401
+
346
402
  ## [2.2.14] - 2025-05-06
347
403
 
348
404
  ### Security
@@ -1130,3 +1186,4 @@ Items below this line are from the previously maintained HISTORY.md and NEWS.md
1130
1186
  [@wjordan]: https://github.com/wjordan "Will Jordan"
1131
1187
  [@BlakeWilliams]: https://github.com/BlakeWilliams "Blake Williams"
1132
1188
  [@davidstosik]: https://github.com/davidstosik "David Stosik"
1189
+ [@earlopain]: https://github.com/earlopain "Earlopain"
data/README.md CHANGED
@@ -210,6 +210,14 @@ query string, before attempting parsing, so if the same parameter key is
210
210
  used multiple times in the query, each counts as a separate parameter for
211
211
  this check.
212
212
 
213
+ ### `RACK_MULTIPART_BUFFERED_UPLOAD_BYTESIZE_LIMIT`
214
+
215
+ This environment variable sets the maximum amount of memory Rack will use
216
+ to buffer multipart parameters when parsing a request body. This considers
217
+ the size of the multipart mime headers and the body part for multipart
218
+ parameters that are buffered in memory and do not use tempfiles. This
219
+ defaults to 16MB if not provided.
220
+
213
221
  ### `param_depth_limit`
214
222
 
215
223
  ```ruby
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'cgi/cookie'
4
3
  require 'time'
5
4
 
6
5
  require_relative 'response'
@@ -11,6 +10,36 @@ module Rack
11
10
  # MockRequest.
12
11
 
13
12
  class MockResponse < Rack::Response
13
+ begin
14
+ # Recent versions of the CGI gem may not provide `CGI::Cookie`.
15
+ require 'cgi/cookie'
16
+ Cookie = CGI::Cookie
17
+ rescue LoadError
18
+ class Cookie
19
+ attr_reader :name, :value, :path, :domain, :expires, :secure
20
+
21
+ def initialize(args)
22
+ @name = args["name"]
23
+ @value = args["value"]
24
+ @path = args["path"]
25
+ @domain = args["domain"]
26
+ @expires = args["expires"]
27
+ @secure = args["secure"]
28
+ end
29
+
30
+ def method_missing(method_name, *args, &block)
31
+ @value.send(method_name, *args, &block)
32
+ end
33
+ # :nocov:
34
+ ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
35
+ # :nocov:
36
+
37
+ def respond_to_missing?(method_name, include_all = false)
38
+ @value.respond_to?(method_name, include_all) || super
39
+ end
40
+ end
41
+ end
42
+
14
43
  class << self
15
44
  alias [] new
16
45
  end
@@ -83,7 +112,7 @@ module Rack
83
112
  Array(set_cookie_header).each do |cookie|
84
113
  cookie_name, cookie_filling = cookie.split('=', 2)
85
114
  cookie_attributes = identify_cookie_attributes cookie_filling
86
- parsed_cookie = CGI::Cookie.new(
115
+ parsed_cookie = Cookie.new(
87
116
  'name' => cookie_name.strip,
88
117
  'value' => cookie_attributes.fetch('value'),
89
118
  'path' => cookie_attributes.fetch('path', nil),
@@ -100,7 +129,7 @@ module Rack
100
129
  def identify_cookie_attributes(cookie_filling)
101
130
  cookie_bits = cookie_filling.split(';')
102
131
  cookie_attributes = Hash.new
103
- cookie_attributes.store('value', cookie_bits[0].strip)
132
+ cookie_attributes.store('value', Array(cookie_bits[0].strip))
104
133
  cookie_bits.drop(1).each do |bit|
105
134
  if bit.include? '='
106
135
  cookie_attribute, attribute_value = bit.split('=', 2)
@@ -31,10 +31,12 @@ module Rack
31
31
  Error = BoundaryTooLongError
32
32
 
33
33
  EOL = "\r\n"
34
+ FWS = /[ \t]+(?:\r\n[ \t]+)?/ # whitespace with optional folding
35
+ HEADER_VALUE = "(?:[^\r\n]|\r\n[ \t])*" # anything but a non-folding CRLF
34
36
  MULTIPART = %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|ni
35
- MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{EOL}/ni
36
- MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:(.*)(?=#{EOL}(\S|\z))/ni
37
- MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{EOL}]*)/ni
37
+ MULTIPART_CONTENT_TYPE = /^Content-Type:#{FWS}?(#{HEADER_VALUE})/ni
38
+ MULTIPART_CONTENT_DISPOSITION = /^Content-Disposition:#{FWS}?(#{HEADER_VALUE})/ni
39
+ MULTIPART_CONTENT_ID = /^Content-ID:#{FWS}?(#{HEADER_VALUE})/ni
38
40
 
39
41
  class Parser
40
42
  BUFSIZE = 1_048_576
@@ -45,6 +47,27 @@ module Rack
45
47
  Tempfile.new(["RackMultipart", extension])
46
48
  }
47
49
 
50
+ BOUNDARY_START_LIMIT = 16 * 1024
51
+ private_constant :BOUNDARY_START_LIMIT
52
+
53
+ MIME_HEADER_BYTESIZE_LIMIT = 64 * 1024
54
+ private_constant :MIME_HEADER_BYTESIZE_LIMIT
55
+
56
+ env_int = lambda do |key, val|
57
+ if str_val = ENV[key]
58
+ begin
59
+ val = Integer(str_val, 10)
60
+ rescue ArgumentError
61
+ raise ArgumentError, "non-integer value provided for environment variable #{key}"
62
+ end
63
+ end
64
+
65
+ val
66
+ end
67
+
68
+ BUFFERED_UPLOAD_BYTESIZE_LIMIT = env_int.call("RACK_MULTIPART_BUFFERED_UPLOAD_BYTESIZE_LIMIT", 16 * 1024 * 1024)
69
+ private_constant :BUFFERED_UPLOAD_BYTESIZE_LIMIT
70
+
48
71
  class BoundedIO # :nodoc:
49
72
  def initialize(io, content_length)
50
73
  @io = io
@@ -204,6 +227,8 @@ module Rack
204
227
 
205
228
  @state = :FAST_FORWARD
206
229
  @mime_index = 0
230
+ @body_retained = nil
231
+ @retained_size = 0
207
232
  @collector = Collector.new tempfile
208
233
 
209
234
  @sbuf = StringScanner.new("".dup)
@@ -285,6 +310,10 @@ module Rack
285
310
 
286
311
  # retry for opening boundary
287
312
  else
313
+ # We raise if we don't find the multipart boundary, to avoid unbounded memory
314
+ # buffering. Note that the actual limit is the higher of 16KB and the buffer size (1MB by default)
315
+ raise Error, "multipart boundary not found within limit" if @sbuf.string.bytesize > BOUNDARY_START_LIMIT
316
+
288
317
  # no boundary found, keep reading data
289
318
  return :want_read
290
319
  end
@@ -401,16 +430,30 @@ module Rack
401
430
  name = filename || "#{content_type || TEXT_PLAIN}[]".dup
402
431
  end
403
432
 
433
+ # Mime part head data is retained for both TempfilePart and BufferPart
434
+ # for the entireity of the parse, even though it isn't used for BufferPart.
435
+ update_retained_size(head.bytesize)
436
+
437
+ # If a filename is given, a TempfilePart will be used, so the body will
438
+ # not be buffered in memory. However, if a filename is not given, a BufferPart
439
+ # will be used, and the body will be buffered in memory.
440
+ @body_retained = !filename
441
+
404
442
  @collector.on_mime_head @mime_index, head, filename, content_type, name
405
443
  @state = :MIME_BODY
406
444
  else
407
- :want_read
445
+ # We raise if the mime part header is too large, to avoid unbounded memory
446
+ # buffering. Note that the actual limit is the higher of 64KB and the buffer size (1MB by default)
447
+ raise Error, "multipart mime part header too large" if @sbuf.string.bytesize > MIME_HEADER_BYTESIZE_LIMIT
448
+
449
+ return :want_read
408
450
  end
409
451
  end
410
452
 
411
453
  def handle_mime_body
412
454
  if (body_with_boundary = @sbuf.check_until(@body_regex)) # check but do not advance the pointer yet
413
455
  body = body_with_boundary.sub(@body_regex_at_end, '') # remove the boundary from the string
456
+ update_retained_size(body.bytesize) if @body_retained
414
457
  @collector.on_mime_body @mime_index, body
415
458
  @sbuf.pos += body.length + 2 # skip \r\n after the content
416
459
  @state = :CONSUME_TOKEN
@@ -419,7 +462,9 @@ module Rack
419
462
  # Save what we have so far
420
463
  if @rx_max_size < @sbuf.rest_size
421
464
  delta = @sbuf.rest_size - @rx_max_size
422
- @collector.on_mime_body @mime_index, @sbuf.peek(delta)
465
+ body = @sbuf.peek(delta)
466
+ update_retained_size(body.bytesize) if @body_retained
467
+ @collector.on_mime_body @mime_index, body
423
468
  @sbuf.pos += delta
424
469
  @sbuf.string = @sbuf.rest
425
470
  end
@@ -427,6 +472,13 @@ module Rack
427
472
  end
428
473
  end
429
474
 
475
+ def update_retained_size(size)
476
+ @retained_size += size
477
+ if @retained_size > BUFFERED_UPLOAD_BYTESIZE_LIMIT
478
+ raise Error, "multipart data over retained size limit"
479
+ end
480
+ end
481
+
430
482
  # Scan until the we find the start or end of the boundary.
431
483
  # If we find it, return the appropriate symbol for the start or
432
484
  # end of the boundary. If we don't find the start or end of the
data/lib/rack/version.rb CHANGED
@@ -12,7 +12,7 @@
12
12
  # so it should be enough just to <tt>require 'rack'</tt> in your code.
13
13
 
14
14
  module Rack
15
- RELEASE = "3.1.14"
15
+ RELEASE = "3.1.17"
16
16
 
17
17
  # Return the Rack release as a dotted string.
18
18
  def self.release
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.14
4
+ version: 3.1.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leah Neukirchen
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-05-06 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: minitest
@@ -76,9 +75,9 @@ email: leah@vuxu.org
76
75
  executables: []
77
76
  extensions: []
78
77
  extra_rdoc_files:
79
- - README.md
80
78
  - CHANGELOG.md
81
79
  - CONTRIBUTING.md
80
+ - README.md
82
81
  files:
83
82
  - CHANGELOG.md
84
83
  - CONTRIBUTING.md
@@ -143,7 +142,6 @@ metadata:
143
142
  changelog_uri: https://github.com/rack/rack/blob/main/CHANGELOG.md
144
143
  documentation_uri: https://rubydoc.info/github/rack/rack
145
144
  source_code_uri: https://github.com/rack/rack
146
- post_install_message:
147
145
  rdoc_options: []
148
146
  require_paths:
149
147
  - lib
@@ -158,8 +156,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
158
156
  - !ruby/object:Gem::Version
159
157
  version: '0'
160
158
  requirements: []
161
- rubygems_version: 3.5.22
162
- signing_key:
159
+ rubygems_version: 3.6.9
163
160
  specification_version: 4
164
161
  summary: A modular Ruby webserver interface.
165
162
  test_files: []