CVE-2023-30611 (Ruby, published 2023-04-19T18:15Z)
Description: value: Discourse-reactions is a plugin that allows user to add their reactions to the post in the Discourse messaging platform. In affected versions data about what reactions were performed on a post in a private topic could be leaked. This issue has been addressed in version 0.3. Users are advised to upgrade. Users unable to upgrade should disable the discourse-reactions plugin to fully mitigate the issue.
Severity: nan (CVSS v3 base score: 5.3)
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/discourse/discourse-reactions
Files changed:
app/controllers/discourse_reactions/custom_reactions_controller.rb
plugin.rb
spec/requests/custom_reactions_controller_spec.rb
Commit message: SECURITY: Publish reactions based on topic permissions (#218)

Only show post reactions to users that have permission to see the
post.

Vulnerable code:
    post = Post.find_by(id: id)
    post = Post.find(params[:post_id])
    MessageBus.publish(
      "/topic/#{post.topic.id}/reactions",
      post_id: post.id,
      reactions: [reaction, previous_reaction].compact.uniq,
    )
# version: 0.2
    it "gives 400 ERROR when the post_id OR reaction_value is invalid" do
      expect(response.status).to eq(400)
      expect(response.status).to eq(400)

Fixed code:
    post = fetch_post_from_params
    post_id = params[:post_id] || params[:id]
    post = Post.find(post_id)
    message = { post_id: post.id, reactions: [reaction, previous_reaction].compact.uniq }

    opts = {}
    secure_audience = post.topic.secure_audience_publish_messages
    opts = secure_audience if secure_audience[:user_ids] != [] && secure_audience[:group_ids] != []

    MessageBus.publish("/topic/#{post.topic.id}/reactions", message, opts)
# version: 0.3
  fab!(:admin) { Fabricate(:admin) }
  fab!(:private_topic) { Fabricate(:private_message_topic, user: user_2, recipient: admin) }
  fab!(:private_post) { Fabricate(:post, topic: private_topic) }
  fab!(:reaction_4) { Fabricate(:reaction, post: private_post, reaction_value: "hugs") }
  fab!(:reaction_user_5) do
    Fabricate(:reaction_user, reaction: reaction_4, user: admin, post: private_post)
  end
    let(:api_key) { Fabricate(:api_key, user: admin, created_by: admin) }
    it "publishes MessageBus messages securely" do
      sign_in(user_1)
      messages =
        MessageBus.track_publish("/topic/#{private_post.topic.id}/reactions") do
          put "/discourse-reactions/posts/#{private_post.id}/custom-reactions/cry/toggle.json",
              headers: {
                "HTTP_API_KEY" => api_key.key,
                "HTTP_API_USERNAME" => api_key.user.username,
              }
        end
      user_1_messages = messages.find { |m| m.user_ids.include?(user_1.id) }
      expect(messages.count).to eq(1)
      expect(user_1_messages).to eq(nil)
    end

    it "gives 404 ERROR when the post_id OR reaction_value is invalid" do
      expect(response.status).to eq(404)
      expect(response.status).to eq(404)

    it "does not show reaction_users on PMs without permission" do
      get "/discourse-reactions/posts/#{private_post.id}/reactions-users.json"
      expect(response.status).to eq(403)
    end

    it "shows reaction_users on PMs with permission" do
      sign_in(user_2)
      get "/discourse-reactions/posts/#{private_post.id}/reactions-users.json"
      expect(response.status).to eq(200)
    end