fix(qa): reject out-of-range lab CLI ports

This commit is contained in:
Vincent Koc
2026-06-23 10:12:09 +02:00
parent d63a73a1b8
commit 3d8d45fb0d
2 changed files with 43 additions and 7 deletions

View File

@@ -800,6 +800,33 @@ describe("qa cli registration", () => {
await expect(invalidProgram.parseAsync(["node", "openclaw", ...args])).rejects.toThrow(message);
});
it.each([
[["qa", "ui", "--port", "65536"], "--port must be a TCP port between 1 and 65535."],
[
["qa", "ui", "--advertise-port", "999999"],
"--advertise-port must be a TCP port between 1 and 65535.",
],
[
["qa", "docker-scaffold", "--output-dir", "/tmp/qa", "--gateway-port", "65536"],
"--gateway-port must be a TCP port between 1 and 65535.",
],
[
["qa", "up", "--qa-lab-port", "65536"],
"--qa-lab-port must be a TCP port between 1 and 65535.",
],
[["qa", "aimock", "--port", "65536"], "--port must be a TCP port between 1 and 65535."],
])("rejects out-of-range QA port option %j", async (args, message) => {
const invalidProgram = new Command();
invalidProgram.exitOverride();
invalidProgram.configureOutput({
writeErr: () => {},
writeOut: () => {},
});
registerQaLabCli(invalidProgram);
await expect(invalidProgram.parseAsync(["node", "openclaw", ...args])).rejects.toThrow(message);
});
it("shows an enable hint when a discovered runner plugin is installed but blocked", async () => {
listQaRunnerCliContributions.mockReset().mockReturnValue([createBlockedQaRunnerContribution()]);
const blockedProgram = new Command();

View File

@@ -62,6 +62,7 @@ const QA_RUN_PROFILE_ONLY_OPTIONS = [
] as const;
const QA_RUN_SELF_CHECK_ONLY_OPTIONS = [{ optionName: "output", flag: "--output" }] as const;
const MAX_QA_CLI_TCP_PORT = 65_535;
type QaSuiteCliOptions = QaScenarioRunCliOptions & {
channelDriver?: QaSuiteCommandOptions["channelDriver"];
@@ -105,6 +106,14 @@ function parseQaCliPositiveIntegerOption(value: string, flag: string): number {
return parsed;
}
function parseQaCliTcpPortOption(value: string, flag: string): number {
const parsed = parseQaCliPositiveIntegerOption(value, flag);
if (parsed > MAX_QA_CLI_TCP_PORT) {
throw invalidQaCliArgument(`${flag} must be a TCP port between 1 and 65535.`);
}
return parsed;
}
function parseQaEvidenceModeOption(value: string): QaProfileCommandOptions["evidenceMode"] {
const evidenceMode = value.trim();
if (evidenceMode === "full" || evidenceMode === "slim") {
@@ -867,11 +876,11 @@ export function registerQaLabCli(program: Command) {
.option("--repo-root <path>", "Repository root to target when running from a neutral cwd")
.option("--host <host>", "Bind host", "127.0.0.1")
.option("--port <port>", "Bind port", (value: string) =>
parseQaCliPositiveIntegerOption(value, "--port"),
parseQaCliTcpPortOption(value, "--port"),
)
.option("--advertise-host <host>", "Optional public host to advertise in bootstrap payloads")
.option("--advertise-port <port>", "Optional public port to advertise", (value: string) =>
parseQaCliPositiveIntegerOption(value, "--advertise-port"),
parseQaCliTcpPortOption(value, "--advertise-port"),
)
.option("--control-ui-url <url>", "Optional Control UI URL to embed beside the QA panel")
.option(
@@ -909,10 +918,10 @@ export function registerQaLabCli(program: Command) {
.option("--repo-root <path>", "Repository root to target when running from a neutral cwd")
.requiredOption("--output-dir <path>", "Output directory for docker-compose + state files")
.option("--gateway-port <port>", "Gateway host port", (value: string) =>
parseQaCliPositiveIntegerOption(value, "--gateway-port"),
parseQaCliTcpPortOption(value, "--gateway-port"),
)
.option("--qa-lab-port <port>", "QA lab host port", (value: string) =>
parseQaCliPositiveIntegerOption(value, "--qa-lab-port"),
parseQaCliTcpPortOption(value, "--qa-lab-port"),
)
.option("--provider-base-url <url>", "Provider base URL for the QA gateway")
.option("--image <name>", "Prebaked image name", "openclaw:qa-local-prebaked")
@@ -950,10 +959,10 @@ export function registerQaLabCli(program: Command) {
.option("--repo-root <path>", "Repository root to target when running from a neutral cwd")
.option("--output-dir <path>", "Output directory for docker-compose + state files")
.option("--gateway-port <port>", "Gateway host port", (value: string) =>
parseQaCliPositiveIntegerOption(value, "--gateway-port"),
parseQaCliTcpPortOption(value, "--gateway-port"),
)
.option("--qa-lab-port <port>", "QA lab host port", (value: string) =>
parseQaCliPositiveIntegerOption(value, "--qa-lab-port"),
parseQaCliTcpPortOption(value, "--qa-lab-port"),
)
.option("--provider-base-url <url>", "Provider base URL for the QA gateway")
.option("--image <name>", "Image tag", "openclaw:qa-local-prebaked")
@@ -985,7 +994,7 @@ export function registerQaLabCli(program: Command) {
.description(providerCommand.description)
.option("--host <host>", "Bind host", "127.0.0.1")
.option("--port <port>", "Bind port", (value: string) =>
parseQaCliPositiveIntegerOption(value, "--port"),
parseQaCliTcpPortOption(value, "--port"),
)
.action(async (opts: { host?: string; port?: number }) => {
await runQaProviderServer(providerCommand.providerMode, opts);