fix: tighten Matrix verification followups

This commit is contained in:
Gustavo Madeira Santana
2026-04-22 22:35:57 -04:00
parent 78d6731efa
commit 5af07d066c
4 changed files with 79 additions and 7 deletions

View File

@@ -373,17 +373,18 @@ describe("matrix CLI verification commands", () => {
roomId: undefined,
});
expect(consoleLogMock).toHaveBeenCalledWith("Verification id: self-verify-1");
expect(consoleLogMock).toHaveBeenCalledWith("Transaction id: txn-1");
expect(consoleLogMock).toHaveBeenCalledWith(
"- Accept the verification request in another Matrix client for this account.",
);
expect(consoleLogMock).toHaveBeenCalledWith(
"- Then run 'openclaw matrix verify start self-verify-1 --account ops' to start SAS verification.",
"- Then run 'openclaw matrix verify start txn-1 --account ops' to start SAS verification.",
);
expect(consoleLogMock).toHaveBeenCalledWith(
"- Run 'openclaw matrix verify sas self-verify-1 --account ops' to display the SAS emoji or decimals.",
"- Run 'openclaw matrix verify sas txn-1 --account ops' to display the SAS emoji or decimals.",
);
expect(consoleLogMock).toHaveBeenCalledWith(
"- When the SAS matches, run 'openclaw matrix verify confirm-sas self-verify-1 --account ops'.",
"- When the SAS matches, run 'openclaw matrix verify confirm-sas txn-1 --account ops'.",
);
});
@@ -470,6 +471,22 @@ describe("matrix CLI verification commands", () => {
);
});
it("prints stable transaction ids in follow-up commands after accepting verification", async () => {
acceptMatrixVerificationMock.mockResolvedValue(
mockMatrixVerificationSummary({
id: "verification-1",
transactionId: "txn-stable",
}),
);
const program = buildProgram();
await program.parseAsync(["matrix", "verify", "accept", "verification-1"], { from: "user" });
expect(consoleLogMock).toHaveBeenCalledWith(
"- Run 'openclaw matrix verify start txn-stable' to start SAS verification.",
);
});
it("confirms, rejects, accepts, starts, and cancels Matrix verification requests", async () => {
acceptMatrixVerificationMock.mockResolvedValue(mockMatrixVerificationSummary({ id: "in-1" }));
startMatrixVerificationMock.mockResolvedValue(mockMatrixVerificationSummary({ id: "in-1" }));

View File

@@ -764,6 +764,10 @@ function printMatrixVerificationSasGuidance(requestId: string, accountId?: strin
]);
}
function getMatrixVerificationCommandId(summary: MatrixCliVerificationSummary): string {
return sanitizeMatrixCliText(summary.transactionId ?? summary.id);
}
async function promptMatrixVerificationSasMatch(): Promise<boolean> {
const { createInterface } = await import("node:readline/promises");
const prompt = createInterface({
@@ -1261,7 +1265,10 @@ export function registerMatrixCli(params: { program: Command }): void {
onText: (summary) => {
printAccountLabel(accountId);
printMatrixVerificationSummary(summary);
printMatrixVerificationRequestGuidance(summary.id, accountId);
printMatrixVerificationRequestGuidance(
getMatrixVerificationCommandId(summary),
accountId,
);
},
errorPrefix: "Verification request failed",
});
@@ -1280,7 +1287,7 @@ export function registerMatrixCli(params: { program: Command }): void {
run: async (accountId, cfg) => await acceptMatrixVerification(id, { accountId, cfg }),
afterText: (summary, accountId) => {
printGuidance([
`Run '${formatMatrixCliCommand(`verify start ${summary.id}`, accountId)}' to start SAS verification.`,
`Run '${formatMatrixCliCommand(`verify start ${getMatrixVerificationCommandId(summary)}`, accountId)}' to start SAS verification.`,
]);
},
errorPrefix: "Verification accept failed",
@@ -1299,7 +1306,7 @@ export function registerMatrixCli(params: { program: Command }): void {
run: async (accountId, cfg) =>
await startMatrixVerification(id, { accountId, cfg, method: "sas" }),
afterText: (summary, accountId) =>
printMatrixVerificationSasGuidance(summary.id, accountId),
printMatrixVerificationSasGuidance(getMatrixVerificationCommandId(summary), accountId),
errorPrefix: "Verification start failed",
});
});

View File

@@ -420,6 +420,54 @@ describe("matrix verification actions", () => {
expect(crypto.startVerification).not.toHaveBeenCalled();
});
it("allows completed self-verification when only backup health remains degraded", async () => {
const requested = {
completed: false,
hasSas: false,
id: "verification-1",
phaseName: "requested",
transactionId: "tx-self",
};
const sas = {
...requested,
hasSas: true,
phaseName: "started",
sas: {
decimal: [1, 2, 3],
},
};
const completed = {
...sas,
completed: true,
phaseName: "done",
};
const crypto = {
confirmVerificationSas: vi.fn(async () => completed),
listVerifications: vi.fn(async () => [sas]),
requestVerification: vi.fn(async () => requested),
startVerification: vi.fn(async () => sas),
};
const bootstrapOwnDeviceVerification = vi.fn(async () => ({
success: false,
error: "Matrix room key backup is not trusted by this device",
verification: mockVerifiedOwnerStatus(),
}));
withStartedActionClientMock.mockImplementation(async (_opts, run) => {
return await run({
bootstrapOwnDeviceVerification,
crypto,
getOwnDeviceVerificationStatus: vi.fn(async () => mockVerifiedOwnerStatus()),
});
});
await expect(
runMatrixSelfVerification({ confirmSas: vi.fn(async () => true), timeoutMs: 500 }),
).resolves.toMatchObject({
completed: true,
deviceOwnerVerified: true,
});
});
it("fails self-verification if SAS completes but full identity trust cannot be established", async () => {
const requested = {
completed: false,

View File

@@ -239,7 +239,7 @@ export async function runMatrixSelfVerification(
allowAutomaticCrossSigningReset: false,
verifyOwnIdentity: true,
});
if (!bootstrap.success) {
if (!bootstrap.verification.verified) {
throw new Error(
`Matrix self-verification completed, but full Matrix identity trust is still incomplete: ${
bootstrap.error ?? formatMatrixOwnerVerificationDiagnostics(bootstrap.verification)