[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

ruby-rack-cors / CVE-2019-18978



Attached is my proposed patch for Jessie. This had to be adapted from
the upstream patch, however I think I have understood the intentions
clearly.

The Rack::Utils.clean_path_info and Rack::Utils.unescape_path functions
required by the upstream patch are not available in Jessie, so I copied
these from upstream:

https://github.com/rack/rack/blob/master/lib/rack/utils.rb

The supplied test case fails as expected without the required change and
succeeds as expected with the required change.

I just noticed I still need to update the changelog entry before
uploading.


diff -Nru ruby-rack-cors-0.2.9/debian/changelog ruby-rack-cors-0.2.9/debian/changelog
--- ruby-rack-cors-0.2.9/debian/changelog	2014-04-24 23:53:38.000000000 +1000
+++ ruby-rack-cors-0.2.9/debian/changelog	2020-02-04 17:25:44.000000000 +1100
@@ -1,3 +1,9 @@
+ruby-rack-cors (0.2.9-1+deb8u1) UNRELEASED; urgency=high
+
+  * Non-maintainer upload by the LTS Team.
+
+ -- Brian May <bam@debian.org>  Tue, 04 Feb 2020 17:25:44 +1100
+
 ruby-rack-cors (0.2.9-1) unstable; urgency=low
 
   * New upstream release
diff -Nru ruby-rack-cors-0.2.9/debian/patches/CVE-2019-18978.patch ruby-rack-cors-0.2.9/debian/patches/CVE-2019-18978.patch
--- ruby-rack-cors-0.2.9/debian/patches/CVE-2019-18978.patch	1970-01-01 10:00:00.000000000 +1000
+++ ruby-rack-cors-0.2.9/debian/patches/CVE-2019-18978.patch	2020-02-04 17:25:44.000000000 +1100
@@ -0,0 +1,104 @@
+--- a/test/unit/cors_test.rb
++++ b/test/unit/cors_test.rb
+@@ -160,6 +160,12 @@
+       assert_cors_success
+       assert_not_nil last_response.headers['Content-Type']
+     end
++
++    should "decode URL and resolve paths before resource matching" do
++      header 'Origin', 'http://localhost:3000'
++      get '/public/a/..%2F..%2Fprivate/stuff'
++      assert_cors_failure
++    end
+   end
+ 
+   protected
+--- a/test/unit/test.ru
++++ b/test/unit/test.ru
+@@ -28,6 +28,7 @@
+   allow do
+     origins '*'
+     resource '/public'
++    resource '/public/*'
+     resource '/public_without_credentials', :credentials => false
+   end
+ 
+--- a/lib/rack/cors.rb
++++ b/lib/rack/cors.rb
+@@ -30,17 +30,20 @@
+       env['HTTP_ORIGIN'] = 'file://' if env['HTTP_ORIGIN'] == 'null'
+       env['HTTP_ORIGIN'] ||= env['HTTP_X_ORIGIN']
+ 
++      path = evaluate_path(env)
++
+       cors_headers = nil
+       if env['HTTP_ORIGIN']
+         debug(env) do
+           [ 'Incoming Headers:',
+             "  Origin: #{env['HTTP_ORIGIN']}",
++            "  Path-Info: #{path}",
+             "  Access-Control-Request-Method: #{env['HTTP_ACCESS_CONTROL_REQUEST_METHOD']}",
+             "  Access-Control-Request-Headers: #{env['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}"
+             ].join("\n")
+         end
+         if env['REQUEST_METHOD'] == 'OPTIONS' and env['HTTP_ACCESS_CONTROL_REQUEST_METHOD']
+-          if headers = process_preflight(env)
++          if headers = process_preflight(env, path)
+             debug(env) do
+               "Preflight Headers:\n" +
+                   headers.collect{|kv| "  #{kv.join(': ')}"}.join("\n")
+@@ -48,7 +51,7 @@
+             return [200, headers, []]
+           end
+         else
+-          cors_headers = process_cors(env)
++          cors_headers = process_cors(env, path)
+         end
+       end
+       status, headers, body = @app.call env
+@@ -74,17 +77,41 @@
+         end
+       end
+ 
++      PATH_SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
++
++      def clean_path_info(path_info)
++        parts = path_info.split PATH_SEPS
++
++        clean = []
++
++        parts.each do |part|
++          next if part.empty? || part == '.'
++          part == '..' ? clean.pop : clean << part
++        end
++
++        clean_path = clean.join(::File::SEPARATOR)
++        clean_path.prepend("/") if parts.empty? || parts.first.empty?
++        clean_path
++      end
++
++      def evaluate_path(env)
++        path = env['PATH_INFO']
++#       path = Rack::Utils.clean_path_info(Rack::Utils.unescape_path(path)) if path
++        path = clean_path_info(::URI::DEFAULT_PARSER.unescape(path)) if path
++        path
++      end
++
+       def all_resources
+         @all_resources ||= []
+       end
+ 
+-      def process_preflight(env)
+-        resource = find_resource(env['HTTP_ORIGIN'], env['PATH_INFO'],env)
++      def process_preflight(env, path)
++        resource = find_resource(env['HTTP_ORIGIN'], path,env)
+         resource && resource.process_preflight(env)
+       end
+ 
+-      def process_cors(env)
+-        resource = find_resource(env['HTTP_ORIGIN'], env['PATH_INFO'],env)
++      def process_cors(env, path)
++        resource = find_resource(env['HTTP_ORIGIN'], path,env)
+         resource.to_headers(env) if resource
+       end
+ 
diff -Nru ruby-rack-cors-0.2.9/debian/patches/series ruby-rack-cors-0.2.9/debian/patches/series
--- ruby-rack-cors-0.2.9/debian/patches/series	1970-01-01 10:00:00.000000000 +1000
+++ ruby-rack-cors-0.2.9/debian/patches/series	2020-02-04 17:20:41.000000000 +1100
@@ -0,0 +1 @@
+CVE-2019-18978.patch

-- 
Brian May <brian@linuxpenguins.xyz>
https://linuxpenguins.xyz/brian/


Reply to: