CVE-2022-41921 (Ruby, published 2022-11-28T15:15Z)
Description: value: Discourse is an open-source discussion platform. Prior to version 2.9.0.beta13, users can post chat messages of an unlimited length, which can cause a denial of service for other users when posting huge amounts of text. Users should upgrade to version 2.9.0.beta13, where a limit has been introduced. No known workarounds are available.
Severity: nan (CVSS v3 base score: 4.3)
Weakness: CWE-770 \u2014 Allocation of Resources Without Limits or Throttling
The product allocates a reusable resource or group of resources on behalf of an actor without imposing any restrictions on the size or number of resources that can be allocated, in violation of the intended security policy for that actor.
Repository: https://github.com/discourse/discourse
Files changed:
plugins/chat/app/controllers/incoming_chat_webhooks_controller.rb
plugins/chat/app/models/chat_message.rb
plugins/chat/config/locales/server.en.yml
plugins/chat/config/settings.yml
plugins/chat/db/post_migrate/20221117052348_truncate_chat_messages_over_max_length.rb
plugins/chat/spec/components/chat_message_creator_spec.rb
plugins/chat/spec/components/chat_message_updater_spec.rb
plugins/chat/spec/requests/incoming_chat_webhooks_controller_spec.rb
Commit message: SECURITY: Limit chat message char length (#19207)

Only allow maximum of 6000 characters for chat messages when they
are created or edited. A hidden setting can control this limit,
6000 is the default.

There is also a migration here to truncate any existing messages to
6000 characters if the message is already over that and if the
chat_messages table exists. We also set cooked_version to NULL
for those messages so we can identify them for rebake.

Vulnerable code:
  WEBHOOK_MAX_MESSAGE_LENGTH = 2000
    return if message.length <= WEBHOOK_MAX_MESSAGE_LENGTH
            "Body cannot be over #{WEBHOOK_MAX_MESSAGE_LENGTH} characters",
    updater = Chat::ChatMessageUpdater.update(
      guardian: Guardian.new(Fabricate(:user)),
      chat_message: chat_message,
      new_content: new_message,
    )
    events = DiscourseEvent.track_events {
      Chat::ChatMessageUpdater.update(
        guardian: guardian,
        chat_message: chat_message,
        new_content: "Change to this!",
      )
    }
    it "errors when the body is over WEBHOOK_MAX_MESSAGE_LENGTH characters" do
             text: "$" * (Chat::IncomingChatWebhooksController::WEBHOOK_MAX_MESSAGE_LENGTH + 1),

Fixed code:
    return if message.length <= SiteSetting.chat_maximum_message_length
            "Body cannot be over #{SiteSetting.chat_maximum_message_length} characters",

    if message_too_long?
      self.errors.add(
        :base,
        I18n.t(
          "chat.errors.message_too_long",
          maximum: SiteSetting.chat_maximum_message_length,
        ),
      )
    end
  def message_too_long?
    message.length > SiteSetting.chat_maximum_message_length
  end

      message_too_long: "Message is too long, messages must be a maximum of %{maximum} characters."
  chat_maximum_message_length:
    type: integer
    default: 6000
    client: true
    hidden: true
    min: 100
    max: 12000
# frozen_string_literal: true

class TruncateChatMessagesOverMaxLength < ActiveRecord::Migration[7.0]
  def up
    if table_exists?(:chat_messages)
      # 6000 is the default of the chat_maximum_message_length
      # site setting, its safe to do this because this will be
      # run the first time the setting is introduced.
      execute <<~SQL
        UPDATE chat_messages
        SET message = LEFT(message, 6000), cooked_version = NULL
        WHERE LENGTH(message) > 6000
      SQL
    end
  end

  def down
    raise ActiveRecord::IrreversibleMigration
  end
end
    it "errors when length is greater than `chat_maximum_message_length`" do
      SiteSetting.chat_maximum_message_length = 100
      creator =
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user1,
          content: "a really long and in depth message that is just too detailed" * 100,
        )
      expect(creator.failed?).to eq(true)
      expect(creator.error.message).to match(
        I18n.t(
          "chat.errors.message_too_long",
          { maximum: SiteSetting.chat_maximum_message_length },
        ),
      )
    end

  it "errors when length is greater than `chat_maximum_message_length`" do
    SiteSetting.chat_maximum_message_length = 100
    og_message = "This won't be changed!"
    chat_message = create_chat_message(user1, og_message, public_chat_channel)
    new_message = "2 long" * 100

    updater =
      Chat::ChatMessageUpdater.update(
        guardian: guardian,
        chat_message: chat_message,
        new_content: new_message,
      )
    expect(updater.failed?).to eq(true)
    expect(updater.error.message).to match(
      I18n.t(
        "chat.errors.message_too_long",
        { maximum: SiteSetting.chat_maximum_message_length },
      ),
    )
    expect(chat_message.reload.message).to eq(og_message)
  end

    updater =
      Chat::ChatMessageUpdater.update(
        guardian: Guardian.new(Fabricate(:user)),
        chat_message: chat_message,
        new_content: new_message,
      )
    events =
      DiscourseEvent.track_events do
        Chat::ChatMessageUpdater.update(
          guardian: guardian,
          chat_message: chat_message,
          new_content: "Change to this!",
        )
      end
    it "errors when the body is over chat_maximum_message_length characters" do
             text: "$" * (SiteSetting.chat_maximum_message_length + 1),