Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fast-forks-sin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rocket.chat/meteor': patch
---

Fixes crash in end-to-end encrypted rooms when sending a quote or message link referencing a message outside the room.
22 changes: 18 additions & 4 deletions apps/meteor/client/lib/e2ee/rocketchat.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import QueryString from 'querystring';
import URL from 'url';

import type { IE2EEMessage, IMessage, IRoom, ISubscription, IUser, IUploadWithUser, MessageAttachment } from '@rocket.chat/core-typings';
import type {
IE2EEMessage,
IMessage,
IRoom,
ISubscription,
IUser,
IUploadWithUser,
MessageAttachment,
Serialized,
} from '@rocket.chat/core-typings';
import { isE2EEMessage, isEncryptedMessageContent } from '@rocket.chat/core-typings';
import { Emitter } from '@rocket.chat/emitter';
import { imperativeModal } from '@rocket.chat/ui-client';
Expand Down Expand Up @@ -773,8 +782,14 @@ class E2E extends Emitter {
return;
}

const getQuotedMessage = await sdk.rest.get('/v1/chat.getMessage', { msgId });
const quotedMessage = getQuotedMessage?.message;
let quotedMessage: Serialized<IMessage>;
try {
const getQuotedMessage = await sdk.rest.get('/v1/chat.getMessage', { msgId });
quotedMessage = getQuotedMessage?.message;
} catch (error) {
console.error(`Error getting quoted message: ${error}`);
return;
}

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

message.attachments = message.attachments || [];

const useRealName = settings.peek('UI_Use_Real_Name');
const quoteAttachment = createQuoteAttachment(
decryptedQuoteMessage,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { faker } from '@faker-js/faker';

import { setupE2EEPassword } from './setupE2EEPassword';
import { BASE_URL } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { EncryptedRoomPage } from '../page-objects/encrypted-room';
import { HomeSidenav } from '../page-objects/fragments';
import { FileUploadModal } from '../page-objects/fragments/file-upload-modal';
import { LoginPage } from '../page-objects/login';
import { createTargetGroupAndReturnFullRoom, deleteChannel, deleteRoom } from '../utils';
import { preserveSettings } from '../utils/preserveSettings';
import { sendMessageFromUser } from '../utils/sendMessage';
import { test, expect } from '../utils/test';

const settingsList = ['E2E_Enable', 'E2E_Allow_Unencrypted_Messages'];
Expand Down Expand Up @@ -144,4 +147,58 @@ test.describe('E2EE Encryption and Decryption - Basic Features', () => {
await expect(encryptedRoomPage.lastMessage.fileUploadName).toContainText(fileName);
await expect(encryptedRoomPage.lastMessage.body).toHaveText(fileDescription);
});

test.describe('E2EE Quotes', () => {
let targetRoomId: string;
let targetChannelName: string;

test.afterAll(async ({ api }) => {
await deleteRoom(api, targetRoomId);
await deleteChannel(api, targetChannelName);
});

test('expect to not crash and not show quote message for a message_link which is not accessible to the user', async ({
page,
request,
api,
}) => {
const encryptedRoomPage = new EncryptedRoomPage(page);
const sidenav = new HomeSidenav(page);
targetChannelName = faker.string.uuid();

await sidenav.createEncryptedChannel(targetChannelName);

await expect(page).toHaveURL(`/group/${targetChannelName}`);
await expect(encryptedRoomPage.encryptedIcon).toBeVisible();
await expect(encryptedRoomPage.encryptionNotReadyIndicator).not.toBeVisible();

await encryptedRoomPage.sendMessage('First encrypted message.');
await expect(encryptedRoomPage.lastMessage.encryptedIcon).toBeVisible();
await expect(encryptedRoomPage.lastMessage.body).toHaveText('First encrypted message.');

// create a private group for user2
const { group: user1Channel } = await createTargetGroupAndReturnFullRoom(api, {
excludeSelf: true,
members: [Users.user2.data._id],
});
targetRoomId = user1Channel._id;

// send a message to the private group, which is not accessible to the main user
const sentMessage = (await sendMessageFromUser(request, Users.user2, targetRoomId, 'This is a test message.')).message;

const messageLink = `${BASE_URL}/group/${user1Channel.name}?msg=${sentMessage._id}`;

await encryptedRoomPage.sendMessage(`This is a message with message link - ${messageLink}`);

await expect(encryptedRoomPage.lastMessage.encryptedIcon).toBeVisible();
await expect(encryptedRoomPage.lastMessage.body).toContainText(`This is a message with message link - ${messageLink}`);
await expect(encryptedRoomPage.lastNthMessage(1).body).toContainText('First encrypted message.');

await page.reload();

await expect(encryptedRoomPage.lastMessage.encryptedIcon).toBeVisible();
await expect(encryptedRoomPage.lastMessage.body).toContainText(`This is a message with message link - ${messageLink}`);
await expect(encryptedRoomPage.lastNthMessage(1).body).toContainText('First encrypted message.');
});
});
});
8 changes: 8 additions & 0 deletions apps/meteor/tests/e2e/utils/create-target-channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,11 @@ export async function createArchivedChannel(api: BaseTest['api']): Promise<strin

return channel.name;
}

export async function createTargetGroupAndReturnFullRoom(
api: BaseTest['api'],
options?: Omit<GroupsCreateProps, 'name'>,
): Promise<{ group: IRoom }> {
const name = faker.string.uuid();
return (await api.post('/groups.create', { name, ...options })).json();
}
19 changes: 19 additions & 0 deletions apps/meteor/tests/e2e/utils/sendMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { APIRequestContext } from 'playwright-core';

import { BASE_API_URL } from '../config/constants';
import type { IUserState } from '../fixtures/userStates';

export const sendMessageFromUser = async (request: APIRequestContext, user: IUserState, rid: string, message: string) => {
return request
.post(`${BASE_API_URL}/chat.postMessage`, {
headers: {
'X-Auth-Token': user.data.loginToken,
'X-User-Id': user.data._id,
},
data: {
roomId: rid,
text: message,
},
})
.then((response) => response.json());
};
Loading