From ad699ca3f5822ce6c409ccd8f9fea898e61703fe Mon Sep 17 00:00:00 2001
From: Aaron Patterson <tenderlove@ruby-lang.org>
Date: Thu, 26 May 2022 13:26:25 -0700
Subject: [PATCH 1/3] Restrict broken mime parsing

This commit restricts broken mime parsing to deal with a ReDOS
vulnerability.

[CVE-2022-30122]
---
 lib/rack/multipart.rb                             |  3 +--
 lib/rack/multipart/parser.rb                      |  3 ++-
 ...ame_with_escaped_quotes_and_modification_param |  2 +-
 test/spec_multipart.rb                            | 15 +--------------
 4 files changed, 5 insertions(+), 18 deletions(-)

diff --git a/lib/rack/multipart.rb b/lib/rack/multipart.rb
index db59ee59..39a1b0c1 100644
--- a/lib/rack/multipart.rb
+++ b/lib/rack/multipart.rb
@@ -14,8 +14,7 @@ module Rack
     TOKEN = /[^\s()<>,;:\\"\/\[\]?=]+/
     CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i
     VALUE = /"(?:\\"|[^"])*"|#{TOKEN}/
-    BROKEN_QUOTED = /^#{CONDISP}.*;\sfilename="(.*?)"(?:\s*$|\s*;\s*#{TOKEN}=)/i
-    BROKEN_UNQUOTED = /^#{CONDISP}.*;\sfilename=(#{TOKEN})/i
+    BROKEN = /^#{CONDISP}.*;\s*filename=(#{VALUE})/i
     MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{EOL}/ni
     MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*\s+name=(#{VALUE})/ni
     MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{EOL}]*)/ni
diff --git a/lib/rack/multipart/parser.rb b/lib/rack/multipart/parser.rb
index 94f0fae3..a80d1579 100644
--- a/lib/rack/multipart/parser.rb
+++ b/lib/rack/multipart/parser.rb
@@ -302,8 +302,9 @@ module Rack
           elsif filename = params['filename*']
             encoding, _, filename = filename.split("'", 3)
           end
-        when BROKEN_QUOTED, BROKEN_UNQUOTED
+        when BROKEN
           filename = $1
+          filename = $1 if filename =~ /^"(.*)"$/
         end
 
         return unless filename
diff --git a/test/multipart/filename_with_escaped_quotes_and_modification_param b/test/multipart/filename_with_escaped_quotes_and_modification_param
index 7619bd50..929f6ad3 100644
--- a/test/multipart/filename_with_escaped_quotes_and_modification_param
+++ b/test/multipart/filename_with_escaped_quotes_and_modification_param
@@ -1,6 +1,6 @@
 --AaB03x
 Content-Type: image/jpeg
-Content-Disposition: attachment; name="files"; filename=""human" genome.jpeg"; modification-date="Wed, 12 Feb 1997 16:29:51 -0500";
+Content-Disposition: attachment; name="files"; filename="\"human\" genome.jpeg"; modification-date="Wed, 12 Feb 1997 16:29:51 -0500";
 Content-Description: a complete map of the human genome
 
 contents
diff --git a/test/spec_multipart.rb b/test/spec_multipart.rb
index 2f957d92..ea3b9ef8 100644
--- a/test/spec_multipart.rb
+++ b/test/spec_multipart.rb
@@ -381,19 +381,6 @@ describe Rack::Multipart do
     params["files"][:tempfile].read.must_equal "contents"
   end
 
-  it "parse filename with unescaped quotes" do
-    env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_quotes))
-    params = Rack::Multipart.parse_multipart(env)
-    params["files"][:type].must_equal "application/octet-stream"
-    params["files"][:filename].must_equal "escape \"quotes"
-    params["files"][:head].must_equal "Content-Disposition: form-data; " +
-      "name=\"files\"; " +
-      "filename=\"escape \"quotes\"\r\n" +
-      "Content-Type: application/octet-stream\r\n"
-    params["files"][:name].must_equal "files"
-    params["files"][:tempfile].read.must_equal "contents"
-  end
-
   it "parse filename with escaped quotes and modification param" do
     env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_escaped_quotes_and_modification_param))
     params = Rack::Multipart.parse_multipart(env)
@@ -402,7 +389,7 @@ describe Rack::Multipart do
     params["files"][:head].must_equal "Content-Type: image/jpeg\r\n" +
       "Content-Disposition: attachment; " +
       "name=\"files\"; " +
-      "filename=\"\"human\" genome.jpeg\"; " +
+      "filename=\"\\\"human\\\" genome.jpeg\"; " +
       "modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";\r\n" +
       "Content-Description: a complete map of the human genome\r\n"
     params["files"][:name].must_equal "files"
-- 
2.36.0

