Skip to content

Commit 8855a4a

Browse files
authored
fix(update): require integer timeout values (#83310)
* fix(update): require integer timeout values * fix(update): reject blank timeout values
1 parent 4b4048f commit 8855a4a

2 files changed

Lines changed: 46 additions & 4 deletions

File tree

src/cli/update-cli/shared.command-runner.test.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { beforeEach, describe, expect, it, vi } from "vitest";
2-
import { createGlobalCommandRunner } from "./shared.js";
2+
import { defaultRuntime } from "../../runtime.js";
3+
import { createGlobalCommandRunner, parseTimeoutMsOrExit } from "./shared.js";
34

45
const runCommandWithTimeout = vi.hoisted(() => vi.fn());
56

@@ -48,4 +49,41 @@ describe("createGlobalCommandRunner", () => {
4849
code: 17,
4950
});
5051
});
52+
53+
it("requires timeout values to be complete positive integer seconds", () => {
54+
const error = vi.spyOn(defaultRuntime, "error").mockImplementation(() => undefined);
55+
const exit = vi.spyOn(defaultRuntime, "exit").mockImplementation(() => undefined as never);
56+
57+
try {
58+
expect(parseTimeoutMsOrExit("1.5")).toBeNull();
59+
expect(parseTimeoutMsOrExit("10abc")).toBeNull();
60+
expect(parseTimeoutMsOrExit("0")).toBeNull();
61+
expect(parseTimeoutMsOrExit("-1")).toBeNull();
62+
expect(parseTimeoutMsOrExit(" ")).toBeNull();
63+
64+
expect(error).toHaveBeenCalledTimes(5);
65+
expect(error).toHaveBeenCalledWith("--timeout must be a positive integer (seconds)");
66+
expect(exit).toHaveBeenCalledTimes(5);
67+
expect(exit).toHaveBeenCalledWith(1);
68+
} finally {
69+
error.mockRestore();
70+
exit.mockRestore();
71+
}
72+
});
73+
74+
it("parses complete positive integer timeout values as milliseconds", () => {
75+
const error = vi.spyOn(defaultRuntime, "error").mockImplementation(() => undefined);
76+
const exit = vi.spyOn(defaultRuntime, "exit").mockImplementation(() => undefined as never);
77+
78+
try {
79+
expect(parseTimeoutMsOrExit(" 10 ")).toBe(10_000);
80+
expect(parseTimeoutMsOrExit("001")).toBe(1_000);
81+
expect(parseTimeoutMsOrExit()).toBeUndefined();
82+
expect(error).not.toHaveBeenCalled();
83+
expect(exit).not.toHaveBeenCalled();
84+
} finally {
85+
error.mockRestore();
86+
exit.mockRestore();
87+
}
88+
});
5189
});

src/cli/update-cli/shared.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,17 @@ export type UpdateWizardOptions = {
5454
const INVALID_TIMEOUT_ERROR = "--timeout must be a positive integer (seconds)";
5555

5656
export function parseTimeoutMsOrExit(timeout?: string): number | undefined | null {
57-
const timeoutMs = timeout ? Number.parseInt(timeout, 10) * 1000 : undefined;
58-
if (timeoutMs !== undefined && (Number.isNaN(timeoutMs) || timeoutMs <= 0)) {
57+
if (timeout === undefined) {
58+
return undefined;
59+
}
60+
const trimmed = timeout.trim();
61+
const seconds = Number(trimmed);
62+
if (!/^\d+$/u.test(trimmed) || !Number.isSafeInteger(seconds) || seconds <= 0) {
5963
defaultRuntime.error(INVALID_TIMEOUT_ERROR);
6064
defaultRuntime.exit(1);
6165
return null;
6266
}
63-
return timeoutMs;
67+
return seconds * 1000;
6468
}
6569

6670
const OPENCLAW_REPO_URL = "https://github.com/openclaw/openclaw.git";

0 commit comments

Comments
 (0)