Skip to content
Open
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
29 changes: 19 additions & 10 deletions src/components/views/dialogs/ForwardDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ import { getKeyBindingsManager } from "../../../KeyBindingsManager";
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
import { OverflowTileView } from "../rooms/OverflowTileView";
import { attachMentions } from "../../../utils/messages";
import { CommandPartCreator } from "../../../editor/parts";
import SettingsStore from "../../../settings/SettingsStore";
import { parseEvent } from "../../../editor/deserialize";
import EditorModel from "../../../editor/model";

const AVATAR_SIZE = 30;

Expand Down Expand Up @@ -184,13 +188,13 @@ const Entry: React.FC<IEntryProps<any>> = ({ room, type, content, matrixClient:
* 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).
* 3. Parse the event back into an EditorModel and recalculate mentions.
*
* @param event - The MatrixEvent to transform.
* @param userId - Current user MXID (passed through to attachMentions()).
* @param cli - The MatrixClient (used for recalculation of mentions).
* @returns The transformed event type and content.
*/
const transformEvent = (event: MatrixEvent, userId: string): { type: string; content: IContent } => {
const transformEvent = (event: MatrixEvent, cli: MatrixClient): { 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
Expand Down Expand Up @@ -225,12 +229,17 @@ const transformEvent = (event: MatrixEvent, userId: string): { type: string; con
};
}

// 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);
// Mentions can leak information about the context of the original message, so:
// 1. Parse the event's message body back into an EditorModel, then
// 2. Pass through attachMentions() to recalculate mentions.
const room = cli.getRoom(event.getRoomId())!;
const partCreator = new CommandPartCreator(room, cli);
const parts = parseEvent(event, partCreator, {
shouldEscape: SettingsStore.getValue("MessageComposerInput.useMarkdown"),
});
const model = new EditorModel(parts, partCreator); // Temporary EditorModel to pass through
const userId = cli.getSafeUserId();
attachMentions(userId, content, model, undefined);

return { type, content };
};
Expand All @@ -242,7 +251,7 @@ const ForwardDialog: React.FC<IProps> = ({ matrixClient: cli, event, permalinkCr
cli.getProfileInfo(userId).then((info) => setProfileInfo(info));
}, [cli, userId]);

const { type, content } = transformEvent(event, userId);
const { type, content } = transformEvent(event, cli);

// For the message preview we fake the sender as ourselves
const mockEvent = new MatrixEvent({
Expand Down
70 changes: 46 additions & 24 deletions test/unit-tests/components/views/dialogs/ForwardDialog-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -249,34 +249,56 @@ 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);
describe("Mention recalculation", () => {
const roomId = "a";
const sendClick = (container: HTMLElement): void =>
act(() => {
const sendButton = container.querySelector(".mx_ForwardList_sendButton");
fireEvent.click(sendButton!);
});
const makeMessage = (body: string, mentions: object, formattedBody?: string) => {
return mkEvent({
type: "m.room.message",
room: sourceRoom,
user: "@bob:example.org",
content: {
"msgtype": "m.text",
"body": body,
"m.mentions": mentions,
...(formattedBody && {
format: "org.matrix.custom.html",
formatted_body: formattedBody,
}),
},
event: true,
});
};

// Click the send button.
act(() => {
const sendButton = container.querySelector(".mx_ForwardList_sendButton");
fireEvent.click(sendButton!);
it("strips extra mentions", async () => {
const message = makeMessage("Hi Alice", { user_ids: [aliceId] });
const { container } = mountForwardDialog(message);
sendClick(container);
// Expected content should have mentions empty.
expect(mockClient.sendEvent).toHaveBeenCalledWith(roomId, message.getType(), {
...message.getContent(),
"m.mentions": {},
});
});

// Expected content should have mentions empty.
expect(mockClient.sendEvent).toHaveBeenCalledWith(roomId, messageWithMention.getType(), {
...messageWithMention.getContent(),
"m.mentions": {},
// TODO: mock room membership
it("recalculates mention pills", async () => {
const message = makeMessage(
"Hi Alice",
{ user_ids: [aliceId] },
`Hi <a href="https://matrix.to/#/${aliceId}">Alice</a>`,
);
const { container } = mountForwardDialog(message);
sendClick(container);
// Expected content should have mentions empty.
expect(mockClient.sendEvent).toHaveBeenCalledWith(roomId, message.getType(), {
...message.getContent(),
"m.mentions": { user_ids: [aliceId] },
});
});
});

Expand Down
Loading