Matrix: keep SAS notices in the verification DM

This commit is contained in:
Gustavo Madeira Santana
2026-03-14 02:58:53 +00:00
parent 77aa26e0c8
commit caebc75456
2 changed files with 97 additions and 0 deletions

View File

@@ -391,6 +391,74 @@ describe("registerMatrixMonitorEvents verification routing", () => {
expect(body).toContain("SAS decimal: 4321 8765 2109");
});
it("prefers the most recent verification DM over the canonical active DM for unmapped SAS summaries", async () => {
const { sendMessage, roomEventListener, verificationSummaryListener } = createHarness({
joinedMembersByRoom: {
"!dm-active:example.org": ["@alice:example.org", "@bot:example.org"],
"!dm-current:example.org": ["@alice:example.org", "@bot:example.org"],
},
});
if (!verificationSummaryListener) {
throw new Error("verification.summary listener was not registered");
}
roomEventListener("!dm-current:example.org", {
event_id: "$start-current",
sender: "@alice:example.org",
type: "m.key.verification.start",
origin_server_ts: Date.now(),
content: {
"m.relates_to": { event_id: "$req-current" },
},
});
await vi.waitFor(() => {
const bodies = (sendMessage.mock.calls as unknown[][]).map((call) =>
String((call[1] as { body?: string } | undefined)?.body ?? ""),
);
expect(bodies.some((body) => body.includes("Matrix verification started with"))).toBe(true);
});
verificationSummaryListener({
id: "verification-current-room",
otherUserId: "@alice:example.org",
isSelfVerification: false,
initiatedByMe: false,
phase: 3,
phaseName: "started",
pending: true,
methods: ["m.sas.v1"],
canAccept: false,
hasSas: true,
sas: {
decimal: [2468, 1357, 9753],
emoji: [
["🔔", "Bell"],
["📁", "Folder"],
["🐴", "Horse"],
],
},
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(() => {
const bodies = (sendMessage.mock.calls as unknown[][]).map((call) =>
String((call[1] as { body?: string } | undefined)?.body ?? ""),
);
expect(bodies.some((body) => body.includes("SAS decimal: 2468 1357 9753"))).toBe(true);
});
const calls = sendMessage.mock.calls as unknown[][];
const sasCall = calls.find((call) =>
String((call[1] as { body?: string } | undefined)?.body ?? "").includes(
"SAS decimal: 2468 1357 9753",
),
);
expect((sasCall?.[0] ?? "") as string).toBe("!dm-current:example.org");
});
it("retries SAS notice lookup when start arrives before SAS payload is available", async () => {
vi.useFakeTimers();
const verifications: Array<{

View File

@@ -310,6 +310,7 @@ export function createMatrixVerificationEventRouter(params: {
const routedVerificationSasFingerprints = new Set<string>();
const routedVerificationStageNotices = new Set<string>();
const verificationFlowRooms = new Map<string, string>();
const verificationUserRooms = new Map<string, string>();
function rememberVerificationRoom(roomId: string, event: MatrixRawEvent, flowId: string | null) {
for (const candidate of resolveVerificationFlowCandidates({ event, flowId })) {
@@ -323,6 +324,22 @@ export function createMatrixVerificationEventRouter(params: {
}
}
function rememberVerificationUserRoom(remoteUserId: string, roomId: string): void {
const normalizedUserId = trimMaybeString(remoteUserId);
const normalizedRoomId = trimMaybeString(roomId);
if (!normalizedUserId || !normalizedRoomId) {
return;
}
verificationUserRooms.delete(normalizedUserId);
verificationUserRooms.set(normalizedUserId, normalizedRoomId);
if (verificationUserRooms.size > MAX_TRACKED_VERIFICATION_EVENTS) {
const oldest = verificationUserRooms.keys().next().value;
if (typeof oldest === "string") {
verificationUserRooms.delete(oldest);
}
}
}
async function resolveSummaryRoomId(
summary: MatrixVerificationSummaryLike,
): Promise<string | null> {
@@ -340,6 +357,17 @@ export function createMatrixVerificationEventRouter(params: {
if (!remoteUserId) {
return null;
}
const recentRoomId = trimMaybeString(verificationUserRooms.get(remoteUserId));
if (
recentRoomId &&
(await isStrictDirectRoom({
client: params.client,
roomId: recentRoomId,
remoteUserId,
}))
) {
return recentRoomId;
}
const inspection = await inspectMatrixDirectRooms({
client: params.client,
remoteUserId,
@@ -406,6 +434,7 @@ export function createMatrixVerificationEventRouter(params: {
);
return;
}
rememberVerificationUserRoom(senderId, roomId);
if (!trackBounded(routedVerificationEvents, sourceFingerprint)) {
return;
}