CVE-2022-21684 (published 2022-01-13T21:15Z)

Discourse is an open source discussion platform. Versions prior to 2.7.13 in `stable`, 2.8.0.beta11 in `beta`, and 2.8.0.beta11 in `tests-passed` allow some users to log in to a community before they should be able to do so. A user invited via email to a forum with `must_approve_users` enabled is going to be automatically logged in, bypassing the check that does not allow unapproved users to sign in. They will be able to do everything an approved user can do. If they logout, they cannot log back in. This issue is patched in the `stable` version 2.7.13, `beta` version 2.8.0.beta11, and `tests-passed` version 2.8.0.beta11. One may disable invites as a workaround. Administrators can increase `min_trust_level_to_allow_invite` to reduce the attack surface to more trusted users.

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

Weakness information:
cwe_id: CWE-287
cwe_name: Improper Authentication
description: When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.
extended_description: 
            
			
         
         

Fix information:
repo_url: https://github.com/discourse/discourse
commit_details: repo_url: https://github.com/discourse/discourse
author: Dan Ungureanu
author_date: 2022-01-12 22:24:54+02:00
msg: SECURITY: Do not sign in unapproved users (#15552)
repository: repo_url: https://github.com/discourse/discourse
repo_name: discourse/discourse
description: A platform for community discussion. Free, open, simple.
repo_language: Ruby
owner: discourse
file_changes: filename: invites_controller.rb
change_type: ModificationType.MODIFY
diff: @@ -298,7 +298,7 @@ class InvitesController < ApplicationController
         return render json: failed_json.merge(message: I18n.t('invite.not_found_json')), status: 404
       end
 
-      log_on_user(user) if user.active?
+      log_on_user(user) if user.active? && user.guardian.can_access_forum?
       user.update_timezone_if_missing(params[:timezone])
       post_process_invite(user)
       create_topic_invite_notifications(invite, user)
@@ -307,14 +307,19 @@ class InvitesController < ApplicationController
       response = {}
 
       if user.present?
-        if user.active?
+        if user.active? && user.guardian.can_access_forum?
           if user.guardian.can_see?(topic)
             response[:redirect_to] = path(topic.relative_url)
           else
             response[:redirect_to] = path("/")
           end
         else
-          response[:message] = I18n.t('invite.confirm_email')
+          response[:message] = if user.active?
+            I18n.t('activation.approval_required')
+          else
+            I18n.t('invite.confirm_email')
+          end
+
           if user.guardian.can_see?(topic)
             cookies[:destination_url] = path(topic.relative_url)
           end

programming_language: Ruby
filename: invites_controller_spec.rb
change_type: ModificationType.MODIFY
diff: @@ -510,6 +510,22 @@ describe InvitesController do
         expect(response.status).to eq(412)
       end
 
+      it 'does not log in the user if they were not approved' do
+        SiteSetting.must_approve_users = true
+
+        put "/invites/show/#{invite.invite_key}.json", params: { password: SecureRandom.hex, email_token: invite.email_token }
+
+        expect(session[:current_user_id]).to eq(nil)
+        expect(response.parsed_body["message"]).to eq(I18n.t('activation.approval_required'))
+      end
+
+      it 'does not log in the user if they were not activated' do
+        put "/invites/show/#{invite.invite_key}.json", params: { password: SecureRandom.hex }
+
+        expect(session[:current_user_id]).to eq(nil)
+        expect(response.parsed_body["message"]).to eq(I18n.t('invite.confirm_email'))
+      end
+
       it 'fails when local login is disabled and no external auth is configured' do
         SiteSetting.enable_local_logins = false
 

programming_language: Ruby