Matrix: route SAS notices through active DM

This commit is contained in:
Gustavo Madeira Santana
2026-03-13 17:55:39 +00:00
parent 45c0f90d48
commit 77aa26e0c8
2 changed files with 65 additions and 5 deletions

View File

@@ -65,6 +65,7 @@ function createHarness(params?: {
async (roomId: string) =>
params?.joinedMembersByRoom?.[roomId] ?? ["@bot:example.org", "@alice:example.org"],
),
getJoinedRooms: vi.fn(async () => Object.keys(params?.joinedMembersByRoom ?? {})),
...(params?.cryptoAvailable === false
? {}
: {
@@ -346,6 +347,50 @@ describe("registerMatrixMonitorEvents verification routing", () => {
});
});
it("posts SAS notices from summary updates using the active strict DM when room mapping is missing", async () => {
const { sendMessage, verificationSummaryListener } = createHarness({
joinedMembersByRoom: {
"!dm-active:example.org": ["@alice:example.org", "@bot:example.org"],
},
});
if (!verificationSummaryListener) {
throw new Error("verification.summary listener was not registered");
}
verificationSummaryListener({
id: "verification-unmapped",
otherUserId: "@alice:example.org",
isSelfVerification: false,
initiatedByMe: false,
phase: 3,
phaseName: "started",
pending: true,
methods: ["m.sas.v1"],
canAccept: false,
hasSas: true,
sas: {
decimal: [4321, 8765, 2109],
emoji: [
["🚀", "Rocket"],
["🦋", "Butterfly"],
["📕", "Book"],
],
},
hasReciprocateQr: false,
completed: false,
createdAt: new Date("2026-02-25T21:42:54.000Z").toISOString(),
updatedAt: new Date("2026-02-25T21:42:55.000Z").toISOString(),
});
await vi.waitFor(() => {
expect(sendMessage).toHaveBeenCalledTimes(1);
});
const roomId = (sendMessage.mock.calls[0]?.[0] ?? "") as string;
const body = getSentNoticeBody(sendMessage, 0);
expect(roomId).toBe("!dm-active:example.org");
expect(body).toContain("SAS decimal: 4321 8765 2109");
});
it("retries SAS notice lookup when start arrives before SAS payload is available", async () => {
vi.useFakeTimers();
const verifications: Array<{

View File

@@ -1,3 +1,4 @@
import { inspectMatrixDirectRooms } from "../direct-management.js";
import { isStrictDirectRoom } from "../direct-room.js";
import type { MatrixClient } from "../sdk.js";
import type { MatrixRawEvent } from "./types.js";
@@ -322,18 +323,32 @@ export function createMatrixVerificationEventRouter(params: {
}
}
function resolveSummaryRoomId(summary: MatrixVerificationSummaryLike): string | null {
return (
async function resolveSummaryRoomId(
summary: MatrixVerificationSummaryLike,
): Promise<string | null> {
const mappedRoomId =
trimMaybeString(summary.roomId) ??
trimMaybeString(
summary.transactionId ? verificationFlowRooms.get(summary.transactionId) : null,
) ??
trimMaybeString(verificationFlowRooms.get(summary.id))
);
trimMaybeString(verificationFlowRooms.get(summary.id));
if (mappedRoomId) {
return mappedRoomId;
}
const remoteUserId = trimMaybeString(summary.otherUserId);
if (!remoteUserId) {
return null;
}
const inspection = await inspectMatrixDirectRooms({
client: params.client,
remoteUserId,
}).catch(() => null);
return trimMaybeString(inspection?.activeRoomId);
}
async function routeVerificationSummary(summary: MatrixVerificationSummaryLike): Promise<void> {
const roomId = resolveSummaryRoomId(summary);
const roomId = await resolveSummaryRoomId(summary);
if (!roomId || !isActiveVerificationSummary(summary)) {
return;
}