fix(matrix): validate CLI numeric option ranges

Validates Matrix CLI numeric option ranges before invoking setup or verification side effects.

`--initial-sync-limit` must now be non-negative, and `--timeout-ms` must now be positive.

Original PR by @rohitjavvadi.

Verification:
- `node scripts/run-vitest.mjs extensions/matrix/src/cli.test.ts --maxWorkers=1`
- autoreview clean
- Crabbox AWS `cbx_5c32f138ab3a` / `swift-lobster`, run `run_6e133b8b82e7`: `check:changed` passed
- exact PR head CI green: `d75f118299029b0516311646276cd2d6582379c5`
This commit is contained in:
Rohit
2026-06-14 04:46:39 +05:30
committed by GitHub
parent 894f521aa5
commit 3429e33feb
2 changed files with 68 additions and 7 deletions

View File

@@ -401,6 +401,20 @@ describe("matrix CLI verification commands", () => {
expect(runMatrixSelfVerificationMock).not.toHaveBeenCalled();
});
it("rejects non-positive Matrix self-verification timeout values", async () => {
const program = buildProgram();
await program.parseAsync(["matrix", "verify", "self", "--timeout-ms", "-1"], {
from: "user",
});
expect(process.exitCode).toBe(1);
expect(consoleErrorMock).toHaveBeenCalledWith(
"Self-verification failed: --timeout-ms must be a positive integer",
);
expect(runMatrixSelfVerificationMock).not.toHaveBeenCalled();
});
it("requests Matrix self-verification and prints the follow-up SAS commands", async () => {
requestMatrixVerificationMock.mockResolvedValue(
mockMatrixVerificationSummary({
@@ -1076,6 +1090,34 @@ describe("matrix CLI verification commands", () => {
);
});
it("rejects negative Matrix initial sync limits at the CLI boundary", async () => {
const program = buildProgram();
await program.parseAsync(
[
"matrix",
"account",
"add",
"--homeserver",
"https://matrix.example.org",
"--user-id",
"@ops:example.org",
"--password",
"secret",
"--initial-sync-limit",
"-1",
],
{ from: "user" },
);
expect(process.exitCode).toBe(1);
expect(consoleErrorMock).toHaveBeenCalledWith(
"Account setup failed: --initial-sync-limit must be a non-negative integer",
);
expect(matrixSetupValidateInputMock).not.toHaveBeenCalled();
expect(matrixRuntimeReplaceConfigFileMock).not.toHaveBeenCalled();
});
it("enables E2EE and bootstraps verification from matrix account add", async () => {
matrixRuntimeLoadConfigMock.mockReturnValue({ channels: {} });
matrixSetupApplyAccountConfigMock.mockImplementation(

View File

@@ -232,7 +232,11 @@ function configureCliLogMode(verbose: boolean): void {
setMatrixSdkConsoleLogging(verbose);
}
function parseOptionalInt(value: string | undefined, fieldName: string): number | undefined {
function parseOptionalInt(
value: string | undefined,
fieldName: string,
opts: { min?: number } = {},
): number | undefined {
const trimmed = value?.trim();
if (!trimmed) {
return undefined;
@@ -244,6 +248,13 @@ function parseOptionalInt(value: string | undefined, fieldName: string): number
if (parsed === undefined) {
throw new Error(`${fieldName} must be an integer`);
}
if (opts.min !== undefined && parsed < opts.min) {
throw new Error(
opts.min === 1
? `${fieldName} must be a positive integer`
: `${fieldName} must be a non-negative integer`,
);
}
return parsed;
}
@@ -289,6 +300,9 @@ async function addMatrixAccount(params: {
useEnv?: boolean;
enableEncryption?: boolean;
}): Promise<MatrixCliAccountAddResult> {
const initialSyncLimit = parseOptionalInt(params.initialSyncLimit, "--initial-sync-limit", {
min: 0,
});
const runtime = getMatrixRuntime();
const cfg = runtime.config.current() as CoreConfig;
if (!matrixSetupAdapter.applyAccountConfig) {
@@ -305,7 +319,7 @@ async function addMatrixAccount(params: {
accessToken: params.accessToken,
password: params.password,
deviceName: params.deviceName,
initialSyncLimit: parseOptionalInt(params.initialSyncLimit, "--initial-sync-limit"),
initialSyncLimit,
useEnv: params.useEnv === true,
};
const accountId =
@@ -1159,15 +1173,18 @@ async function runMatrixCliVerificationSummaryCommand(params: {
async function runMatrixCliSelfVerificationCommand(
options: MatrixCliSelfVerificationCommandOptions,
): Promise<void> {
const { accountId, cfg } = resolveMatrixCliAccountContext(options.account);
let resolvedAccountId: string | undefined;
await runMatrixCliCommand({
verbose: options.verbose === true,
json: false,
run: async () =>
await runMatrixSelfVerification({
run: async () => {
const timeoutMs = parseOptionalInt(options.timeoutMs, "--timeout-ms", { min: 1 });
const { accountId, cfg } = resolveMatrixCliAccountContext(options.account);
resolvedAccountId = accountId;
return await runMatrixSelfVerification({
accountId,
cfg,
timeoutMs: parseOptionalInt(options.timeoutMs, "--timeout-ms"),
timeoutMs,
onRequested: (summary) => {
printAccountLabel(accountId);
printMatrixVerificationSummary(summary);
@@ -1184,7 +1201,8 @@ async function runMatrixCliSelfVerificationCommand(
console.log("Compare this SAS with the other Matrix client.");
},
confirmSas: async () => await promptMatrixVerificationSasMatch(),
}),
});
},
onText: (summary, verbose) => {
printMatrixVerificationSummary(summary);
console.log(`Device verified by owner: ${summary.deviceOwnerVerified ? "yes" : "no"}`);
@@ -1196,6 +1214,7 @@ async function runMatrixCliSelfVerificationCommand(
console.log("Self-verification complete.");
},
onTextError: () => {
const accountId = resolvedAccountId ?? options.account;
printGuidance([
`Run ${formatMatrixCliCommand("verify self", accountId)} again and accept the request in another verified Matrix client for this account.`,
`Then run ${formatMatrixCliCommand("verify status --verbose", accountId)} to confirm Cross-signing verified: yes and Signed by owner: yes.`,