From f5faca9222541591e1a7c3c97552ebb0c92733c7 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@jeremyevans.net>
Date: Wed, 18 Sep 2024 14:11:49 -0700
Subject: [PATCH] Prevent request smuggling

If a request has both a content-length and transfer-encoding
headers, return a 400 response.  This is allowed by RFC 7230
section 3.3.3.3.

Fixes #145
---
 tool/lib/webrick/httprequest.rb       |  4 ++++
 tool/test/webrick/test_httprequest.rb | 18 ++++++++++++++++++
 2 files changed, 22 insertions(+)

diff --git a/tool/lib/webrick/httprequest.rb b/tool/lib/webrick/httprequest.rb
index 5cf5844..820acb2 100644
--- a/tool/lib/webrick/httprequest.rb
+++ b/tool/lib/webrick/httprequest.rb
@@ -474,6 +474,10 @@ module WEBrick
     def read_body(socket, block)
       return unless socket
       if tc = self['transfer-encoding']
+        if self['content-length']
+          raise HTTPStatus::BadRequest, "request with both transfer-encoding and content-length, possible request smuggling"
+        end
+
         case tc
         when /\Achunked\z/io then read_chunked(socket, block)
         else raise HTTPStatus::NotImplemented, "Transfer-Encoding: #{tc}."
diff --git a/tool/test/webrick/test_httprequest.rb b/tool/test/webrick/test_httprequest.rb
index 855ff9d..cce9b91 100644
--- a/tool/test/webrick/test_httprequest.rb
+++ b/tool/test/webrick/test_httprequest.rb
@@ -81,6 +81,24 @@ GET /
     }
   end
 
+  def test_content_length_and_transfer_encoding_headers_smuggling
+    msg = <<~HTTP.gsub("\n", "\r\n")
+      POST /user HTTP/1.1
+      Content-Length: 28
+      Transfer-Encoding: chunked
+
+      0
+
+      GET /admin HTTP/1.1
+
+    HTTP
+    req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+    req.parse(StringIO.new(msg))
+    assert_raise(WEBrick::HTTPStatus::BadRequest){
+      req.body
+    }
+  end
+
   def test_parse_headers
     msg = <<-_end_of_message_
       GET /path HTTP/1.1
-- 
2.27.0

