From 54e4ffdd5affebcb0c015cc6ae74635c0831ed71 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@jeremyevans.net>
Date: Thu, 25 Sep 2025 17:51:18 +0900
Subject: [PATCH] Unbounded parameter parsing in `Rack::QueryParser`.

---
 CHANGELOG.md              | 4 ++++
 lib/rack/query_parser.rb  | 2 +-
 test/spec_query_parser.rb | 7 +++++++
 3 files changed, 12 insertions(+), 1 deletion(-)

--- a/lib/rack/query_parser.rb
+++ b/lib/rack/query_parser.rb
@@ -189,7 +189,7 @@
           raise QueryLimitError, "total query size (#{qs.bytesize}) exceeds limit (#{@bytesize_limit})"
         end
 
-        if (param_count = qs.count(sep.is_a?(String) ? sep : '&')) >= @params_limit
+        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
 
--- a/test/spec_query_parser.rb
+++ b/test/spec_query_parser.rb
@@ -29,5 +29,12 @@
     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
+
+    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
