mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-29 16:05:13 +00:00
fix(cli): validate tasks audit limit (#84901)
* fix(cli): validate tasks audit limit * docs(changelog): note tasks audit limit validation --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
collectOption,
|
||||
parsePositiveIntOrUndefined,
|
||||
parseStrictPositiveIntOrUndefined,
|
||||
resolveActionArgs,
|
||||
resolveCommandOptionArgs,
|
||||
} from "./helpers.js";
|
||||
@@ -31,6 +32,27 @@ describe("program helpers", () => {
|
||||
expect(parsePositiveIntOrUndefined(value)).toBe(expected);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ value: undefined, expected: undefined },
|
||||
{ value: null, expected: undefined },
|
||||
{ value: "", expected: undefined },
|
||||
{ value: 5, expected: 5 },
|
||||
{ value: 5.9, expected: undefined },
|
||||
{ value: 0, expected: undefined },
|
||||
{ value: -1, expected: undefined },
|
||||
{ value: Number.NaN, expected: undefined },
|
||||
{ value: "10", expected: 10 },
|
||||
{ value: " 10 ", expected: 10 },
|
||||
{ value: "+10", expected: 10 },
|
||||
{ value: "10ms", expected: undefined },
|
||||
{ value: "1.5", expected: undefined },
|
||||
{ value: "0", expected: undefined },
|
||||
{ value: "nope", expected: undefined },
|
||||
{ value: true, expected: undefined },
|
||||
])("parseStrictPositiveIntOrUndefined(%j)", ({ value, expected }) => {
|
||||
expect(parseStrictPositiveIntOrUndefined(value)).toBe(expected);
|
||||
});
|
||||
|
||||
it("resolveActionArgs returns args when command has arg array", () => {
|
||||
const command = new Command();
|
||||
(command as Command & { args?: string[] }).args = ["one", "two"];
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Command } from "commander";
|
||||
import { parseStrictPositiveInteger } from "../../infra/parse-finite-number.js";
|
||||
|
||||
export function collectOption(value: string, previous: string[] = []): string[] {
|
||||
return [...previous, value];
|
||||
@@ -25,6 +26,10 @@ export function parsePositiveIntOrUndefined(value: unknown): number | undefined
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function parseStrictPositiveIntOrUndefined(value: unknown): number | undefined {
|
||||
return parseStrictPositiveInteger(value);
|
||||
}
|
||||
|
||||
export function resolveActionArgs(actionCommand?: Command): string[] {
|
||||
if (!actionCommand) {
|
||||
return [];
|
||||
|
||||
@@ -433,6 +433,16 @@ describe("registerStatusHealthSessionsCommands", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects partially numeric tasks audit limits", async () => {
|
||||
await runCli(["tasks", "--json", "audit", "--limit", "5abc"]);
|
||||
|
||||
expect(runtime.error).toHaveBeenCalledWith(
|
||||
"--limit must be a positive integer, for example --limit 25.",
|
||||
);
|
||||
expect(runtime.exit).toHaveBeenCalledWith(1);
|
||||
expect(tasksAuditCommand).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("routes tasks flow commands through the TaskFlow handlers", async () => {
|
||||
await runCli(["tasks", "flow", "list", "--json", "--status", "blocked"]);
|
||||
expectCommandOptions(flowsListCommand, {});
|
||||
|
||||
@@ -5,7 +5,7 @@ import { formatDocsLink } from "../../terminal/links.js";
|
||||
import { theme } from "../../terminal/theme.js";
|
||||
import { runCommandWithRuntime } from "../cli-utils.js";
|
||||
import { formatHelpExamples } from "../help-format.js";
|
||||
import { parsePositiveIntOrUndefined } from "./helpers.js";
|
||||
import { parsePositiveIntOrUndefined, parseStrictPositiveIntOrUndefined } from "./helpers.js";
|
||||
|
||||
function resolveVerbose(opts: { verbose?: boolean; debug?: boolean }): boolean {
|
||||
return Boolean(opts.verbose || opts.debug);
|
||||
@@ -73,6 +73,16 @@ function parseTimeoutMs(timeout: unknown): number | null | undefined {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function parseTasksAuditLimit(limit: unknown): number | null | undefined {
|
||||
const parsed = parseStrictPositiveIntOrUndefined(limit);
|
||||
if (limit !== undefined && parsed === undefined) {
|
||||
defaultRuntime.error("--limit must be a positive integer, for example --limit 25.");
|
||||
defaultRuntime.exit(1);
|
||||
return null;
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
async function runWithVerboseAndTimeout(
|
||||
opts: { verbose?: boolean; debug?: boolean; timeout?: unknown },
|
||||
action: (params: { verbose: boolean; timeoutMs: number | undefined }) => Promise<void>,
|
||||
@@ -444,6 +454,10 @@ export function registerStatusHealthSessionsCommands(program: Command) {
|
||||
.option("--limit <n>", "Limit displayed findings")
|
||||
.action(async (opts, command) => {
|
||||
const parentOpts = command.parent?.opts() as { json?: boolean } | undefined;
|
||||
const limit = parseTasksAuditLimit(opts.limit);
|
||||
if (limit === null) {
|
||||
return;
|
||||
}
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
const { tasksAuditCommand } = await import("../../commands/tasks.js");
|
||||
await tasksAuditCommand(
|
||||
@@ -464,7 +478,7 @@ export function registerStatusHealthSessionsCommands(program: Command) {
|
||||
| "missing_linked_tasks"
|
||||
| "blocked_task_missing"
|
||||
| undefined,
|
||||
limit: parsePositiveIntOrUndefined(opts.limit),
|
||||
limit,
|
||||
},
|
||||
defaultRuntime,
|
||||
);
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
getVerboseFlag,
|
||||
hasFlag,
|
||||
} from "../argv.js";
|
||||
import { parseStrictPositiveIntOrUndefined } from "./helpers.js";
|
||||
|
||||
type OptionalFlagParse = {
|
||||
ok: boolean;
|
||||
@@ -352,8 +353,12 @@ export function parseTasksAuditRouteArgs(argv: string[]) {
|
||||
if (!code.ok) {
|
||||
return null;
|
||||
}
|
||||
const limit = getPositiveIntFlagValue(argv, "--limit");
|
||||
if (limit === null) {
|
||||
const rawLimit = getFlagValue(argv, "--limit");
|
||||
if (rawLimit === null) {
|
||||
return null;
|
||||
}
|
||||
const limit = rawLimit === undefined ? undefined : parseStrictPositiveIntOrUndefined(rawLimit);
|
||||
if (rawLimit !== undefined && limit === undefined) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -592,6 +592,10 @@ describe("program routes", () => {
|
||||
["tasks", "audit"],
|
||||
["node", "openclaw", "tasks", "audit", "--json", "--limit"],
|
||||
);
|
||||
await expectRunFalse(
|
||||
["tasks", "audit"],
|
||||
["node", "openclaw", "tasks", "audit", "--json", "--limit", "5abc"],
|
||||
);
|
||||
await expectRunFalse(
|
||||
["tasks", "audit"],
|
||||
["node", "openclaw", "tasks", "audit", "--json", "--unknown"],
|
||||
|
||||
Reference in New Issue
Block a user