CVE-2021-43840 (published 2021-12-17T19:15Z)

message_bus is a messaging bus for Ruby processes and web clients. In versions prior to 3.3.7 users who deployed message bus with diagnostics features enabled (default off) are vulnerable to a path traversal bug, which could lead to disclosure of secret information on a machine if an unintended user were to gain access to the diagnostic route. The impact is also greater if there is no proxy for your web application as the number of steps up the directories is not bounded. For deployments which uses a proxy, the impact varies. For example, If a request goes through a proxy like Nginx with `merge_slashes` enabled, the number of steps up the directories that can be read is limited to 3 levels. This issue has been patched in version 3.3.7. Users unable to upgrade should ensure that MessageBus::Diagnostics is disabled.

Severity: LOW
CVSS v2 base score: 3.5 (AV:N/AC:M/Au:S/C:P/I:N/A:N)
CVSS v3 base score: 6.5, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N)

Weakness information:
cwe_id: CWE-22
cwe_name: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
extended_description: 
            Many file operations are intended to take place within a restricted directory. By using special elements such as ".." and "/" separators, attackers can escape outside of the restricted location to access files or directories that are elsewhere on the system. One of the most common special elements is the "../" sequence, which in most modern operating systems is interpreted as the parent directory of the current location. This is referred to as relative path traversal. Path traversal also covers the use of absolute pathnames such as "/usr/local/bin" to access unexpected files. This is referred to as absolute path traversal.
         
         

Fix information:
repo_url: https://github.com/discourse/message_bus
commit_details: repo_url: https://github.com/discourse/message_bus
author: Alan Guo Xiang Tan
author_date: 2021-12-15 12:03:03+08:00
msg: SECURITY: Fix path traversal on diagnostics route.
repository: repo_url: https://github.com/discourse/message_bus
repo_name: discourse/message_bus
description: A reliable and robust messaging bus for Ruby and Rack
repo_language: Ruby
owner: discourse
file_changes: filename: CHANGELOG
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,7 @@
 Unreleased
 
   - FIX: Prevent simple polling from clobbering the session
+  - SECURITY: Fix path traversal on diagnostics route.
 
 31-05-2021
 

programming_language: Markdown
filename: diagnostics.rb
change_type: ModificationType.MODIFY
diff: @@ -13,6 +13,15 @@ class MessageBus::Rack::Diagnostics
     @bus = config[:message_bus] || MessageBus
   end
 
+  JS_ASSETS = %w{
+    jquery-1.8.2.js
+    react.js
+    react-dom.js
+    babel.min.js
+    message-bus.js
+    application.jsx
+  }
+
   # Process an HTTP request from a subscriber client
   # @param [Rack::Request::Env] env the request environment
   def call(env)
@@ -39,7 +48,8 @@ class MessageBus::Rack::Diagnostics
     end
 
     asset = route.split('/assets/')[1]
-    if asset && !asset !~ /\//
+
+    if asset && JS_ASSETS.include?(asset)
       content = asset_contents(asset)
       return [200, { 'Content-Type' => 'application/javascript;charset=UTF-8' }, [content]]
     end
@@ -74,6 +84,23 @@ class MessageBus::Rack::Diagnostics
     File.expand_path("../../../../assets/#{asset}", __FILE__)
   end
 
+  def script_tags
+    tags = []
+
+    JS_ASSETS.each do |asset|
+      type =
+        if asset.end_with?('.js')
+          'text/javascript'
+        elsif asset.end_with?('.jsx')
+          'text/jsx'
+        end
+
+      tags << js_asset(asset, type)
+    end
+
+    tags.join("\n")
+  end
+
   def index
     html = <<~HTML
       <!DOCTYPE html>
@@ -82,12 +109,8 @@ class MessageBus::Rack::Diagnostics
         </head>
         <body>
           <div id="app"></div>
-          #{js_asset "jquery-1.8.2.js"}
-          #{js_asset "react.js"}
-          #{js_asset "react-dom.js"}
-          #{js_asset "babel.min.js"}
-          #{js_asset "message-bus.js"}
-          #{js_asset "application.jsx", "text/jsx"}
+
+          #{script_tags}
         </body>
       </html>
     HTML

programming_language: Ruby
filename: middleware_spec.rb
change_type: ModificationType.MODIFY
diff: @@ -142,13 +142,12 @@ describe MessageBus::Rack::Middleware do
   end
 
   describe "diagnostics" do
-    it "should return a 403 if a user attempts to get at the _diagnostics path" do
+    it "should return a 403 if an unauthorized user attempts to get at the _diagnostics path" do
       get "/message-bus/_diagnostics"
       last_response.status.must_equal 403
     end
 
     it "should get a 200 with html for an authorized user" do
-
       def @bus.is_admin_lookup
         proc { |_| true }
       end
@@ -171,7 +170,6 @@ describe MessageBus::Rack::Middleware do
     end
 
     it "should get the script it asks for" do
-
       def @bus.is_admin_lookup
         proc { |_| true }
       end
@@ -180,6 +178,15 @@ describe MessageBus::Rack::Middleware do
       last_response.status.must_equal 200
       last_response.content_type.must_equal "application/javascript;charset=UTF-8"
     end
+
+    it "should return 404 for invalid assets path" do
+      def @bus.is_admin_lookup
+        proc { |_| true }
+      end
+
+      get "/message-bus/_diagnostics/assets/../Gemfile"
+      last_response.status.must_equal 404
+    end
   end
 
   describe "polling" do

programming_language: Ruby