diff --git a/extensions/matrix/src/matrix/sdk/crypto-bootstrap.ts b/extensions/matrix/src/matrix/sdk/crypto-bootstrap.ts index 70cf24c61d6..4a1a03fa83b 100644 --- a/extensions/matrix/src/matrix/sdk/crypto-bootstrap.ts +++ b/extensions/matrix/src/matrix/sdk/crypto-bootstrap.ts @@ -274,6 +274,9 @@ export class MatrixCryptoBootstrapper { // Track incoming requests; verification lifecycle decisions live in the // verification manager so acceptance/start/dedupe share one code path. + // Remote-user verifications are only auto-accepted. The human-operated + // client must explicitly choose "Verify by emoji" so we do not race a + // second SAS start from the bot side and end up with mismatched keys. crypto.on(CryptoEvent.VerificationRequestReceived, async (request) => { const verificationRequest = request as MatrixVerificationRequestLike; try { diff --git a/extensions/matrix/src/matrix/sdk/verification-manager.test.ts b/extensions/matrix/src/matrix/sdk/verification-manager.test.ts index b10fe380627..c9dfa068d69 100644 --- a/extensions/matrix/src/matrix/sdk/verification-manager.test.ts +++ b/extensions/matrix/src/matrix/sdk/verification-manager.test.ts @@ -256,7 +256,7 @@ describe("MatrixVerificationManager", () => { }); }); - it("auto-starts inbound SAS when request becomes ready without a verifier", async () => { + it("does not auto-start non-self inbound SAS when request becomes ready without a verifier", async () => { const verify = vi.fn(async () => {}); const verifier = new MockVerifier( { @@ -276,8 +276,55 @@ describe("MatrixVerificationManager", () => { verify, ); const request = new MockVerificationRequest({ - transactionId: "txn-auto-start-sas", + transactionId: "txn-no-auto-start-dm-sas", initiatedByMe: false, + isSelfVerification: false, + verifier: undefined, + }); + request.startVerification = vi.fn(async (_method: string) => { + request.phase = VerificationPhase.Started; + request.verifier = verifier; + return verifier; + }); + const manager = new MatrixVerificationManager(); + const tracked = manager.trackVerificationRequest(request); + + request.phase = VerificationPhase.Ready; + request.emit(VerificationRequestEvent.Change); + + await vi.waitFor(() => { + expect(manager.listVerifications().find((item) => item.id === tracked.id)?.phase).toBe( + VerificationPhase.Ready, + ); + }); + expect(request.startVerification).not.toHaveBeenCalled(); + expect(verify).not.toHaveBeenCalled(); + expect(manager.listVerifications().find((item) => item.id === tracked.id)?.hasSas).toBe(false); + }); + + it("auto-starts self verification SAS when request becomes ready without a verifier", async () => { + const verify = vi.fn(async () => {}); + const verifier = new MockVerifier( + { + sas: { + decimal: [1234, 5678, 9012], + emoji: [ + ["gift", "Gift"], + ["rocket", "Rocket"], + ["butterfly", "Butterfly"], + ], + }, + confirm: vi.fn(async () => {}), + mismatch: vi.fn(), + cancel: vi.fn(), + }, + null, + verify, + ); + const request = new MockVerificationRequest({ + transactionId: "txn-auto-start-self-sas", + initiatedByMe: false, + isSelfVerification: true, verifier: undefined, }); request.startVerification = vi.fn(async (_method: string) => { diff --git a/extensions/matrix/src/matrix/sdk/verification-manager.ts b/extensions/matrix/src/matrix/sdk/verification-manager.ts index 2134ed2121b..521599395b4 100644 --- a/extensions/matrix/src/matrix/sdk/verification-manager.ts +++ b/extensions/matrix/src/matrix/sdk/verification-manager.ts @@ -313,6 +313,9 @@ export class MatrixVerificationManager { if (this.readRequestValue(session.request, () => session.request.initiatedByMe, true)) { return; } + if (!this.readRequestValue(session.request, () => session.request.isSelfVerification, false)) { + return; + } const phase = this.readRequestValue( session.request, () => session.request.phase,