diff --git a/.changeset/orange-mugs-relax.md b/.changeset/orange-mugs-relax.md new file mode 100644 index 0000000000000..5da0fb6826e56 --- /dev/null +++ b/.changeset/orange-mugs-relax.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixes an issue where incoming webhook messages did not respect the maximum allowed characters per message. diff --git a/apps/meteor/app/lib/server/functions/processWebhookMessage.ts b/apps/meteor/app/lib/server/functions/processWebhookMessage.ts index 55ab99d89f9d8..1fe93e0b663a8 100644 --- a/apps/meteor/app/lib/server/functions/processWebhookMessage.ts +++ b/apps/meteor/app/lib/server/functions/processWebhookMessage.ts @@ -9,6 +9,7 @@ import { ensureArray } from '../../../../lib/utils/arrayUtils'; import { trim } from '../../../../lib/utils/stringUtils'; import { SystemLogger } from '../../../../server/lib/logger/system'; import { validateRoomMessagePermissionsAsync } from '../../../authorization/server/functions/canSendMessage'; +import { settings } from '../../../settings/server'; type Payload = { channel?: string | string[]; @@ -114,6 +115,12 @@ export const processWebhookMessage = async function ( customFields: messageObj.customFields, }; + if (message.msg) { + if (message.msg.length > (settings.get('Message_MaxAllowedSize') ?? 0)) { + throw Error('error-message-size-exceeded'); + } + } + if (!_.isEmpty(messageObj.icon_url) || !_.isEmpty(messageObj.avatar)) { message.avatar = messageObj.icon_url || messageObj.avatar; } else if (!_.isEmpty(messageObj.icon_emoji) || !_.isEmpty(messageObj.emoji)) { diff --git a/apps/meteor/app/lib/server/functions/sendMessage.ts b/apps/meteor/app/lib/server/functions/sendMessage.ts index 599e75987eda4..d41e6d3e49a24 100644 --- a/apps/meteor/app/lib/server/functions/sendMessage.ts +++ b/apps/meteor/app/lib/server/functions/sendMessage.ts @@ -213,7 +213,9 @@ export function prepareMessageObject( } /** - * Validates and sends the message object. + * Validates and sends the message object. This function does not verify the Message_MaxAllowedSize settings. + * Caller of the function should verify the Message_MaxAllowedSize if needed. + * There might be same use cases which needs to override this setting. Example - sending error logs. */ export const sendMessage = async function (user: any, message: any, room: any, upsert = false, previewUrls?: string[]) { if (!user || !message || !room._id) { diff --git a/apps/meteor/tests/end-to-end/api/incoming-integrations.ts b/apps/meteor/tests/end-to-end/api/incoming-integrations.ts index 3df8b8df3ba60..4e689d5e4efb0 100644 --- a/apps/meteor/tests/end-to-end/api/incoming-integrations.ts +++ b/apps/meteor/tests/end-to-end/api/incoming-integrations.ts @@ -7,7 +7,7 @@ import { after, before, describe, it } from 'mocha'; import { getCredentials, api, request, credentials } from '../../data/api-data'; import { createIntegration, removeIntegration } from '../../data/integration.helper'; -import { updatePermission } from '../../data/permissions.helper'; +import { updatePermission, updateSetting } from '../../data/permissions.helper'; import { createRoom, deleteRoom } from '../../data/rooms.helper'; import { createTeam, deleteTeam } from '../../data/teams.helper'; import { password } from '../../data/user'; @@ -1202,5 +1202,67 @@ describe('[Incoming Integrations]', () => { expect((res.body.members as AtLeast[]).find((m) => m._id === nonMemberUser._id)).not.to.be.undefined; }); }); + + describe('Message Settings', async () => { + const maxSize = 5000; + before(() => updateSetting('Message_MaxAllowedSize', maxSize)); + after(() => updateSetting('Message_MaxAllowedSize', maxSize)); + + it('should not send a message if message size is greater than the Message_MaxAllowedSize', async () => { + const largeMesssage = Array.from({ length: maxSize + 1 }) + .map(() => 'A') + .join(''); + await request + .post(`/hooks/${integration4._id}/${integration4.token}`) + .send({ + text: largeMesssage, + }) + .expect(400) + .expect((res) => { + expect(res.body.error).to.be.equal('error-message-size-exceeded'); + }); + await request + .get(api('channels.messages')) + .set(credentials) + .query({ + roomId: publicRoom._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('messages').and.to.be.an('array'); + expect((res.body.messages as IMessage[]).find((m) => m.msg === largeMesssage)).to.be.undefined; + }); + }); + + it('should send a message if message size is less than the Message_MaxAllowedSize', async () => { + const smallerMessage = Array.from({ length: maxSize - 1 }) + .map(() => 'A') + .join(''); + await request + .post(`/hooks/${integration4._id}/${integration4.token}`) + .send({ + text: smallerMessage, + }) + .expect(200) + .expect((res) => { + expect(res.body.success).to.be.equal(true); + }); + await request + .get(api('channels.messages')) + .set(credentials) + .query({ + roomId: publicRoom._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('messages').and.to.be.an('array'); + expect((res.body.messages as IMessage[]).find((m) => m.msg === smallerMessage)).to.not.be.undefined; + }); + }); + }); }); });