Skip to content

Commit 3fc317e

Browse files
authored
fix(E2EE): Message quotes crashing room (RocketChat#37158)
1 parent 2ae6dff commit 3fc317e

5 files changed

Lines changed: 107 additions & 4 deletions

File tree

.changeset/fast-forks-sin.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@rocket.chat/meteor': patch
3+
---
4+
5+
Fixes crash in end-to-end encrypted rooms when sending a quote or message link referencing a message outside the room.

apps/meteor/client/lib/e2ee/rocketchat.e2e.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
import QueryString from 'querystring';
22
import URL from 'url';
33

4-
import type { IE2EEMessage, IMessage, IRoom, ISubscription, IUser, IUploadWithUser, MessageAttachment } from '@rocket.chat/core-typings';
4+
import type {
5+
IE2EEMessage,
6+
IMessage,
7+
IRoom,
8+
ISubscription,
9+
IUser,
10+
IUploadWithUser,
11+
MessageAttachment,
12+
Serialized,
13+
} from '@rocket.chat/core-typings';
514
import { isE2EEMessage, isEncryptedMessageContent } from '@rocket.chat/core-typings';
615
import { Emitter } from '@rocket.chat/emitter';
716
import { imperativeModal } from '@rocket.chat/ui-client';
@@ -773,8 +782,14 @@ class E2E extends Emitter {
773782
return;
774783
}
775784

776-
const getQuotedMessage = await sdk.rest.get('/v1/chat.getMessage', { msgId });
777-
const quotedMessage = getQuotedMessage?.message;
785+
let quotedMessage: Serialized<IMessage>;
786+
try {
787+
const getQuotedMessage = await sdk.rest.get('/v1/chat.getMessage', { msgId });
788+
quotedMessage = getQuotedMessage?.message;
789+
} catch (error) {
790+
console.error(`Error getting quoted message: ${error}`);
791+
return;
792+
}
778793

779794
if (!quotedMessage) {
780795
return;
@@ -783,7 +798,6 @@ class E2E extends Emitter {
783798
const decryptedQuoteMessage = await this.decryptMessage(mapMessageFromApi(quotedMessage));
784799

785800
message.attachments = message.attachments || [];
786-
787801
const useRealName = settings.peek('UI_Use_Real_Name');
788802
const quoteAttachment = createQuoteAttachment(
789803
decryptedQuoteMessage,

apps/meteor/tests/e2e/e2e-encryption/e2ee-encryption-decryption.spec.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import { faker } from '@faker-js/faker';
22

33
import { setupE2EEPassword } from './setupE2EEPassword';
4+
import { BASE_URL } from '../config/constants';
45
import { Users } from '../fixtures/userStates';
56
import { EncryptedRoomPage } from '../page-objects/encrypted-room';
67
import { HomeSidenav } from '../page-objects/fragments';
78
import { FileUploadModal } from '../page-objects/fragments/file-upload-modal';
89
import { LoginPage } from '../page-objects/login';
10+
import { createTargetGroupAndReturnFullRoom, deleteChannel, deleteRoom } from '../utils';
911
import { preserveSettings } from '../utils/preserveSettings';
12+
import { sendMessageFromUser } from '../utils/sendMessage';
1013
import { test, expect } from '../utils/test';
1114

1215
const settingsList = ['E2E_Enable', 'E2E_Allow_Unencrypted_Messages'];
@@ -144,4 +147,58 @@ test.describe('E2EE Encryption and Decryption - Basic Features', () => {
144147
await expect(encryptedRoomPage.lastMessage.fileUploadName).toContainText(fileName);
145148
await expect(encryptedRoomPage.lastMessage.body).toHaveText(fileDescription);
146149
});
150+
151+
test.describe('E2EE Quotes', () => {
152+
let targetRoomId: string;
153+
let targetChannelName: string;
154+
155+
test.afterAll(async ({ api }) => {
156+
await deleteRoom(api, targetRoomId);
157+
await deleteChannel(api, targetChannelName);
158+
});
159+
160+
test('expect to not crash and not show quote message for a message_link which is not accessible to the user', async ({
161+
page,
162+
request,
163+
api,
164+
}) => {
165+
const encryptedRoomPage = new EncryptedRoomPage(page);
166+
const sidenav = new HomeSidenav(page);
167+
targetChannelName = faker.string.uuid();
168+
169+
await sidenav.createEncryptedChannel(targetChannelName);
170+
171+
await expect(page).toHaveURL(`/group/${targetChannelName}`);
172+
await expect(encryptedRoomPage.encryptedIcon).toBeVisible();
173+
await expect(encryptedRoomPage.encryptionNotReadyIndicator).not.toBeVisible();
174+
175+
await encryptedRoomPage.sendMessage('First encrypted message.');
176+
await expect(encryptedRoomPage.lastMessage.encryptedIcon).toBeVisible();
177+
await expect(encryptedRoomPage.lastMessage.body).toHaveText('First encrypted message.');
178+
179+
// create a private group for user2
180+
const { group: user1Channel } = await createTargetGroupAndReturnFullRoom(api, {
181+
excludeSelf: true,
182+
members: [Users.user2.data._id],
183+
});
184+
targetRoomId = user1Channel._id;
185+
186+
// send a message to the private group, which is not accessible to the main user
187+
const sentMessage = (await sendMessageFromUser(request, Users.user2, targetRoomId, 'This is a test message.')).message;
188+
189+
const messageLink = `${BASE_URL}/group/${user1Channel.name}?msg=${sentMessage._id}`;
190+
191+
await encryptedRoomPage.sendMessage(`This is a message with message link - ${messageLink}`);
192+
193+
await expect(encryptedRoomPage.lastMessage.encryptedIcon).toBeVisible();
194+
await expect(encryptedRoomPage.lastMessage.body).toContainText(`This is a message with message link - ${messageLink}`);
195+
await expect(encryptedRoomPage.lastNthMessage(1).body).toContainText('First encrypted message.');
196+
197+
await page.reload();
198+
199+
await expect(encryptedRoomPage.lastMessage.encryptedIcon).toBeVisible();
200+
await expect(encryptedRoomPage.lastMessage.body).toContainText(`This is a message with message link - ${messageLink}`);
201+
await expect(encryptedRoomPage.lastNthMessage(1).body).toContainText('First encrypted message.');
202+
});
203+
});
147204
});

apps/meteor/tests/e2e/utils/create-target-channel.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,11 @@ export async function createArchivedChannel(api: BaseTest['api']): Promise<strin
116116

117117
return channel.name;
118118
}
119+
120+
export async function createTargetGroupAndReturnFullRoom(
121+
api: BaseTest['api'],
122+
options?: Omit<GroupsCreateProps, 'name'>,
123+
): Promise<{ group: IRoom }> {
124+
const name = faker.string.uuid();
125+
return (await api.post('/groups.create', { name, ...options })).json();
126+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import type { APIRequestContext } from 'playwright-core';
2+
3+
import { BASE_API_URL } from '../config/constants';
4+
import type { IUserState } from '../fixtures/userStates';
5+
6+
export const sendMessageFromUser = async (request: APIRequestContext, user: IUserState, rid: string, message: string) => {
7+
return request
8+
.post(`${BASE_API_URL}/chat.postMessage`, {
9+
headers: {
10+
'X-Auth-Token': user.data.loginToken,
11+
'X-User-Id': user.data._id,
12+
},
13+
data: {
14+
roomId: rid,
15+
text: message,
16+
},
17+
})
18+
.then((response) => response.json());
19+
};

0 commit comments

Comments
 (0)