Add sanity checks to prevent users from ignoring themselves (#30079)

* Fix missing state

* Throw error if membership event changes

* Write test

* Fix broken tests

* Cache inviter when room is loaded

* Translate error message for dialog
This commit is contained in:
R Midhun Suresh
2025-06-04 23:09:19 +05:30
committed by GitHub
parent 9c0604f849
commit b9f319a9f5
4 changed files with 95 additions and 13 deletions

View File

@@ -77,6 +77,7 @@ import { type ViewUserPayload } from "../../../../src/dispatcher/payloads/ViewUs
import { CallStore } from "../../../../src/stores/CallStore.ts";
import MediaDeviceHandler, { MediaDeviceKindEnum } from "../../../../src/MediaDeviceHandler.ts";
import Modal from "../../../../src/Modal.tsx";
import ErrorDialog from "../../../../src/components/views/dialogs/ErrorDialog.tsx";
// Used by group calls
jest.spyOn(MediaDeviceHandler, "getDevices").mockResolvedValue({
@@ -237,6 +238,7 @@ describe("RoomView", () => {
member.membership = KnownMembership.Invite;
member.events.member = new MatrixEvent({
sender: "@bob:example.org",
content: { membership: KnownMembership.Invite },
});
room.getMyMembership = jest.fn().mockReturnValue(KnownMembership.Invite);
room.getMember = jest.fn().mockReturnValue(member);
@@ -271,10 +273,45 @@ describe("RoomView", () => {
finished: Promise.resolve([true, true, false]),
close: jest.fn(),
});
await fireEvent.click(getByRole("button", { name: "Decline and block" }));
await act(() => fireEvent.click(getByRole("button", { name: "Decline and block" })));
expect(cli.leave).toHaveBeenCalledWith(room.roomId);
expect(cli.setIgnoredUsers).toHaveBeenCalledWith(["@carol:example.org", "@bob:example.org"]);
});
it("prevents ignoring own user", async () => {
const member = new RoomMember(room.roomId, cli.getSafeUserId());
member.membership = KnownMembership.Invite;
member.events.member = new MatrixEvent({
/*
It doesn't matter that this is an invite event coming from own user, we just
want to simulate a situation where the sender of the membership event somehow
ends up being own user.
*/
sender: cli.getSafeUserId(),
content: { membership: KnownMembership.Invite },
});
jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Invite);
jest.spyOn(room, "getMember").mockReturnValue(member);
const { getByRole } = await mountRoomView();
cli.getIgnoredUsers.mockReturnValue(["@carol:example.org"]);
jest.spyOn(Modal, "createDialog").mockReturnValue({
finished: Promise.resolve([true, true, false]),
close: jest.fn(),
});
await act(() => fireEvent.click(getByRole("button", { name: "Decline and block" })));
// Should show error in a modal dialog
await waitFor(() => {
expect(Modal.createDialog).toHaveBeenLastCalledWith(ErrorDialog, {
title: "Failed to reject invite",
description: "Cannot determine which user to ignore since the member event has changed.",
});
});
// The ignore call should not go through
expect(cli.setIgnoredUsers).not.toHaveBeenCalled();
});
it("handles declining an invite and reporting the room", async () => {
const { getByRole } = await mountRoomView();
jest.spyOn(Modal, "createDialog").mockReturnValue({

View File

@@ -1363,7 +1363,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
aria-label="Open room settings"
aria-live="off"
class="_avatar_1qbcf_8 mx_BaseAvatar _avatar-imageless_1qbcf_52"
data-color="4"
data-color="5"
data-testid="avatar-img"
data-type="round"
role="button"
@@ -1390,7 +1390,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
<span
class="mx_RoomHeader_truncated mx_lineClamp"
>
!11:example.org
!12:example.org
</span>
</div>
</div>
@@ -1571,7 +1571,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
aria-label="Open room settings"
aria-live="off"
class="_avatar_1qbcf_8 mx_BaseAvatar _avatar-imageless_1qbcf_52"
data-color="4"
data-color="5"
data-testid="avatar-img"
data-type="round"
role="button"
@@ -1598,7 +1598,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
<span
class="mx_RoomHeader_truncated mx_lineClamp"
>
!11:example.org
!12:example.org
</span>
</div>
</div>
@@ -1952,7 +1952,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
aria-label="Open room settings"
aria-live="off"
class="_avatar_1qbcf_8 mx_BaseAvatar _avatar-imageless_1qbcf_52"
data-color="3"
data-color="4"
data-testid="avatar-img"
data-type="round"
role="button"
@@ -1979,7 +1979,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
<span
class="mx_RoomHeader_truncated mx_lineClamp"
>
!16:example.org
!17:example.org
</span>
</div>
</div>