From 14686727156884605062646f38b0f5a51d3c4944 Mon Sep 17 00:00:00 2001 From: Julio Araujo Date: Thu, 14 May 2026 14:51:44 +0000 Subject: [PATCH 1/2] fix: ensures the translateMessage Meteor method validates access and type (#40528) --- .changeset/neat-trams-juggle.md | 5 ++ .../server/methods/translateMessage.ts | 21 ++++- .../tests/end-to-end/api/autotranslate.ts | 90 ++++++++++++++++++- 3 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 .changeset/neat-trams-juggle.md diff --git a/.changeset/neat-trams-juggle.md b/.changeset/neat-trams-juggle.md new file mode 100644 index 0000000000000..56f327f68cc0c --- /dev/null +++ b/.changeset/neat-trams-juggle.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Ensures the Meteor method for translateMessage validates access and types diff --git a/apps/meteor/app/autotranslate/server/methods/translateMessage.ts b/apps/meteor/app/autotranslate/server/methods/translateMessage.ts index 7c8741782647c..1c68073948f6e 100644 --- a/apps/meteor/app/autotranslate/server/methods/translateMessage.ts +++ b/apps/meteor/app/autotranslate/server/methods/translateMessage.ts @@ -1,7 +1,10 @@ import type { IMessage } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ddp-client'; +import { Messages, Rooms } from '@rocket.chat/models'; +import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; +import { canAccessRoomAsync } from '../../../authorization/server'; import { translateMessage } from '../functions/translateMessage'; declare module '@rocket.chat/ddp-client' { @@ -13,6 +16,22 @@ declare module '@rocket.chat/ddp-client' { Meteor.methods({ async 'autoTranslate.translateMessage'(message, targetLanguage) { - return translateMessage(targetLanguage, message); + const userId = Meteor.userId(); + if (!userId) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { + method: 'autoTranslate.translateMessage', + }); + } + check(message?._id, String); + check(targetLanguage, String); + const msg = await Messages.findOneById(message._id); + if (!msg) { + throw new Meteor.Error('error-message-not-found', 'Message not found'); + } + const room = await Rooms.findOneById(msg.rid); + if (!room || !(await canAccessRoomAsync(room, { _id: userId }))) { + throw new Meteor.Error('error-not-allowed', 'Not allowed'); + } + return translateMessage(targetLanguage, msg); }, }); diff --git a/apps/meteor/tests/end-to-end/api/autotranslate.ts b/apps/meteor/tests/end-to-end/api/autotranslate.ts index 3b5e03e8016f5..aa133665486c6 100644 --- a/apps/meteor/tests/end-to-end/api/autotranslate.ts +++ b/apps/meteor/tests/end-to-end/api/autotranslate.ts @@ -3,7 +3,7 @@ import type { IMessage, IRoom, IUser } from '@rocket.chat/core-typings'; import { expect } from 'chai'; import { before, describe, after, it } from 'mocha'; -import { getCredentials, api, request, credentials } from '../../data/api-data'; +import { getCredentials, api, request, credentials, methodCall } from '../../data/api-data'; import { sendSimpleMessage } from '../../data/chat.helper'; import { updatePermission, updateSetting } from '../../data/permissions.helper'; import { createRoom, deleteRoom } from '../../data/rooms.helper'; @@ -376,6 +376,94 @@ describe('AutoTranslate', () => { }); }); + describe('[autoTranslate.translateMessage method]', () => { + let userA: TestUser; + let userB: TestUser; + let credA: Credentials; + let credB: Credentials; + let privateRoom: IRoom; + let privateMessage: IMessage; + + before(async () => { + await updateSetting('AutoTranslate_Enabled', true); + + userA = await createUser(); + userB = await createUser(); + + credA = await login(userA.username, password); + credB = await login(userB.username, password); + + privateRoom = ( + await createRoom({ + type: 'p', + name: `test-autotranslate-method-${Date.now()}`, + credentials: credA, + }) + ).body.group; + + const msgRes = await sendSimpleMessage({ + roomId: privateRoom._id, + text: 'Isso é um teste', + userCredentials: credA, + }); + privateMessage = msgRes.body.message; + }); + + after(async () => { + await Promise.all([ + updateSetting('AutoTranslate_Enabled', false), + deleteUser(userA), + deleteUser(userB), + deleteRoom({ type: 'p', roomId: privateRoom._id }), + ]); + }); + + it('should fail when messageId is not a string', (done) => { + void request + .post(methodCall('autoTranslate.translateMessage')) + .set(credA) + .send({ + message: JSON.stringify({ + msg: 'method', + id: 'id', + method: 'autoTranslate.translateMessage', + params: [{ _id: { $gt: '' } }, 'en'], + }), + }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.a.property('success', false); + const parsedBody = JSON.parse(res.body.message); + expect(parsedBody).to.have.a.property('error'); + }) + .end(done); + }); + + it('should return error-not-allowed when the caller is not a member of the room', (done) => { + void request + .post(methodCall('autoTranslate.translateMessage')) + .set(credB) + .send({ + message: JSON.stringify({ + msg: 'method', + id: 'id', + method: 'autoTranslate.translateMessage', + params: [{ _id: privateMessage._id }, 'en'], + }), + }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.a.property('success', false); + const parsedBody = JSON.parse(res.body.message); + expect(parsedBody).to.have.a.property('error'); + expect(parsedBody.error).to.have.a.property('error', 'error-not-allowed'); + }) + .end(done); + }); + }); + describe('Autoenable setting', () => { let userA: TestUser; let userB: TestUser; From 54bec7ec3da1256f851fa5a95dcc2964d9bf3def Mon Sep 17 00:00:00 2001 From: Julio Araujo Date: Fri, 15 May 2026 08:33:25 +0200 Subject: [PATCH 2/2] Fix tests --- apps/meteor/tests/end-to-end/api/autotranslate.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/meteor/tests/end-to-end/api/autotranslate.ts b/apps/meteor/tests/end-to-end/api/autotranslate.ts index aa133665486c6..ffe873b171a72 100644 --- a/apps/meteor/tests/end-to-end/api/autotranslate.ts +++ b/apps/meteor/tests/end-to-end/api/autotranslate.ts @@ -431,9 +431,9 @@ describe('AutoTranslate', () => { }), }) .expect('Content-Type', 'application/json') - .expect(400) + .expect(200) .expect((res) => { - expect(res.body).to.have.a.property('success', false); + expect(res.body).to.have.a.property('success', true); const parsedBody = JSON.parse(res.body.message); expect(parsedBody).to.have.a.property('error'); }) @@ -453,9 +453,9 @@ describe('AutoTranslate', () => { }), }) .expect('Content-Type', 'application/json') - .expect(400) + .expect(200) .expect((res) => { - expect(res.body).to.have.a.property('success', false); + expect(res.body).to.have.a.property('success', true); const parsedBody = JSON.parse(res.body.message); expect(parsedBody).to.have.a.property('error'); expect(parsedBody.error).to.have.a.property('error', 'error-not-allowed');