Package: release.debian.org
Severity: normal
X-Debbugs-Cc: ruby-r...@packages.debian.org
Control: affects -1 + src:ruby-rack
User: release.debian....@packages.debian.org
Usertags: unblock

Please unblock package ruby-rack.

[ Reason ]
Fixes for RC bugs #1104927 and #1109027.

[ Impact ]
autopkgtests fail, CVE-2025-46727 is exploitable (DoS).

[ Tests ]
autopkgtests pass in unstable.

[ Risks ]
The minor version update also includes other changes including one other
CVE fix. I do not think they pose a significant risk as they also come
with additional unit tests.

[ Checklist ]
  [x] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

[ Other info ]
I have fixed the bugs via a NMU.

unblock ruby-rack/3.1.16-0.1
diff -Nru ruby-rack-3.1.12/CHANGELOG.md ruby-rack-3.1.16/CHANGELOG.md
--- ruby-rack-3.1.12/CHANGELOG.md       2025-03-10 22:21:44.000000000 +0100
+++ ruby-rack-3.1.16/CHANGELOG.md       2025-06-05 00:27:50.000000000 +0200
@@ -2,6 +2,20 @@
 
 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/).
 
+## [3.1.15] - 2025-05-18
+
+- 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])
+
+## [3.1.14] - 2025-05-06
+
+### Security
+
+- 
[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.
+
+## [3.1.13] - 2025-04-13
+
+- Ensure `Rack::ETag` correctly updates response body. 
([#2324](https://github.com/rack/rack/pull/2324), [@ioquatix])
+
 ## [3.1.12] - 2025-03-11
 
 ### Security
@@ -129,6 +143,24 @@
 
 - In `Rack::Files`, ignore the `Range` header if served file is 0 bytes. 
([#2159](https://github.com/rack/rack/pull/2159), [@zarqman])
 
+## [3.0.18] - 2025-05-22
+
+- Fix incorrect backport of optional `CGI::Cookie` support. 
([#2335](https://github.com/rack/rack/pull/2335), [@jeremyevans])
+
+## [3.0.17] - 2025-05-18
+
+- 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])
+
+## [3.0.16] - 2025-05-06
+
+### Security
+
+- 
[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.
+
+## [3.0.15] - 2025-04-13
+
+- Ensure `Rack::ETag` correctly updates response body. 
([#2324](https://github.com/rack/rack/pull/2324), [@ioquatix])
+
 ## [3.0.14] - 2025-03-11
 
 ### Security
@@ -141,6 +173,10 @@
 
 - 
[CVE-2025-27111](https://github.com/rack/rack/security/advisories/GHSA-8cgq-6mh2-7j6v)
 Possible Log Injection in `Rack::Sendfile`.
 
+### Fixed
+
+- Remove autoloads for constants no longer shipped with Rack. 
([#2269](https://github.com/rack/rack/pull/2269), 
[@ccutrer](https://github.com/ccutrer))
+
 ## [3.0.12] - 2025-02-12
 
 ### Security
@@ -275,7 +311,7 @@
 - Remove deprecated Rack::Request::SCHEME_WHITELIST. ([@jeremyevans])
 - 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])
 - Remove `rack.version` as it comes too late to be useful. 
([#1938](https://github.com/rack/rack/pull/1938), [@ioquatix])
-- 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])
+- Extract `rackup` command, `Rack::Server`, `Rack::Handler` and related code 
into a separate gem. ([#1937](https://github.com/rack/rack/pull/1937), 
[@ioquatix])
 
 ### Added
 
@@ -323,6 +359,20 @@
 - 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))
 - `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))
 
+## [2.2.16] - 2025-05-22
+
+- Fix incorrect backport of optional `CGI::Cookie` support. 
([#2335](https://github.com/rack/rack/pull/2335), [@jeremyevans])
+
+## [2.2.15] - 2025-05-18
+
+- 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])
+
+## [2.2.14] - 2025-05-06
+
+### Security
+
+- 
[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.
+
 ## [2.2.13] - 2025-03-11
 
 ### Security
@@ -1104,3 +1154,4 @@
 [@wjordan]: https://github.com/wjordan "Will Jordan"
 [@BlakeWilliams]: https://github.com/BlakeWilliams "Blake Williams"
 [@davidstosik]: https://github.com/davidstosik "David Stosik"
+[@earlopain]: https://github.com/earlopain "Earlopain"
diff -Nru ruby-rack-3.1.12/debian/changelog ruby-rack-3.1.16/debian/changelog
--- ruby-rack-3.1.12/debian/changelog   2025-03-19 16:53:01.000000000 +0100
+++ ruby-rack-3.1.16/debian/changelog   2025-07-15 18:00:20.000000000 +0200
@@ -1,3 +1,12 @@
+ruby-rack (3.1.16-0.1) unstable; urgency=medium
+
+  * Non-maintainer upload
+  * New upstream version 3.1.16 (Closes: #1104927, #1107363),
+    fixes CVE-2025-46727, CVE-2025-49007
+  * Remove ruby-bacon from autopkgtests (Closes: #1109027)
+
+ -- Bastian Germann <b...@debian.org>  Tue, 15 Jul 2025 18:00:20 +0200
+
 ruby-rack (3.1.12-1) unstable; urgency=medium
 
   * Team upload
diff -Nru ruby-rack-3.1.12/debian/tests/control 
ruby-rack-3.1.16/debian/tests/control
--- ruby-rack-3.1.12/debian/tests/control       2025-03-16 18:51:46.000000000 
+0100
+++ ruby-rack-3.1.16/debian/tests/control       2025-07-15 18:00:20.000000000 
+0200
@@ -2,5 +2,5 @@
 Depends: @, curl, ruby-rackup, ruby-webrick
 
 Test-Command: gem2deb-test-runner
-Depends: @, gem2deb-test-runner, rake, ruby-bacon, 
ruby-minitest-global-expectations, ruby-webrick
+Depends: @, gem2deb-test-runner, rake, ruby-minitest-global-expectations, 
ruby-webrick
 Restrictions: allow-stderr
diff -Nru ruby-rack-3.1.12/.github/workflows/test.yaml 
ruby-rack-3.1.16/.github/workflows/test.yaml
--- ruby-rack-3.1.12/.github/workflows/test.yaml        2025-03-10 
22:21:44.000000000 +0100
+++ ruby-rack-3.1.16/.github/workflows/test.yaml        2025-06-05 
00:27:50.000000000 +0200
@@ -21,6 +21,7 @@
           - '3.1'
           - '3.2'
           - '3.3'
+          - '3.4'
           - jruby-head
           - truffleruby-head
         include:
diff -Nru ruby-rack-3.1.12/lib/rack/etag.rb ruby-rack-3.1.16/lib/rack/etag.rb
--- ruby-rack-3.1.12/lib/rack/etag.rb   2025-03-10 22:21:44.000000000 +0100
+++ ruby-rack-3.1.16/lib/rack/etag.rb   2025-06-05 00:27:50.000000000 +0200
@@ -32,6 +32,9 @@
         body = body.to_ary
         digest = digest_body(body)
         headers[ETAG_STRING] = %(W/"#{digest}") if digest
+
+        # Body was modified, so we need to re-assign it:
+        response[2] = body
       end
 
       unless headers[CACHE_CONTROL]
diff -Nru ruby-rack-3.1.12/lib/rack/mock_response.rb 
ruby-rack-3.1.16/lib/rack/mock_response.rb
--- ruby-rack-3.1.12/lib/rack/mock_response.rb  2025-03-10 22:21:44.000000000 
+0100
+++ ruby-rack-3.1.16/lib/rack/mock_response.rb  2025-06-05 00:27:50.000000000 
+0200
@@ -1,6 +1,5 @@
 # frozen_string_literal: true
 
-require 'cgi/cookie'
 require 'time'
 
 require_relative 'response'
@@ -11,6 +10,36 @@
   # MockRequest.
 
   class MockResponse < Rack::Response
+    begin
+      # Recent versions of the CGI gem may not provide `CGI::Cookie`.
+      require 'cgi/cookie'
+      Cookie = CGI::Cookie
+    rescue LoadError
+      class Cookie
+        attr_reader :name, :value, :path, :domain, :expires, :secure
+
+        def initialize(args)
+          @name = args["name"]
+          @value = args["value"]
+          @path = args["path"]
+          @domain = args["domain"]
+          @expires = args["expires"]
+          @secure = args["secure"]
+        end
+
+        def method_missing(method_name, *args, &block)
+          @value.send(method_name, *args, &block)
+        end
+        # :nocov:
+        ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
+        # :nocov:
+
+        def respond_to_missing?(method_name, include_all = false)
+          @value.respond_to?(method_name, include_all) || super
+        end
+      end
+    end
+
     class << self
       alias [] new
     end
@@ -83,7 +112,7 @@
         Array(set_cookie_header).each do |cookie|
           cookie_name, cookie_filling = cookie.split('=', 2)
           cookie_attributes = identify_cookie_attributes cookie_filling
-          parsed_cookie = CGI::Cookie.new(
+          parsed_cookie = Cookie.new(
             'name' => cookie_name.strip,
             'value' => cookie_attributes.fetch('value'),
             'path' => cookie_attributes.fetch('path', nil),
@@ -100,7 +129,7 @@
     def identify_cookie_attributes(cookie_filling)
       cookie_bits = cookie_filling.split(';')
       cookie_attributes = Hash.new
-      cookie_attributes.store('value', cookie_bits[0].strip)
+      cookie_attributes.store('value', Array(cookie_bits[0].strip))
       cookie_bits.drop(1).each do |bit|
         if bit.include? '='
           cookie_attribute, attribute_value = bit.split('=', 2)
diff -Nru ruby-rack-3.1.12/lib/rack/multipart/parser.rb 
ruby-rack-3.1.16/lib/rack/multipart/parser.rb
--- ruby-rack-3.1.12/lib/rack/multipart/parser.rb       2025-03-10 
22:21:44.000000000 +0100
+++ ruby-rack-3.1.16/lib/rack/multipart/parser.rb       2025-06-05 
00:27:50.000000000 +0200
@@ -31,10 +31,12 @@
     Error = BoundaryTooLongError
 
     EOL = "\r\n"
+    FWS = /[ \t]+(?:\r\n[ \t]+)?/ # whitespace with optional folding
+    HEADER_VALUE = "(?:[^\r\n]|\r\n[ \t])*" # anything but a non-folding CRLF
     MULTIPART = %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|ni
-    MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{EOL}/ni
-    MULTIPART_CONTENT_DISPOSITION = 
/Content-Disposition:(.*)(?=#{EOL}(\S|\z))/ni
-    MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{EOL}]*)/ni
+    MULTIPART_CONTENT_TYPE = /^Content-Type:#{FWS}?(#{HEADER_VALUE})/ni
+    MULTIPART_CONTENT_DISPOSITION = 
/^Content-Disposition:#{FWS}?(#{HEADER_VALUE})/ni
+    MULTIPART_CONTENT_ID = /^Content-ID:#{FWS}?(#{HEADER_VALUE})/ni
 
     class Parser
       BUFSIZE = 1_048_576
diff -Nru ruby-rack-3.1.12/lib/rack/query_parser.rb 
ruby-rack-3.1.16/lib/rack/query_parser.rb
--- ruby-rack-3.1.12/lib/rack/query_parser.rb   2025-03-10 22:21:44.000000000 
+0100
+++ ruby-rack-3.1.16/lib/rack/query_parser.rb   2025-06-05 00:27:50.000000000 
+0200
@@ -21,21 +21,47 @@
       include BadRequest
     end
 
-    # ParamsTooDeepError is the error that is raised when params are 
recursively
-    # nested over the specified limit.
-    class ParamsTooDeepError < RangeError
+    # QueryLimitError is for errors raised when the query provided exceeds one
+    # of the query parser limits.
+    class QueryLimitError < RangeError
       include BadRequest
     end
 
-    def self.make_default(param_depth_limit)
-      new Params, param_depth_limit
+    # ParamsTooDeepError is the old name for the error that is raised when 
params
+    # are recursively nested over the specified limit. Make it the same as
+    # as QueryLimitError, so that code that rescues ParamsTooDeepError error
+    # to handle bad query strings also now handles other limits.
+    ParamsTooDeepError = QueryLimitError
+
+    def self.make_default(param_depth_limit, **options)
+      new(Params, param_depth_limit, **options)
     end
 
     attr_reader :param_depth_limit
 
-    def initialize(params_class, param_depth_limit)
+    env_int = lambda do |key, val|
+      if str_val = ENV[key]
+        begin
+          val = Integer(str_val, 10)
+        rescue ArgumentError
+          raise ArgumentError, "non-integer value provided for environment 
variable #{key}"
+        end
+      end
+
+      val
+    end
+
+    BYTESIZE_LIMIT = env_int.call("RACK_QUERY_PARSER_BYTESIZE_LIMIT", 4194304)
+    private_constant :BYTESIZE_LIMIT
+
+    PARAMS_LIMIT = env_int.call("RACK_QUERY_PARSER_PARAMS_LIMIT", 4096)
+    private_constant :PARAMS_LIMIT
+
+    def initialize(params_class, param_depth_limit, bytesize_limit: 
BYTESIZE_LIMIT, params_limit: PARAMS_LIMIT)
       @params_class = params_class
       @param_depth_limit = param_depth_limit
+      @bytesize_limit = bytesize_limit
+      @params_limit = params_limit
     end
 
     # Stolen from Mongrel, with some small modifications:
@@ -47,7 +73,7 @@
 
       params = make_params
 
-      (qs || '').split(separator ? (COMMON_SEP[separator] || /[#{separator}] 
*/n) : DEFAULT_SEP).each do |p|
+      check_query_string(qs, separator).split(separator ? 
(COMMON_SEP[separator] || /[#{separator}] */n) : DEFAULT_SEP).each do |p|
         next if p.empty?
         k, v = p.split('=', 2).map!(&unescaper)
 
@@ -74,7 +100,7 @@
       params = make_params
 
       unless qs.nil? || qs.empty?
-        (qs || '').split(separator ? (COMMON_SEP[separator] || /[#{separator}] 
*/n) : DEFAULT_SEP).each do |p|
+        check_query_string(qs, separator).split(separator ? 
(COMMON_SEP[separator] || /[#{separator}] */n) : DEFAULT_SEP).each do |p|
           k, v = p.split('=', 2).map! { |s| unescape(s) }
 
           _normalize_params(params, k, v, 0)
@@ -189,6 +215,22 @@
       true
     end
 
+    def check_query_string(qs, sep)
+      if qs
+        if qs.bytesize > @bytesize_limit
+          raise QueryLimitError, "total query size (#{qs.bytesize}) exceeds 
limit (#{@bytesize_limit})"
+        end
+
+        if (param_count = qs.count(sep.is_a?(String) ? sep : '&')) >= 
@params_limit
+          raise QueryLimitError, "total number of query parameters 
(#{param_count+1}) exceeds limit (#{@params_limit})"
+        end
+
+        qs
+      else
+        ''
+      end
+    end
+
     def unescape(string, encoding = Encoding::UTF_8)
       URI.decode_www_form_component(string, encoding)
     end
diff -Nru ruby-rack-3.1.12/lib/rack/version.rb 
ruby-rack-3.1.16/lib/rack/version.rb
--- ruby-rack-3.1.12/lib/rack/version.rb        2025-03-10 22:21:44.000000000 
+0100
+++ ruby-rack-3.1.16/lib/rack/version.rb        2025-06-05 00:27:50.000000000 
+0200
@@ -12,7 +12,7 @@
 # so it should be enough just to <tt>require 'rack'</tt> in your code.
 
 module Rack
-  RELEASE = "3.1.12"
+  RELEASE = "3.1.16"
 
   # Return the Rack release as a dotted string.
   def self.release
diff -Nru ruby-rack-3.1.12/README.md ruby-rack-3.1.16/README.md
--- ruby-rack-3.1.12/README.md  2025-03-10 22:21:44.000000000 +0100
+++ ruby-rack-3.1.16/README.md  2025-06-05 00:27:50.000000000 +0200
@@ -183,6 +183,33 @@
 Rack exposes several configuration parameters to control various features of 
the
 implementation.
 
+### `RACK_QUERY_PARSER_BYTESIZE_LIMIT`
+
+This environment variable sets the default for the maximum query string 
bytesize
+that `Rack::QueryParser` will attempt to parse.  Attempts to use a query string
+that exceeds this number of bytes will result in a
+`Rack::QueryParser::QueryLimitError` exception. If this enviroment variable is
+provided, it must be an integer, or `Rack::QueryParser` will raise an 
exception.
+
+The default limit can be overridden on a per-`Rack::QueryParser` basis using
+the `bytesize_limit` keyword argument when creating the `Rack::QueryParser`.
+
+### `RACK_QUERY_PARSER_PARAMS_LIMIT`
+
+This environment variable sets the default for the maximum number of query
+parameters that `Rack::QueryParser` will attempt to parse.  Attempts to use a
+query string with more than this many query parameters will result in a
+`Rack::QueryParser::QueryLimitError` exception. If this enviroment variable is
+provided, it must be an integer, or `Rack::QueryParser` will raise an 
exception.
+
+The default limit can be overridden on a per-`Rack::QueryParser` basis using
+the `params_limit` keyword argument when creating the `Rack::QueryParser`.
+
+This is implemented by counting the number of parameter separators in the
+query string, before attempting parsing, so if the same parameter key is
+used multiple times in the query, each counts as a separate parameter for
+this check.
+
 ### `param_depth_limit`
 
 ```ruby
diff -Nru ruby-rack-3.1.12/test/spec_etag.rb ruby-rack-3.1.16/test/spec_etag.rb
--- ruby-rack-3.1.12/test/spec_etag.rb  2025-03-10 22:21:44.000000000 +0100
+++ ruby-rack-3.1.16/test/spec_etag.rb  2025-06-05 00:27:50.000000000 +0200
@@ -28,6 +28,16 @@
     response[1]['etag'].must_equal "W/\"dffd6021bb2bd5b0af676290809ec3a5\""
   end
 
+  it "returns a valid response body when using a linted app" do
+    app = lambda { |env| [200, { 'content-type' => 'text/plain' }, ["Hello, 
World!"]] }
+    response = etag(Rack::Lint.new(app)).call(request)
+    response[1]['etag'].must_equal "W/\"dffd6021bb2bd5b0af676290809ec3a5\""
+
+    response[2].each do |chunk|
+      chunk.must_equal "Hello, World!"
+    end
+  end
+
   it "set etag if none is set if status is 201" do
     app = lambda { |env| [201, { 'content-type' => 'text/plain' }, ["Hello, 
World!"]] }
     response = etag(app).call(request)
diff -Nru ruby-rack-3.1.12/test/spec_headers.rb 
ruby-rack-3.1.16/test/spec_headers.rb
--- ruby-rack-3.1.12/test/spec_headers.rb       2025-03-10 22:21:44.000000000 
+0100
+++ ruby-rack-3.1.16/test/spec_headers.rb       2025-06-05 00:27:50.000000000 
+0200
@@ -220,8 +220,8 @@
 
   def test_inspect
     %i'inspect to_s'.each do |meth|
-      assert_equal '{}', @h.send(meth)
-      assert_equal '{"ab"=>"1", "cd"=>"2", "3"=>"4"}', @fh.send(meth)
+      assert_equal({}.inspect, @h.send(meth))
+      assert_equal({"ab"=>"1", "cd"=>"2", "3"=>"4"}.inspect, @fh.send(meth))
     end
   end
 
diff -Nru ruby-rack-3.1.12/test/spec_mock_response.rb 
ruby-rack-3.1.16/test/spec_mock_response.rb
--- ruby-rack-3.1.12/test/spec_mock_response.rb 2025-03-10 22:21:44.000000000 
+0100
+++ ruby-rack-3.1.16/test/spec_mock_response.rb 2025-06-05 00:27:50.000000000 
+0200
@@ -84,6 +84,7 @@
   it "provides access to session cookies" do
     res = Rack::MockRequest.new(app).get("")
     session_cookie = res.cookie("session_test")
+    session_cookie[0].must_equal "session_test"
     session_cookie.value[0].must_equal "session_test"
     session_cookie.domain.must_equal "test.com"
     session_cookie.path.must_equal "/"
diff -Nru ruby-rack-3.1.12/test/spec_multipart.rb 
ruby-rack-3.1.16/test/spec_multipart.rb
--- ruby-rack-3.1.12/test/spec_multipart.rb     2025-03-10 22:21:44.000000000 
+0100
+++ ruby-rack-3.1.16/test/spec_multipart.rb     2025-06-05 00:27:50.000000000 
+0200
@@ -334,7 +334,7 @@
 
   it "ignores content-disposition values over to 1536 bytes" do
     x = content_disposition_parse.call("a=#{'a'*1510}; filename=\"bar\"; 
name=\"file\"")
-    x.must_equal "text/plain"=>[""]
+    x.must_equal "application/pdf"=>[""]
   end
 
   it 'raises an EOF error on content-length mismatch' do
@@ -955,7 +955,7 @@
     data = <<-EOF
 --AaB03x\r
 content-type: text/plain\r
-content-disposition: attachment; name="quoted\\\\chars\\"in\rname"\r
+content-disposition: attachment; name="quoted\\\\chars\\"in\tname"\r
 \r
 true\r
 --AaB03x--\r
@@ -968,7 +968,7 @@
     }
     env = Rack::MockRequest.env_for("/", options)
     params = Rack::Multipart.parse_multipart(env)
-    params["quoted\\chars\"in\rname"].must_equal 'true'
+    params["quoted\\chars\"in\tname"].must_equal 'true'
   end
 
   it "supports mixed case metadata" do
diff -Nru ruby-rack-3.1.12/test/spec_query_parser.rb 
ruby-rack-3.1.16/test/spec_query_parser.rb
--- ruby-rack-3.1.12/test/spec_query_parser.rb  2025-03-10 22:21:44.000000000 
+0100
+++ ruby-rack-3.1.16/test/spec_query_parser.rb  2025-06-05 00:27:50.000000000 
+0200
@@ -7,11 +7,30 @@
 end
 
 describe Rack::QueryParser do
-  query_parser ||= Rack::QueryParser.make_default(8)
-
   it "can normalize values with missing values" do
+    query_parser = Rack::QueryParser.make_default(8)
     query_parser.parse_nested_query("a=a").must_equal({"a" => "a"})
     query_parser.parse_nested_query("a=").must_equal({"a" => ""})
     query_parser.parse_nested_query("a").must_equal({"a" => nil})
   end
+
+  it "accepts bytesize_limit to specify maximum size of query string to parse" 
do
+    query_parser = Rack::QueryParser.make_default(32, bytesize_limit: 3)
+    query_parser.parse_query("a=a").must_equal({"a" => "a"})
+    query_parser.parse_nested_query("a=a").must_equal({"a" => "a"})
+    query_parser.parse_nested_query("a=a", '&').must_equal({"a" => "a"})
+    proc { query_parser.parse_query("a=aa") }.must_raise 
Rack::QueryParser::QueryLimitError
+    proc { query_parser.parse_nested_query("a=aa") }.must_raise 
Rack::QueryParser::QueryLimitError
+    proc { query_parser.parse_nested_query("a=aa", '&') }.must_raise 
Rack::QueryParser::QueryLimitError
+  end
+
+  it "accepts params_limit to specify maximum number of query parameters to 
parse" do
+    query_parser = Rack::QueryParser.make_default(32, params_limit: 2)
+    query_parser.parse_query("a=a&b=b").must_equal({"a" => "a", "b" => "b"})
+    query_parser.parse_nested_query("a=a&b=b").must_equal({"a" => "a", "b" => 
"b"})
+    query_parser.parse_nested_query("a=a&b=b", '&').must_equal({"a" => "a", 
"b" => "b"})
+    proc { query_parser.parse_query("a=a&b=b&c=c") }.must_raise 
Rack::QueryParser::QueryLimitError
+    proc { query_parser.parse_nested_query("a=a&b=b&c=c", '&') }.must_raise 
Rack::QueryParser::QueryLimitError
+    proc { query_parser.parse_query("b[]=a&b[]=b&b[]=c") }.must_raise 
Rack::QueryParser::QueryLimitError
+  end
 end
diff -Nru ruby-rack-3.1.12/test/spec_request.rb 
ruby-rack-3.1.16/test/spec_request.rb
--- ruby-rack-3.1.12/test/spec_request.rb       2025-03-10 22:21:44.000000000 
+0100
+++ ruby-rack-3.1.16/test/spec_request.rb       2025-06-05 00:27:50.000000000 
+0200
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 require_relative 'helper'
-require 'cgi'
+require 'cgi/escape'
 require 'forwardable'
 require 'securerandom'
 
@@ -1910,6 +1910,9 @@
 
   class NonDelegate < Rack::Request
     def delegate?; false; end
+    def query_parser
+      Rack::QueryParser.make_default(Rack::Utils.param_depth_limit, 
bytesize_limit: 2**30)
+    end
   end
 
   def make_request(env)
@@ -1931,6 +1934,10 @@
       end
 
       def delegate?; true; end
+
+      def query_parser
+        Rack::QueryParser.make_default(Rack::Utils.param_depth_limit, 
bytesize_limit: 2**30)
+      end
     end
 
     def make_request(env)

Reply via email to