rack 3.1.16 → 3.1.18

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: 9cfb0a3abaaa98c515919466c018ed20c30b3f4d025d0ec68cfc61614735270f
4
- data.tar.gz: 4fd015b49d2c70a01d6518ea38b593d14b6d93de09132c3142390ab5fd719ff2
3
+ metadata.gz: 627b4fe8d3af482f544229eaa2a32868adf887decbc3616669646c0ecdb514e5
4
+ data.tar.gz: f5c0b0b49232d4dd4630e4ccf71f30a322bf7cf91f8e38711e9a203b2e2c03c7
5
5
  SHA512:
6
- metadata.gz: db64e3a6431f22b41af6ec7a31d38465d4db45612843f402692893665d88c904d4058469137372b208dbc6d79d7dcde12460e4fb7707681d81de8d3a6dc8e45f
7
- data.tar.gz: 695b14f7308dfb1a6be5304fb9bcf460ee855e296308ad114d8b9f12b9dcb420a17d4af3883f3a47f3fc66df2e28793fe4871965dec51b557840d136de14c22c
6
+ metadata.gz: 5f61477c3fc2f135ee874290de05f1c18c015fe9285348e5d673a41d86782bd00f61f83d8fea2a81af15158e3d87acea8c2edcf5600c7e292df79c2e40480f98
7
+ data.tar.gz: 6e22900f3d703d4db6d14e4a7960a52f835df19df2796cf1669e1c4415b4243a5e9097cf87ab1b37102090a98109408524ca6c2762a77f69eeda9755ed47aef1
data/CHANGELOG.md CHANGED
@@ -2,6 +2,27 @@
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.18] - 2025-10-10
6
+
7
+ ### Security
8
+
9
+ - [CVE-2025-61780](https://github.com/advisories/GHSA-r657-rxjc-j557) Improper handling of headers in `Rack::Sendfile` may allow proxy bypass.
10
+ - [CVE-2025-61919](https://github.com/advisories/GHSA-6xw4-3v39-52mm) Unbounded read in `Rack::Request` form parsing can lead to memory exhaustion.
11
+
12
+ ## [3.1.17] - 2025-10-07
13
+
14
+ ### Security
15
+
16
+ - [CVE-2025-61772](https://github.com/advisories/GHSA-wpv5-97wm-hp9c) Multipart parser buffers unbounded per-part headers, enabling DoS (memory exhaustion)
17
+ - [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)
18
+ - [CVE-2025-61770](https://github.com/advisories/GHSA-p543-xpfm-54cp) Unbounded multipart preamble buffering enables DoS (memory exhaustion)
19
+
20
+ ## [3.1.16] - 2025-06-04
21
+
22
+ ### Security
23
+
24
+ - [CVE-2025-49007](https://github.com/advisories/GHSA-47m2-26rw-j2jw) Fix ReDoS in multipart request.
25
+
5
26
  ## [3.1.15] - 2025-05-18
6
27
 
7
28
  - 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])
@@ -10,7 +31,7 @@ All notable changes to this project will be documented in this file. For info on
10
31
 
11
32
  ### Security
12
33
 
13
- - [CVE-2025-46727](https://github.com/rack/rack/security/advisories/GHSA-gjh7-p2fx-99vx) Unbounded parameter parsing in `Rack::QueryParser` can lead to memory exhaustion.
34
+ - [CVE-2025-46727](https://github.com/advisories/GHSA-gjh7-p2fx-99vx) Unbounded parameter parsing in `Rack::QueryParser` can lead to memory exhaustion.
14
35
 
15
36
  ## [3.1.13] - 2025-04-13
16
37
 
@@ -20,19 +41,19 @@ All notable changes to this project will be documented in this file. For info on
20
41
 
21
42
  ### Security
22
43
 
23
- - [CVE-2025-27610](https://github.com/rack/rack/security/advisories/GHSA-7wqh-767x-r66v) Local file inclusion in `Rack::Static`.
44
+ - [CVE-2025-27610](https://github.com/advisories/GHSA-7wqh-767x-r66v) Local file inclusion in `Rack::Static`.
24
45
 
25
46
  ## [3.1.11] - 2025-03-04
26
47
 
27
48
  ### Security
28
49
 
29
- - [CVE-2025-27111](https://github.com/rack/rack/security/advisories/GHSA-8cgq-6mh2-7j6v) Possible Log Injection in `Rack::Sendfile`.
50
+ - [CVE-2025-27111](https://github.com/advisories/GHSA-8cgq-6mh2-7j6v) Possible Log Injection in `Rack::Sendfile`.
30
51
 
31
52
  ## [3.1.10] - 2025-02-12
32
53
 
33
54
  ### Security
34
55
 
35
- - [CVE-2025-25184](https://github.com/rack/rack/security/advisories/GHSA-7g2v-jj9q-g3rg) Possible Log Injection in `Rack::CommonLogger`.
56
+ - [CVE-2025-25184](https://github.com/advisories/GHSA-7g2v-jj9q-g3rg) Possible Log Injection in `Rack::CommonLogger`.
36
57
 
37
58
  ## [3.1.9] - 2025-01-31
38
59
 
@@ -65,7 +86,7 @@ All notable changes to this project will be documented in this file. For info on
65
86
 
66
87
  ### Security
67
88
 
68
- - Fix potential ReDoS attack in `Rack::Request#parse_http_accept_header`. ([GHSA-cj83-2ww7-mvq7](https://github.com/rack/rack/security/advisories/GHSA-cj83-2ww7-mvq7), [@dwisiswant0](https://github.com/dwisiswant0))
89
+ - Fix potential ReDoS attack in `Rack::Request#parse_http_accept_header`. ([GHSA-cj83-2ww7-mvq7](https://github.com/advisories/GHSA-cj83-2ww7-mvq7), [@dwisiswant0](https://github.com/dwisiswant0))
69
90
 
70
91
  ## [3.1.4] - 2024-06-22
71
92
 
@@ -155,7 +176,7 @@ Rack v3.1 is primarily a maintenance release that removes features deprecated in
155
176
 
156
177
  ### Security
157
178
 
158
- - [CVE-2025-46727](https://github.com/rack/rack/security/advisories/GHSA-gjh7-p2fx-99vx) Unbounded parameter parsing in `Rack::QueryParser` can lead to memory exhaustion.
179
+ - [CVE-2025-46727](https://github.com/advisories/GHSA-gjh7-p2fx-99vx) Unbounded parameter parsing in `Rack::QueryParser` can lead to memory exhaustion.
159
180
 
160
181
  ## [3.0.15] - 2025-04-13
161
182
 
@@ -165,13 +186,13 @@ Rack v3.1 is primarily a maintenance release that removes features deprecated in
165
186
 
166
187
  ### Security
167
188
 
168
- - [CVE-2025-27610](https://github.com/rack/rack/security/advisories/GHSA-7wqh-767x-r66v) Local file inclusion in `Rack::Static`.
189
+ - [CVE-2025-27610](https://github.com/advisories/GHSA-7wqh-767x-r66v) Local file inclusion in `Rack::Static`.
169
190
 
170
191
  ## [3.0.13] - 2025-03-04
171
192
 
172
193
  ### Security
173
194
 
174
- - [CVE-2025-27111](https://github.com/rack/rack/security/advisories/GHSA-8cgq-6mh2-7j6v) Possible Log Injection in `Rack::Sendfile`.
195
+ - [CVE-2025-27111](https://github.com/advisories/GHSA-8cgq-6mh2-7j6v) Possible Log Injection in `Rack::Sendfile`.
175
196
 
176
197
  ### Fixed
177
198
 
@@ -181,7 +202,7 @@ Rack v3.1 is primarily a maintenance release that removes features deprecated in
181
202
 
182
203
  ### Security
183
204
 
184
- - [CVE-2025-25184](https://github.com/rack/rack/security/advisories/GHSA-7g2v-jj9q-g3rg) Possible Log Injection in `Rack::CommonLogger`.
205
+ - [CVE-2025-25184](https://github.com/advisories/GHSA-7g2v-jj9q-g3rg) Possible Log Injection in `Rack::CommonLogger`.
185
206
 
186
207
  ## [3.0.11] - 2024-05-10
187
208
 
@@ -359,6 +380,31 @@ Rack v3.1 is primarily a maintenance release that removes features deprecated in
359
380
  - 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))
360
381
  - `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))
361
382
 
383
+ ## [2.2.20] - 2025-10-10
384
+
385
+ ### Security
386
+
387
+ - [CVE-2025-61780](https://github.com/advisories/GHSA-r657-rxjc-j557) Improper handling of headers in `Rack::Sendfile` may allow proxy bypass.
388
+ - [CVE-2025-61919](https://github.com/advisories/GHSA-6xw4-3v39-52mm) Unbounded read in `Rack::Request` form parsing can lead to memory exhaustion.
389
+
390
+ ## [2.2.19] - 2025-10-07
391
+
392
+ ### Security
393
+
394
+ - [CVE-2025-61772](https://github.com/advisories/GHSA-wpv5-97wm-hp9c) Multipart parser buffers unbounded per-part headers, enabling DoS (memory exhaustion)
395
+ - [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)
396
+ - [CVE-2025-61770](https://github.com/advisories/GHSA-p543-xpfm-54cp) Unbounded multipart preamble buffering enables DoS (memory exhaustion)
397
+
398
+ ## [2.2.18] - 2025-09-25
399
+
400
+ ### Security
401
+
402
+ - [CVE-2025-59830](https://github.com/advisories/GHSA-625h-95r8-8xpm) Unbounded parameter parsing in `Rack::QueryParser` can lead to memory exhaustion via semicolon-separated parameters.
403
+
404
+ ## [2.2.17] - 2025-06-03
405
+
406
+ - Backport `Rack::MediaType#params` now handles parameters without values. ([#2263](https://github.com/rack/rack/pull/2263), [@AllyMarthaJ](https://github.com/AllyMarthaJ))
407
+
362
408
  ## [2.2.16] - 2025-05-22
363
409
 
364
410
  - Fix incorrect backport of optional `CGI::Cookie` support. ([#2335](https://github.com/rack/rack/pull/2335), [@jeremyevans])
@@ -371,25 +417,25 @@ Rack v3.1 is primarily a maintenance release that removes features deprecated in
371
417
 
372
418
  ### Security
373
419
 
374
- - [CVE-2025-46727](https://github.com/rack/rack/security/advisories/GHSA-gjh7-p2fx-99vx) Unbounded parameter parsing in `Rack::QueryParser` can lead to memory exhaustion.
420
+ - [CVE-2025-46727](https://github.com/advisories/GHSA-gjh7-p2fx-99vx) Unbounded parameter parsing in `Rack::QueryParser` can lead to memory exhaustion.
375
421
 
376
422
  ## [2.2.13] - 2025-03-11
377
423
 
378
424
  ### Security
379
425
 
380
- - [CVE-2025-27610](https://github.com/rack/rack/security/advisories/GHSA-7wqh-767x-r66v) Local file inclusion in `Rack::Static`.
426
+ - [CVE-2025-27610](https://github.com/advisories/GHSA-7wqh-767x-r66v) Local file inclusion in `Rack::Static`.
381
427
 
382
428
  ## [2.2.12] - 2025-03-04
383
429
 
384
430
  ### Security
385
431
 
386
- - [CVE-2025-27111](https://github.com/rack/rack/security/advisories/GHSA-8cgq-6mh2-7j6v) Possible Log Injection in `Rack::Sendfile`.
432
+ - [CVE-2025-27111](https://github.com/advisories/GHSA-8cgq-6mh2-7j6v) Possible Log Injection in `Rack::Sendfile`.
387
433
 
388
434
  ## [2.2.11] - 2025-02-12
389
435
 
390
436
  ### Security
391
437
 
392
- - [CVE-2025-25184](https://github.com/rack/rack/security/advisories/GHSA-7g2v-jj9q-g3rg) Possible Log Injection in `Rack::CommonLogger`.
438
+ - [CVE-2025-25184](https://github.com/advisories/GHSA-7g2v-jj9q-g3rg) Possible Log Injection in `Rack::CommonLogger`.
393
439
 
394
440
  ## [2.2.10] - 2024-10-14
395
441
 
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
@@ -47,6 +47,27 @@ module Rack
47
47
  Tempfile.new(["RackMultipart", extension])
48
48
  }
49
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
+
50
71
  class BoundedIO # :nodoc:
51
72
  def initialize(io, content_length)
52
73
  @io = io
@@ -206,6 +227,8 @@ module Rack
206
227
 
207
228
  @state = :FAST_FORWARD
208
229
  @mime_index = 0
230
+ @body_retained = nil
231
+ @retained_size = 0
209
232
  @collector = Collector.new tempfile
210
233
 
211
234
  @sbuf = StringScanner.new("".dup)
@@ -287,6 +310,10 @@ module Rack
287
310
 
288
311
  # retry for opening boundary
289
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
+
290
317
  # no boundary found, keep reading data
291
318
  return :want_read
292
319
  end
@@ -403,16 +430,30 @@ module Rack
403
430
  name = filename || "#{content_type || TEXT_PLAIN}[]".dup
404
431
  end
405
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
+
406
442
  @collector.on_mime_head @mime_index, head, filename, content_type, name
407
443
  @state = :MIME_BODY
408
444
  else
409
- :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
410
450
  end
411
451
  end
412
452
 
413
453
  def handle_mime_body
414
454
  if (body_with_boundary = @sbuf.check_until(@body_regex)) # check but do not advance the pointer yet
415
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
416
457
  @collector.on_mime_body @mime_index, body
417
458
  @sbuf.pos += body.length + 2 # skip \r\n after the content
418
459
  @state = :CONSUME_TOKEN
@@ -421,7 +462,9 @@ module Rack
421
462
  # Save what we have so far
422
463
  if @rx_max_size < @sbuf.rest_size
423
464
  delta = @sbuf.rest_size - @rx_max_size
424
- @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
425
468
  @sbuf.pos += delta
426
469
  @sbuf.string = @sbuf.rest
427
470
  end
@@ -429,6 +472,13 @@ module Rack
429
472
  end
430
473
  end
431
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
+
432
482
  # Scan until the we find the start or end of the boundary.
433
483
  # If we find it, return the appropriate symbol for the start or
434
484
  # end of the boundary. If we don't find the start or end of the
@@ -57,6 +57,8 @@ module Rack
57
57
  PARAMS_LIMIT = env_int.call("RACK_QUERY_PARSER_PARAMS_LIMIT", 4096)
58
58
  private_constant :PARAMS_LIMIT
59
59
 
60
+ attr_reader :bytesize_limit
61
+
60
62
  def initialize(params_class, param_depth_limit, bytesize_limit: BYTESIZE_LIMIT, params_limit: PARAMS_LIMIT)
61
63
  @params_class = params_class
62
64
  @param_depth_limit = param_depth_limit
@@ -218,7 +220,7 @@ module Rack
218
220
  def check_query_string(qs, sep)
219
221
  if qs
220
222
  if qs.bytesize > @bytesize_limit
221
- raise QueryLimitError, "total query size (#{qs.bytesize}) exceeds limit (#{@bytesize_limit})"
223
+ raise QueryLimitError, "total query size exceeds limit (#{@bytesize_limit})"
222
224
  end
223
225
 
224
226
  if (param_count = qs.count(sep.is_a?(String) ? sep : '&')) >= @params_limit
data/lib/rack/request.rb CHANGED
@@ -528,7 +528,10 @@ module Rack
528
528
  set_header RACK_REQUEST_FORM_PAIRS, pairs
529
529
  set_header RACK_REQUEST_FORM_HASH, expand_param_pairs(pairs)
530
530
  else
531
- form_vars = get_header(RACK_INPUT).read
531
+ # Add 2 bytes. One to check whether it is over the limit, and a second
532
+ # in case the slice! call below removes the last byte
533
+ # If read returns nil, use the empty string
534
+ form_vars = get_header(RACK_INPUT).read(query_parser.bytesize_limit + 2) || ''
532
535
 
533
536
  # Fix for Safari Ajax postings that always append \0
534
537
  # form_vars.sub!(/\0\z/, '') # performance replacement:
data/lib/rack/sendfile.rb CHANGED
@@ -16,21 +16,21 @@ module Rack
16
16
  # delivery code.
17
17
  #
18
18
  # In order to take advantage of this middleware, the response body must
19
- # respond to +to_path+ and the request must include an x-sendfile-type
19
+ # respond to +to_path+ and the request must include an `x-sendfile-type`
20
20
  # header. Rack::Files and other components implement +to_path+ so there's
21
- # rarely anything you need to do in your application. The x-sendfile-type
21
+ # rarely anything you need to do in your application. The `x-sendfile-type`
22
22
  # header is typically set in your web servers configuration. The following
23
23
  # sections attempt to document
24
24
  #
25
25
  # === Nginx
26
26
  #
27
- # Nginx supports the x-accel-redirect header. This is similar to x-sendfile
27
+ # Nginx supports the `x-accel-redirect` header. This is similar to `x-sendfile`
28
28
  # but requires parts of the filesystem to be mapped into a private URL
29
29
  # hierarchy.
30
30
  #
31
31
  # The following example shows the Nginx configuration required to create
32
- # a private "/files/" area, enable x-accel-redirect, and pass the special
33
- # x-sendfile-type and x-accel-mapping headers to the backend:
32
+ # a private "/files/" area, enable `x-accel-redirect`, and pass the special
33
+ # `x-accel-mapping` header to the backend:
34
34
  #
35
35
  # location ~ /files/(.*) {
36
36
  # internal;
@@ -44,24 +44,29 @@ module Rack
44
44
  # proxy_set_header X-Real-IP $remote_addr;
45
45
  # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
46
46
  #
47
- # proxy_set_header x-sendfile-type x-accel-redirect;
48
47
  # proxy_set_header x-accel-mapping /var/www/=/files/;
49
48
  #
50
49
  # proxy_pass http://127.0.0.1:8080/;
51
50
  # }
52
51
  #
53
- # Note that the x-sendfile-type header must be set exactly as shown above.
54
- # The x-accel-mapping header should specify the location on the file system,
52
+ # The `x-accel-mapping` header should specify the location on the file system,
55
53
  # followed by an equals sign (=), followed name of the private URL pattern
56
54
  # that it maps to. The middleware performs a simple substitution on the
57
55
  # resulting path.
58
56
  #
57
+ # To enable `x-accel-redirect`, you must configure the middleware explicitly:
58
+ #
59
+ # use Rack::Sendfile, "x-accel-redirect"
60
+ #
61
+ # For security reasons, the `x-sendfile-type` header from requests is ignored.
62
+ # The sendfile variation must be set via the middleware constructor.
63
+ #
59
64
  # See Also: https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile
60
65
  #
61
66
  # === lighttpd
62
67
  #
63
- # Lighttpd has supported some variation of the x-sendfile header for some
64
- # time, although only recent version support x-sendfile in a reverse proxy
68
+ # Lighttpd has supported some variation of the `x-sendfile` header for some
69
+ # time, although only recent version support `x-sendfile` in a reverse proxy
65
70
  # configuration.
66
71
  #
67
72
  # $HTTP["host"] == "example.com" {
@@ -83,7 +88,7 @@ module Rack
83
88
  #
84
89
  # === Apache
85
90
  #
86
- # x-sendfile is supported under Apache 2.x using a separate module:
91
+ # `x-sendfile` is supported under Apache 2.x using a separate module:
87
92
  #
88
93
  # https://tn123.org/mod_xsendfile/
89
94
  #
@@ -97,16 +102,28 @@ module Rack
97
102
  # === Mapping parameter
98
103
  #
99
104
  # The third parameter allows for an overriding extension of the
100
- # x-accel-mapping header. Mappings should be provided in tuples of internal to
105
+ # `x-accel-mapping` header. Mappings should be provided in tuples of internal to
101
106
  # external. The internal values may contain regular expression syntax, they
102
107
  # will be matched with case indifference.
108
+ #
109
+ # When `x-accel-redirect` is explicitly enabled via the variation parameter,
110
+ # and no application-level mappings are provided, the middleware will read
111
+ # the `x-accel-mapping` header from the proxy. This allows nginx to control
112
+ # the path mapping without requiring application-level configuration.
113
+ #
114
+ # === Security
115
+ #
116
+ # For security reasons, the `x-sendfile-type` header from HTTP requests is
117
+ # ignored. The sendfile variation must be explicitly configured via the
118
+ # middleware constructor to prevent information disclosure vulnerabilities
119
+ # where attackers could bypass proxy restrictions.
103
120
 
104
121
  class Sendfile
105
122
  def initialize(app, variation = nil, mappings = [])
106
123
  @app = app
107
124
  @variation = variation
108
125
  @mappings = mappings.map do |internal, external|
109
- [/^#{internal}/i, external]
126
+ [/\A#{internal}/i, external]
110
127
  end
111
128
  end
112
129
 
@@ -145,22 +162,35 @@ module Rack
145
162
  end
146
163
 
147
164
  private
165
+
148
166
  def variation(env)
149
- @variation ||
150
- env['sendfile.type'] ||
151
- env['HTTP_X_SENDFILE_TYPE']
167
+ # Note: HTTP_X_SENDFILE_TYPE is intentionally NOT read for security reasons.
168
+ # Attackers could use this header to enable x-accel-redirect and bypass proxy restrictions.
169
+ @variation || env['sendfile.type']
170
+ end
171
+
172
+ def x_accel_mapping(env)
173
+ # Only allow header when:
174
+ # 1. `x-accel-redirect` is explicitly enabled via constructor.
175
+ # 2. No application-level mappings are configured.
176
+ return nil unless @variation =~ /x-accel-redirect/i
177
+ return nil if @mappings.any?
178
+
179
+ env['HTTP_X_ACCEL_MAPPING']
152
180
  end
153
181
 
154
182
  def map_accel_path(env, path)
155
183
  if mapping = @mappings.find { |internal, _| internal =~ path }
156
- path.sub(*mapping)
157
- elsif mapping = env['HTTP_X_ACCEL_MAPPING']
184
+ return path.sub(*mapping)
185
+ elsif mapping = x_accel_mapping(env)
186
+ # Safe to use header: explicit config + no app mappings:
158
187
  mapping.split(',').map(&:strip).each do |m|
159
188
  internal, external = m.split('=', 2).map(&:strip)
160
- new_path = path.sub(/^#{internal}/i, external)
189
+ new_path = path.sub(/\A#{internal}/i, external)
161
190
  return new_path unless path == new_path
162
191
  end
163
- path
192
+
193
+ return path
164
194
  end
165
195
  end
166
196
  end
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.16"
15
+ RELEASE = "3.1.18"
16
16
 
17
17
  # Return the Rack release as a dotted string.
18
18
  def self.release
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.16
4
+ version: 3.1.18
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leah Neukirchen
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-06-04 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: minitest
@@ -75,9 +75,9 @@ email: leah@vuxu.org
75
75
  executables: []
76
76
  extensions: []
77
77
  extra_rdoc_files:
78
- - README.md
79
78
  - CHANGELOG.md
80
79
  - CONTRIBUTING.md
80
+ - README.md
81
81
  files:
82
82
  - CHANGELOG.md
83
83
  - CONTRIBUTING.md
@@ -156,7 +156,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
156
156
  - !ruby/object:Gem::Version
157
157
  version: '0'
158
158
  requirements: []
159
- rubygems_version: 3.6.2
159
+ rubygems_version: 3.6.9
160
160
  specification_version: 4
161
161
  summary: A modular Ruby webserver interface.
162
162
  test_files: []