From 35c9a0fdc97ab73a4020ff19086ab81563789ab7 Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Thu, 23 Apr 2026 09:20:35 -0400 Subject: [PATCH] fix: harden Matrix verification fallback paths --- extensions/matrix/src/matrix/sdk.ts | 2 +- .../matrix/sdk/verification-manager.test.ts | 39 +++++++++++++++++++ .../src/matrix/sdk/verification-manager.ts | 6 ++- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/extensions/matrix/src/matrix/sdk.ts b/extensions/matrix/src/matrix/sdk.ts index 0a1338536d0..d68245460b2 100644 --- a/extensions/matrix/src/matrix/sdk.ts +++ b/extensions/matrix/src/matrix/sdk.ts @@ -372,7 +372,7 @@ export class MatrixClient { trustOwnDeviceAfterSas: async (deviceId: string) => { const crypto = this.client.getCrypto() as MatrixCryptoBootstrapApi | undefined; if (typeof crypto?.crossSignDevice !== "function") { - throw new Error("Matrix crypto backend does not support cross-signing devices"); + return; } await crypto.crossSignDevice(deviceId); }, diff --git a/extensions/matrix/src/matrix/sdk/verification-manager.test.ts b/extensions/matrix/src/matrix/sdk/verification-manager.test.ts index 597daf05d3d..b39e0ee7deb 100644 --- a/extensions/matrix/src/matrix/sdk/verification-manager.test.ts +++ b/extensions/matrix/src/matrix/sdk/verification-manager.test.ts @@ -188,6 +188,45 @@ describe("MatrixVerificationManager", () => { expect(secondSummary.chosenMethod).toBe("m.sas.v1"); }); + it("reuses the tracked id when the other device id is populated later", () => { + const manager = new MatrixVerificationManager(); + const first = new MockVerificationRequest({ + transactionId: "txn-device-later", + phase: VerificationPhase.Requested, + }); + const second = new MockVerificationRequest({ + transactionId: "txn-device-later", + phase: VerificationPhase.Ready, + otherDeviceId: "DEVICE_LATER", + pending: false, + }); + + const firstSummary = manager.trackVerificationRequest(first); + const secondSummary = manager.trackVerificationRequest(second); + + expect(secondSummary.id).toBe(firstSummary.id); + expect(secondSummary.otherDeviceId).toBe("DEVICE_LATER"); + expect(manager.listVerifications()).toHaveLength(1); + }); + + it("keeps separate sessions when stable other device ids differ", () => { + const manager = new MatrixVerificationManager(); + const first = new MockVerificationRequest({ + transactionId: "txn-different-devices", + otherDeviceId: "DEVICE_A", + }); + const second = new MockVerificationRequest({ + transactionId: "txn-different-devices", + otherDeviceId: "DEVICE_B", + }); + + const firstSummary = manager.trackVerificationRequest(first); + const secondSummary = manager.trackVerificationRequest(second); + + expect(secondSummary.id).not.toBe(firstSummary.id); + expect(manager.listVerifications()).toHaveLength(2); + }); + it("does not overwrite a different verification request with a colliding transaction ID", async () => { const manager = new MatrixVerificationManager(); const first = new MockVerificationRequest({ diff --git a/extensions/matrix/src/matrix/sdk/verification-manager.ts b/extensions/matrix/src/matrix/sdk/verification-manager.ts index 00133387f0f..1e5ff531ba3 100644 --- a/extensions/matrix/src/matrix/sdk/verification-manager.ts +++ b/extensions/matrix/src/matrix/sdk/verification-manager.ts @@ -204,12 +204,16 @@ export class MatrixVerificationManager { leftIdentity.transactionId === rightIdentity.transactionId && leftIdentity.roomId === rightIdentity.roomId && leftIdentity.otherUserId === rightIdentity.otherUserId && - leftIdentity.otherDeviceId === rightIdentity.otherDeviceId && + this.isSameOptionalIdentityValue(leftIdentity.otherDeviceId, rightIdentity.otherDeviceId) && leftIdentity.isSelfVerification === rightIdentity.isSelfVerification && leftIdentity.initiatedByMe === rightIdentity.initiatedByMe ); } + private isSameOptionalIdentityValue(left: string, right: string): boolean { + return left === "" || right === "" || left === right; + } + private pruneVerificationSessions(nowMs: number): void { for (const [id, session] of this.verificationSessions) { const phase = this.readVerificationPhase(session.request, -1);