From dbe441de33d11b9a35255f90cd6125d4bf9e99be Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Tue, 23 Sep 2025 14:51:19 +0100 Subject: [PATCH] Exclude cancelled requests from in-progress lists (#5016) Fixes https://github.com/element-hq/element-web/issues/29882 When we ask for the in-progress verification requests, exclude requests that have been cancelled. This means that we don't erroneously tell the user that the new request they are about to create has been cancelled. --- spec/integ/crypto/verification.spec.ts | 52 ++++++++++++++++++++++++++ src/rust-crypto/rust-crypto.ts | 4 +- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/spec/integ/crypto/verification.spec.ts b/spec/integ/crypto/verification.spec.ts index 9af06dcf2..0aefe75c9 100644 --- a/spec/integ/crypto/verification.spec.ts +++ b/spec/integ/crypto/verification.spec.ts @@ -735,6 +735,35 @@ describe("verification", () => { expect(request.cancellingUserId).toEqual("@alice:localhost"); }); + it("does not include cancelled requests in the list of requests", async () => { + // Given Alice started a verification request + const [, request] = await Promise.all([ + expectSendToDeviceMessage("m.key.verification.request"), + aliceClient.getCrypto()!.requestDeviceVerification(TEST_USER_ID, TEST_DEVICE_ID), + ]); + const transactionId = request.transactionId!; + + returnToDeviceMessageFromSync(buildReadyMessage(transactionId, ["m.sas.v1"])); + await waitForVerificationRequestChanged(request); + + // Sanity: the request is listed + const requestsBeforeCancel = aliceClient + .getCrypto()! + .getVerificationRequestsToDeviceInProgress(TEST_USER_ID); + + expect(requestsBeforeCancel).toHaveLength(1); + + // When Alice cancels it + await Promise.all([expectSendToDeviceMessage("m.key.verification.cancel"), request.cancel()]); + + // Then it is no longer listed as in progress + const requestsAfterCancel = aliceClient + .getCrypto()! + .getVerificationRequestsToDeviceInProgress(TEST_USER_ID); + + expect(requestsAfterCancel).toHaveLength(0); + }); + it("can cancel during the SAS phase", async () => { // have alice initiate a verification. She should send a m.key.verification.request const [, request] = await Promise.all([ @@ -1072,6 +1101,29 @@ describe("verification", () => { ).not.toBeDefined(); }); + it("ignores cancelled verification requests", async () => { + // Given a verification request exists + const event = createVerificationRequestEvent(); + returnRoomMessageFromSync(TEST_ROOM_ID, event); + + // Wait for the request to be received + await emitPromise(aliceClient, CryptoEvent.VerificationRequestReceived); + + const request = aliceClient.getCrypto()!.findVerificationRequestDMInProgress(TEST_ROOM_ID, "@bob:xyz"); + + // When I cancel it + fetchMock.put("express:/_matrix/client/v3/rooms/:roomId/send/m.key.verification.cancel/:id", { + event_id: event.event_id, + }); + await request!.cancel(); + expect(request!.phase).toEqual(VerificationPhase.Cancelled); + + // Then it is no longer found + expect( + aliceClient.getCrypto()!.findVerificationRequestDMInProgress(TEST_ROOM_ID, "@bob:xyz"), + ).not.toBeDefined(); + }); + it("Plaintext verification request from Bob to Alice", async () => { // Add verification request from Bob to Alice in the DM between them returnRoomMessageFromSync(TEST_ROOM_ID, createVerificationRequestEvent()); diff --git a/src/rust-crypto/rust-crypto.ts b/src/rust-crypto/rust-crypto.ts index eb22b5bc5..44619abc8 100644 --- a/src/rust-crypto/rust-crypto.ts +++ b/src/rust-crypto/rust-crypto.ts @@ -1040,7 +1040,7 @@ export class RustCrypto extends TypedEventEmitter request.roomId === undefined) + .filter((request) => request.roomId === undefined && !request.isCancelled()) .map((request) => this.makeVerificationRequest(request)); } @@ -1063,7 +1063,7 @@ export class RustCrypto extends TypedEventEmitter request.roomId?.toString() === roomId); + const request = requests.find((request) => request.roomId?.toString() === roomId && !request.isCancelled()); if (request) { return this.makeVerificationRequest(request);