diff --git a/src/components/views/dialogs/ForwardDialog.tsx b/src/components/views/dialogs/ForwardDialog.tsx index 512f0ad6979..32aab5d6e98 100644 --- a/src/components/views/dialogs/ForwardDialog.tsx +++ b/src/components/views/dialogs/ForwardDialog.tsx @@ -57,6 +57,7 @@ import { import { getKeyBindingsManager } from "../../../KeyBindingsManager"; import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts"; import { OverflowTileView } from "../rooms/OverflowTileView"; +import { attachMentions } from "../../../utils/messages"; const AVATAR_SIZE = 30; @@ -178,7 +179,18 @@ const Entry: React.FC> = ({ room, type, content, matrixClient: ); }; -const transformEvent = (event: MatrixEvent): { type: string; content: IContent } => { +/** + * Transform content of a MatrixEvent before forwarding: + * 1. Strip all relations. + * 2. Convert location events into a static pin-drop location share, + * and remove description from self-location shares. + * 3. Pass through attachMentions() to strip mentions (as no EditorModel is present to recalculate from). + * + * @param event - The MatrixEvent to transform. + * @param userId - Current user MXID (passed through to attachMentions()). + * @returns The transformed event type and content. + */ +const transformEvent = (event: MatrixEvent, userId: string): { type: string; content: IContent } => { const { // eslint-disable-next-line @typescript-eslint/no-unused-vars "m.relates_to": _, // strip relations - in future we will attach a relation pointing at the original event @@ -213,6 +225,13 @@ const transformEvent = (event: MatrixEvent): { type: string; content: IContent } }; } + // Mentions can leak information about the context of the original message, + // so pass through attachMentions() to recalculate mentions. + // Currently, this strips all mentions (forces an empty m.mentions), + // as there is no EditorModel to parse pills from. + // Future improvements could actually recalculate mentions based on the message body. + attachMentions(userId, content, null, undefined); + return { type, content }; }; @@ -223,7 +242,7 @@ const ForwardDialog: React.FC = ({ matrixClient: cli, event, permalinkCr cli.getProfileInfo(userId).then((info) => setProfileInfo(info)); }, [cli, userId]); - const { type, content } = transformEvent(event); + const { type, content } = transformEvent(event, userId); // For the message preview we fake the sender as ourselves const mockEvent = new MatrixEvent({ diff --git a/test/unit-tests/components/views/dialogs/ForwardDialog-test.tsx b/test/unit-tests/components/views/dialogs/ForwardDialog-test.tsx index b6ab8a7b38d..370f612c9cf 100644 --- a/test/unit-tests/components/views/dialogs/ForwardDialog-test.tsx +++ b/test/unit-tests/components/views/dialogs/ForwardDialog-test.tsx @@ -153,8 +153,9 @@ describe("ForwardDialog", () => { await userEvent.keyboard("[Enter]"); expect(mockClient.sendEvent).toHaveBeenCalledWith("A", "m.room.message", { - body: "Hello world!", - msgtype: "m.text", + "body": "Hello world!", + "msgtype": "m.text", + "m.mentions": {}, }); }); @@ -248,6 +249,37 @@ describe("ForwardDialog", () => { expect(secondButton.getAttribute("aria-disabled")).toBeFalsy(); }); + it("strips mentions from forwarded messages", async () => { + const messageWithMention = mkEvent({ + type: "m.room.message", + room: sourceRoom, + user: "@bob:example.org", + content: { + "msgtype": "m.text", + "body": "Hi @alice:example.org", + "m.mentions": { + user_ids: ["@alice:example.org"], + }, + }, + event: true, + }); + + const { container } = mountForwardDialog(messageWithMention); + const roomId = "a"; + + // Click the send button. + act(() => { + const sendButton = container.querySelector(".mx_ForwardList_sendButton"); + fireEvent.click(sendButton!); + }); + + // Expected content should have mentions empty. + expect(mockClient.sendEvent).toHaveBeenCalledWith(roomId, messageWithMention.getType(), { + ...messageWithMention.getContent(), + "m.mentions": {}, + }); + }); + describe("Location events", () => { // 14.03.2022 16:15 const now = 1647270879403; @@ -357,11 +389,12 @@ describe("ForwardDialog", () => { sendToFirstRoom(container); - expect(mockClient.sendEvent).toHaveBeenCalledWith( - roomId, - pinDropLocationEvent.getType(), - pinDropLocationEvent.getContent(), - ); + const expectedContent = { + ...pinDropLocationEvent.getContent(), + "m.mentions": {}, // Add mentions (explicitly set to empty) + }; + + expect(mockClient.sendEvent).toHaveBeenCalledWith(roomId, pinDropLocationEvent.getType(), expectedContent); }); });