SECURITY: Limit chat drafts length and preloaded count (#19987)

Only allow maximum of `50_000` characters for chat drafts. A hidden `max_chat_draft_length` setting can control this limit. A migration is also provided to delete any abusive draft in the database.

The number of drafts loaded on current user has also been limited and ordered by most recent update.

Note that spec files moved are not directly related to the fix.

Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
Co-authored-by: Régis Hanol <regis@hanol.fr>
This commit is contained in:
Natalie Tay
2023-01-25 19:50:10 +08:00
committed by GitHub
parent ec2ed5b7f6
commit 5eaf080239
11 changed files with 121 additions and 4 deletions

View File

@ -126,3 +126,16 @@ Fabricator(:user_chat_channel_membership_for_dm, from: :user_chat_channel_member
desktop_notification_level 2
mobile_notification_level 2
end
Fabricator(:chat_draft) do
user
chat_channel
transient :value, "chat draft message"
transient :uploads, []
transient :reply_to_msg
data do |attrs|
{ value: attrs[:value], replyToMsg: attrs[:reply_to_msg], uploads: attrs[:uploads] }.to_json
end
end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
RSpec.describe ChatDraft do
before { SiteSetting.max_chat_draft_length = 100 }
it "errors when data.value is greater than `max_chat_draft_length`" do
draft =
described_class.create(
user_id: Fabricate(:user).id,
chat_channel_id: Fabricate(:chat_channel).id,
data: { value: "A" * (SiteSetting.max_chat_draft_length + 1) }.to_json,
)
expect(draft.errors.full_messages).to eq(
[I18n.t("chat.errors.draft_too_long", { maximum: SiteSetting.max_chat_draft_length })],
)
end
end

View File

@ -1286,6 +1286,19 @@ RSpec.describe Chat::ChatController do
post "/chat/drafts.json", params: { chat_channel_id: dm_channel.id, data: "{}" }
}.to change { ChatDraft.count }.by(1)
end
it "cannot create a too long chat draft" do
SiteSetting.max_chat_draft_length = 100
post "/chat/drafts.json",
params: {
chat_channel_id: chat_channel.id,
data: { value: "a" * (SiteSetting.max_chat_draft_length + 1) }.to_json,
}
expect(response.status).to eq(422)
expect(response.parsed_body["errors"]).to eq([I18n.t("chat.errors.draft_too_long")])
end
end
describe "#message_link" do

View File

@ -0,0 +1,40 @@
# frozen_string_literal: true
RSpec.describe CurrentUserSerializer do
fab!(:current_user) { Fabricate(:user) }
let(:serializer) do
described_class.new(current_user, scope: Guardian.new(current_user), root: false)
end
before do
SiteSetting.chat_enabled = true
SiteSetting.chat_allowed_groups = Group::AUTO_GROUPS[:everyone]
current_user.user_option.update(chat_enabled: true)
end
describe "#chat_drafts" do
context "when user can't chat" do
before { SiteSetting.chat_allowed_groups = Group::AUTO_GROUPS[:staff] }
it "is not present" do
expect(serializer.as_json[:chat_drafts]).to be_blank
end
end
it "is ordered by most recent drafts" do
Fabricate(:chat_draft, user: current_user, value: "second draft")
Fabricate(:chat_draft, user: current_user, value: "first draft")
values =
serializer.as_json[:chat_drafts].map { |draft| MultiJson.load(draft[:data])["value"] }
expect(values).to eq(["first draft", "second draft"])
end
it "limits the numbers of drafts" do
21.times { Fabricate(:chat_draft, user: current_user) }
expect(serializer.as_json[:chat_drafts].length).to eq(20)
end
end
end