From 0ac898140eddab5e81d1f13d4f751239f7e7c6ae Mon Sep 17 00:00:00 2001 From: Tol Wassman Date: Fri, 26 Sep 2025 03:26:14 +0000 Subject: [PATCH 1/9] strip mentions from forwarded messages fixes element-hq/element-web#30883 --- src/components/views/dialogs/ForwardDialog.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/views/dialogs/ForwardDialog.tsx b/src/components/views/dialogs/ForwardDialog.tsx index 512f0ad6979..c0d4c47687e 100644 --- a/src/components/views/dialogs/ForwardDialog.tsx +++ b/src/components/views/dialogs/ForwardDialog.tsx @@ -182,6 +182,8 @@ const transformEvent = (event: MatrixEvent): { 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 + // eslint-disable-next-line @typescript-eslint/no-unused-vars + "m.mentions": __, // strip mentions // We're taking a shallow copy here to avoid https://github.com/vector-im/element-web/issues/10924 ...content } = event.getContent(); From ecd770add03adef7ac39dfc4d6c40809d676d251 Mon Sep 17 00:00:00 2001 From: Tol Wassman Date: Wed, 8 Oct 2025 19:25:08 +0000 Subject: [PATCH 2/9] call attachMentions() for empty m.mentions in forwarded messages As there is no EditorModel, attachMentions() currently does nothing --- src/components/views/dialogs/ForwardDialog.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/views/dialogs/ForwardDialog.tsx b/src/components/views/dialogs/ForwardDialog.tsx index c0d4c47687e..6c33a4e8da2 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 "../rooms/SendMessageComposer"; const AVATAR_SIZE = 30; @@ -178,12 +179,10 @@ const Entry: React.FC> = ({ room, type, content, matrixClient: ); }; -const transformEvent = (event: MatrixEvent): { type: string; content: IContent } => { +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 - // eslint-disable-next-line @typescript-eslint/no-unused-vars - "m.mentions": __, // strip mentions // We're taking a shallow copy here to avoid https://github.com/vector-im/element-web/issues/10924 ...content } = event.getContent(); @@ -214,6 +213,9 @@ const transformEvent = (event: MatrixEvent): { type: string; content: IContent } }, }; } + + // Force an empty m.mentions property as there is no EditorModel to parse pills from + attachMentions(userId, content, null); return { type, content }; }; @@ -225,7 +227,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({ From cbe117eb4fe855503f6d04a19a8f63eb3fcd78d1 Mon Sep 17 00:00:00 2001 From: Tol Wassman Date: Thu, 9 Oct 2025 08:26:50 +0000 Subject: [PATCH 3/9] fix lint and typecheck --- src/components/views/dialogs/ForwardDialog.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/dialogs/ForwardDialog.tsx b/src/components/views/dialogs/ForwardDialog.tsx index 6c33a4e8da2..12abcfc6103 100644 --- a/src/components/views/dialogs/ForwardDialog.tsx +++ b/src/components/views/dialogs/ForwardDialog.tsx @@ -213,9 +213,9 @@ const transformEvent = (event: MatrixEvent, userId: string): { type: string; con }, }; } - + // Force an empty m.mentions property as there is no EditorModel to parse pills from - attachMentions(userId, content, null); + attachMentions(userId, content, null, undefined); return { type, content }; }; From 232b4471c2d7a8d756a142c2f38dcf68c9625724 Mon Sep 17 00:00:00 2001 From: Tol Wassman Date: Thu, 9 Oct 2025 09:01:53 +0000 Subject: [PATCH 4/9] update test "should be navigable using arrow keys" --- .../components/views/dialogs/ForwardDialog-test.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/unit-tests/components/views/dialogs/ForwardDialog-test.tsx b/test/unit-tests/components/views/dialogs/ForwardDialog-test.tsx index b6ab8a7b38d..e5d84c66b36 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": {}, }); }); From a735dd5fc461c94fec8655592478d8d1f49d84e4 Mon Sep 17 00:00:00 2001 From: Tol Wassman Date: Fri, 31 Oct 2025 03:39:20 +0000 Subject: [PATCH 5/9] update test "forwards pin drop event" add empty mentions to expected content --- .../components/views/dialogs/ForwardDialog-test.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/unit-tests/components/views/dialogs/ForwardDialog-test.tsx b/test/unit-tests/components/views/dialogs/ForwardDialog-test.tsx index e5d84c66b36..535a6ea83e6 100644 --- a/test/unit-tests/components/views/dialogs/ForwardDialog-test.tsx +++ b/test/unit-tests/components/views/dialogs/ForwardDialog-test.tsx @@ -358,10 +358,15 @@ describe("ForwardDialog", () => { sendToFirstRoom(container); + const expectedContent = { + ...pinDropLocationEvent.getContent(), + "m.mentions": {}, // Add empty mentions + }; + expect(mockClient.sendEvent).toHaveBeenCalledWith( roomId, pinDropLocationEvent.getType(), - pinDropLocationEvent.getContent(), + expectedContent, ); }); }); From 5496103093a95a58f885aa39c971ed71df6f4027 Mon Sep 17 00:00:00 2001 From: Tol Wassman Date: Fri, 31 Oct 2025 04:02:07 +0000 Subject: [PATCH 6/9] add doc to transformEvent() & elaborate on attachMentions() --- src/components/views/dialogs/ForwardDialog.tsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/components/views/dialogs/ForwardDialog.tsx b/src/components/views/dialogs/ForwardDialog.tsx index 12abcfc6103..40a1d78e682 100644 --- a/src/components/views/dialogs/ForwardDialog.tsx +++ b/src/components/views/dialogs/ForwardDialog.tsx @@ -179,6 +179,17 @@ const Entry: React.FC> = ({ room, type, content, matrixClient: ); }; +/** + * 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 @@ -214,7 +225,11 @@ const transformEvent = (event: MatrixEvent, userId: string): { type: string; con }; } - // Force an empty m.mentions property as there is no EditorModel to parse pills from + // 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 }; From 324b52f036f763f86477c6dfa944a7ee0bee3c68 Mon Sep 17 00:00:00 2001 From: Tol Wassman Date: Fri, 31 Oct 2025 04:29:38 +0000 Subject: [PATCH 7/9] add test "strips mentions from forwarded messages" --- .../views/dialogs/ForwardDialog-test.tsx | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/test/unit-tests/components/views/dialogs/ForwardDialog-test.tsx b/test/unit-tests/components/views/dialogs/ForwardDialog-test.tsx index 535a6ea83e6..34a61649e8c 100644 --- a/test/unit-tests/components/views/dialogs/ForwardDialog-test.tsx +++ b/test/unit-tests/components/views/dialogs/ForwardDialog-test.tsx @@ -249,6 +249,41 @@ 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; @@ -360,7 +395,7 @@ describe("ForwardDialog", () => { const expectedContent = { ...pinDropLocationEvent.getContent(), - "m.mentions": {}, // Add empty mentions + "m.mentions": {}, // Add mentions (explicitly set to empty) }; expect(mockClient.sendEvent).toHaveBeenCalledWith( From 348855ef6617e8aaeff440b627237c91c693832a Mon Sep 17 00:00:00 2001 From: Tol Wassman Date: Fri, 31 Oct 2025 04:52:55 +0000 Subject: [PATCH 8/9] fix lint --- .../views/dialogs/ForwardDialog-test.tsx | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/test/unit-tests/components/views/dialogs/ForwardDialog-test.tsx b/test/unit-tests/components/views/dialogs/ForwardDialog-test.tsx index 34a61649e8c..370f612c9cf 100644 --- a/test/unit-tests/components/views/dialogs/ForwardDialog-test.tsx +++ b/test/unit-tests/components/views/dialogs/ForwardDialog-test.tsx @@ -258,7 +258,7 @@ describe("ForwardDialog", () => { "msgtype": "m.text", "body": "Hi @alice:example.org", "m.mentions": { - "user_ids": ["@alice:example.org"], + user_ids: ["@alice:example.org"], }, }, event: true, @@ -274,14 +274,10 @@ describe("ForwardDialog", () => { }); // Expected content should have mentions empty. - expect(mockClient.sendEvent).toHaveBeenCalledWith( - roomId, - messageWithMention.getType(), - { - ...messageWithMention.getContent(), - "m.mentions": {}, - }, - ); + expect(mockClient.sendEvent).toHaveBeenCalledWith(roomId, messageWithMention.getType(), { + ...messageWithMention.getContent(), + "m.mentions": {}, + }); }); describe("Location events", () => { @@ -398,11 +394,7 @@ describe("ForwardDialog", () => { "m.mentions": {}, // Add mentions (explicitly set to empty) }; - expect(mockClient.sendEvent).toHaveBeenCalledWith( - roomId, - pinDropLocationEvent.getType(), - expectedContent, - ); + expect(mockClient.sendEvent).toHaveBeenCalledWith(roomId, pinDropLocationEvent.getType(), expectedContent); }); }); From 1273d7079b4baa40317e9dbff7b1df6f488db7e4 Mon Sep 17 00:00:00 2001 From: Tol Wassman Date: Tue, 4 Nov 2025 18:32:45 +0000 Subject: [PATCH 9/9] update source of `attachMentions()` import --- src/components/views/dialogs/ForwardDialog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/dialogs/ForwardDialog.tsx b/src/components/views/dialogs/ForwardDialog.tsx index 40a1d78e682..32aab5d6e98 100644 --- a/src/components/views/dialogs/ForwardDialog.tsx +++ b/src/components/views/dialogs/ForwardDialog.tsx @@ -57,7 +57,7 @@ import { import { getKeyBindingsManager } from "../../../KeyBindingsManager"; import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts"; import { OverflowTileView } from "../rooms/OverflowTileView"; -import { attachMentions } from "../rooms/SendMessageComposer"; +import { attachMentions } from "../../../utils/messages"; const AVATAR_SIZE = 30;