From 6018ab9de4e94452d671429f2231dad28b5ee28a Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@jeremyevans.net>
Date: Sat, 11 Nov 2023 16:46:08 -0800
Subject: [PATCH] Fix bug chunk extension detection

This fixes a request smuggling vulnerability (Fixes #124).

Co-authored-by: Ben Kallus <benjamin.p.kallus.gr@dartmouth.edu>
---
 lib/webrick/httprequest.rb       |  2 +-
 test/webrick/test_httprequest.rb | 25 +++++++++++++++++++++++++
 2 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/lib/webrick/httprequest.rb b/lib/webrick/httprequest.rb
index 7a1686b..0cfc1c9 100644
--- a/lib/webrick/httprequest.rb
+++ b/lib/webrick/httprequest.rb
@@ -542,7 +542,7 @@ def read_body(socket, block)
 
     def read_chunk_size(socket)
       line = read_line(socket)
-      if /^([0-9a-fA-F]+)(?:;(\S+))?/ =~ line
+      if /\A([0-9a-fA-F]+)(?:;(\S+(?:=\S+)?))?\r\n\z/ =~ line
         chunk_size = $1.hex
         chunk_ext = $2
         [ chunk_size, chunk_ext ]
diff --git a/test/webrick/test_httprequest.rb b/test/webrick/test_httprequest.rb
index 9033217..73fa8a3 100644
--- a/test/webrick/test_httprequest.rb
+++ b/test/webrick/test_httprequest.rb
@@ -289,6 +289,31 @@ def test_chunked
     assert_equal(expect, dst.string)
   end
 
+  def test_bad_chunked
+    crlf = "\x0d\x0a"
+    expect = File.binread(__FILE__).freeze
+    msg = <<-_end_of_message_
+      POST /path HTTP/1.1\r
+      Transfer-Encoding: chunked\r
+      \r
+      01x1\r
+      \r
+      1
+    _end_of_message_
+    msg.gsub!(/^ {6}/, "")
+    req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+    req.parse(StringIO.new(msg))
+    assert_raise(WEBrick::HTTPStatus::BadRequest){ req.body }
+
+    # chunked req.body_reader
+    req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+    req.parse(StringIO.new(msg))
+    dst = StringIO.new
+    assert_raise(WEBrick::HTTPStatus::BadRequest) do
+      IO.copy_stream(req.body_reader, dst)
+    end
+  end
+
   def test_forwarded
     msg = <<-_end_of_message_
       GET /foo HTTP/1.1