From bbb1e855060b6a6885bbeb0c7c1465ab61f29e1c Mon Sep 17 00:00:00 2001
From: Aaron Patterson <tenderlove@ruby-lang.org>
Date: Thu, 26 May 2022 16:18:10 -0700
Subject: [PATCH 2/3] Escape untrusted text when logging

This fixes a shell escape issue

[CVE-2022-30123]
---
 lib/rack/common_logger.rb  |  3 +++
 lib/rack/lint.rb           |  2 +-
 test/spec_common_logger.rb | 12 ++++++++++++
 test/spec_lint.rb          |  5 +++++
 4 files changed, 21 insertions(+), 1 deletion(-)

--- a/lib/rack/common_logger.rb
+++ b/lib/rack/common_logger.rb
@@ -55,7 +55,10 @@
         length,
         Utils.clock_time - began_at ]
 
+      msg.gsub!(/[^[:print:]\n]/) { |c| "\\x#{c.ord}" }
+
       logger = @logger || env[RACK_ERRORS]
+
       # Standard library logger doesn't support write but it supports << which actually
       # calls to write on the log device without formatting
       if logger.respond_to?(:write)
--- a/lib/rack/lint.rb
+++ b/lib/rack/lint.rb
@@ -296,7 +296,7 @@
       check_hijack env
 
       ## * The <tt>REQUEST_METHOD</tt> must be a valid token.
-      assert("REQUEST_METHOD unknown: #{env[REQUEST_METHOD]}") {
+      assert("REQUEST_METHOD unknown: #{env[REQUEST_METHOD].dump}") {
         env[REQUEST_METHOD] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/
       }
 
--- a/test/spec_common_logger.rb
+++ b/test/spec_common_logger.rb
@@ -23,6 +23,10 @@
     [200,
      { "Content-Type" => "text/html", "Content-Length" => "0" },
      []]}
+  app_without_lint = lambda { |env|
+    [200,
+     { "content-type" => "text/html", "content-length" => length.to_s },
+     [obj]]}
 
   it "log to rack.errors by default" do
     res = Rack::MockRequest.new(Rack::CommonLogger.new(app)).get("/")
@@ -87,6 +91,14 @@
     (0..1).must_include duration.to_f
   end
 
+  it "escapes non printable characters except newline" do
+    logdev = StringIO.new
+    log = Logger.new(logdev)
+    Rack::MockRequest.new(Rack::CommonLogger.new(app_without_lint, log)).request("GET\b", "/hello")
+
+    logdev.string.must_match(/GET\\x8 \/hello/)
+  end
+
   def length
     123
   end
--- a/test/spec_lint.rb
+++ b/test/spec_lint.rb
@@ -99,6 +99,11 @@
       message.must_match(/REQUEST_METHOD/)
 
     lambda {
+      Rack::Lint.new(nil).call(env("REQUEST_METHOD" => "OOPS?\b!"))
+    }.must_raise(Rack::Lint::LintError).
+      message.must_match(/OOPS\?\\/)
+
+    lambda {
       Rack::Lint.new(nil).call(env("SCRIPT_NAME" => "howdy"))
     }.must_raise(Rack::Lint::LintError).
       message.must_match(/must start with/)
