CVE-2022-36068: Missing Authorization (CWE-862)
value: Discourse is an open source discussion platform. In versions prior to 2.8.9 on the `stable` branch and prior to 2.9.0.beta10 on the `beta` and `tests-passed` branches, a moderator can create new and edit existing themes by using the API when they should not be able to do so. The problem is patched in version 2.8.9 on the `stable` branch and version 2.9.0.beta10 on the `beta` and `tests-passed` branches. There are no known workarounds.
CWE description: The product does not perform an authorization check when an actor attempts to access a resource or perform an action.
CVSS v3 base score: 4.3
Published: 2022-09-29T20:15Z
Repository: https://github.com/discourse/discourse
Language: Ruby
Files changed: app/controllers/admin/themes_controller.rb, config/routes.rb, spec/requests/admin/themes_controller_spec.rb
Commit date: 2022-09-29 20:00:20 +0200
Commit message: SECURITY: moderator shouldn't be able to import a theme via API (#18418)

* SECURITY: moderator shouldn't be able to import a theme via API.
* DEV: apply `AdminConstraint` for all the "themes" routes.

Co-authored-by: Vinoth Kannan <svkn.87@gmail.com>
Diff: diff --git a/app/controllers/admin/themes_controller.rb b/app/controllers/admin/themes_controller.rb
index c22228ab6fef..49da3418fac1 100644
--- a/app/controllers/admin/themes_controller.rb
+++ b/app/controllers/admin/themes_controller.rb
@@ -3,10 +3,11 @@
 require 'base64'
 
 class Admin::ThemesController < Admin::AdminController
 
   skip_before_action :check_xhr, only: [:show, :preview, :export]
+  before_action :ensure_admin
 
   def preview
     theme = Theme.find_by(id: params[:id])
     raise Discourse::InvalidParameters.new(:id) unless theme
 
diff --git a/config/routes.rb b/config/routes.rb
index 7bd9ae757d3e..0b1931aaeb91 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -204,17 +204,21 @@ Discourse::Application.routes.draw do
       get "customize/colors/:id" => "color_schemes#index", constraints: AdminConstraint.new
       get "customize/permalinks" => "permalinks#index", constraints: AdminConstraint.new
       get "customize/embedding" => "embedding#show", constraints: AdminConstraint.new
       put "customize/embedding" => "embedding#update", constraints: AdminConstraint.new
 
-      resources :themes, constraints: AdminConstraint.new
-
-      post "themes/import" => "themes#import"
-      post "themes/upload_asset" => "themes#upload_asset"
-      post "themes/generate_key_pair" => "themes#generate_key_pair"
-      get "themes/:id/preview" => "themes#preview"
-      put "themes/:id/setting" => "themes#update_single_setting"
+      resources :themes, constraints: AdminConstraint.new do
+        member do
+          get "preview" => "themes#preview"
+          put "setting" => "themes#update_single_setting"
+        end
+        collection do
+          post "import" => "themes#import"
+          post "upload_asset" => "themes#upload_asset"
+          post "generate_key_pair" => "themes#generate_key_pair"
+        end
+      end
 
       scope "/customize", constraints: AdminConstraint.new do
         resources :user_fields, constraints: AdminConstraint.new
         resources :emojis, constraints: AdminConstraint.new
 
diff --git a/spec/requests/admin/themes_controller_spec.rb b/spec/requests/admin/themes_controller_spec.rb
index 70e06609e4b5..1fc23f2abb9a 100644
--- a/spec/requests/admin/themes_controller_spec.rb
+++ b/spec/requests/admin/themes_controller_spec.rb
@@ -158,10 +158,17 @@ RSpec.describe Admin::ThemesController do
       expect(RemoteTheme.last.private_key).to eq('rsa private key')
 
       expect(response.status).to eq(201)
     end
 
+    it 'should not be able to import a theme by moderator' do
+      sign_in(Fabricate(:moderator))
+
+      post "/admin/themes/import.json", params: { theme: theme_json_file }
+      expect(response.status).to eq(404)
+    end
+
     it 'imports a theme' do
       post "/admin/themes/import.json", params: { theme: theme_json_file }
       expect(response.status).to eq(201)
 
       json = response.parsed_body

Vulnerable code:       resources :themes, constraints: AdminConstraint.new

      post "themes/import" => "themes#import"
      post "themes/upload_asset" => "themes#upload_asset"
      post "themes/generate_key_pair" => "themes#generate_key_pair"
      get "themes/:id/preview" => "themes#preview"
      put "themes/:id/setting" => "themes#update_single_setting"
Fixed code:   before_action :ensure_admin
      resources :themes, constraints: AdminConstraint.new do
        member do
          get "preview" => "themes#preview"
          put "setting" => "themes#update_single_setting"
        end
        collection do
          post "import" => "themes#import"
          post "upload_asset" => "themes#upload_asset"
          post "generate_key_pair" => "themes#generate_key_pair"
        end
      end
    it 'should not be able to import a theme by moderator' do
      sign_in(Fabricate(:moderator))

      post "/admin/themes/import.json", params: { theme: theme_json_file }
      expect(response.status).to eq(404)
    end