mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-03 19:24:05 +00:00
fix: reject partial numeric CLI options
This commit is contained in:
@@ -20,6 +20,8 @@ import {
|
||||
buildNodeInvokeParams,
|
||||
callGatewayCli,
|
||||
nodesCallOpts,
|
||||
parseOptionalNodeNonNegativeInteger,
|
||||
parseOptionalNodePositiveInteger,
|
||||
resolveNode,
|
||||
resolveNodeId,
|
||||
} from "./rpc.js";
|
||||
@@ -131,16 +133,17 @@ export function registerNodesCameraCommands(nodes: Command) {
|
||||
);
|
||||
})();
|
||||
|
||||
const maxWidth = opts.maxWidth ? Number.parseInt(opts.maxWidth, 10) : undefined;
|
||||
const maxWidth = parseOptionalNodePositiveInteger(opts.maxWidth, "--max-width");
|
||||
const quality = opts.quality ? Number.parseFloat(opts.quality) : undefined;
|
||||
const delayMs = opts.delayMs ? Number.parseInt(opts.delayMs, 10) : undefined;
|
||||
const delayMs = parseOptionalNodeNonNegativeInteger(opts.delayMs, "--delay-ms");
|
||||
const deviceId = normalizeOptionalString(opts.deviceId);
|
||||
if (deviceId && facings.length > 1) {
|
||||
throw new Error("facing=both is not allowed when --device-id is set");
|
||||
}
|
||||
const timeoutMs = opts.invokeTimeout
|
||||
? Number.parseInt(opts.invokeTimeout, 10)
|
||||
: undefined;
|
||||
const timeoutMs = parseOptionalNodePositiveInteger(
|
||||
opts.invokeTimeout,
|
||||
"--invoke-timeout",
|
||||
);
|
||||
|
||||
const results: Array<{
|
||||
facing: CameraFacing;
|
||||
@@ -216,9 +219,10 @@ export function registerNodesCameraCommands(nodes: Command) {
|
||||
const facing = parseFacing(opts.facing ?? "front");
|
||||
const durationMs = parseDurationMs(opts.duration ?? "3000");
|
||||
const includeAudio = opts.audio !== false;
|
||||
const timeoutMs = opts.invokeTimeout
|
||||
? Number.parseInt(opts.invokeTimeout, 10)
|
||||
: undefined;
|
||||
const timeoutMs = parseOptionalNodePositiveInteger(
|
||||
opts.invokeTimeout,
|
||||
"--invoke-timeout",
|
||||
);
|
||||
const deviceId = normalizeOptionalString(opts.deviceId);
|
||||
|
||||
const invokeParams = buildNodeInvokeParams({
|
||||
|
||||
@@ -6,7 +6,12 @@ import {
|
||||
normalizeOptionalString,
|
||||
} from "../../shared/string-coerce.js";
|
||||
import { getNodesTheme, runNodesCommand } from "./cli-utils.js";
|
||||
import { callGatewayCli, nodesCallOpts, resolveNodeId } from "./rpc.js";
|
||||
import {
|
||||
callGatewayCli,
|
||||
nodesCallOpts,
|
||||
parseOptionalNodePositiveInteger,
|
||||
resolveNodeId,
|
||||
} from "./rpc.js";
|
||||
import type { NodesRpcOpts } from "./types.js";
|
||||
|
||||
const BLOCKED_NODE_INVOKE_COMMANDS = new Set(["system.run", "system.run.prepare"]);
|
||||
@@ -37,9 +42,10 @@ export function registerNodesInvokeCommands(nodes: Command) {
|
||||
);
|
||||
}
|
||||
const params = JSON.parse(opts.params ?? "{}") as unknown;
|
||||
const timeoutMs = opts.invokeTimeout
|
||||
? Number.parseInt(opts.invokeTimeout, 10)
|
||||
: undefined;
|
||||
const timeoutMs = parseOptionalNodePositiveInteger(
|
||||
opts.invokeTimeout,
|
||||
"--invoke-timeout",
|
||||
);
|
||||
|
||||
const invokeParams: Record<string, unknown> = {
|
||||
nodeId,
|
||||
|
||||
@@ -3,7 +3,13 @@ import { randomIdempotencyKey } from "../../gateway/call.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { normalizeOptionalLowercaseString } from "../../shared/string-coerce.js";
|
||||
import { runNodesCommand } from "./cli-utils.js";
|
||||
import { callGatewayCli, nodesCallOpts, resolveNodeId } from "./rpc.js";
|
||||
import {
|
||||
callGatewayCli,
|
||||
nodesCallOpts,
|
||||
parseOptionalNodeNonNegativeInteger,
|
||||
parseOptionalNodePositiveInteger,
|
||||
resolveNodeId,
|
||||
} from "./rpc.js";
|
||||
import type { NodesRpcOpts } from "./types.js";
|
||||
|
||||
export function registerNodesLocationCommands(nodes: Command) {
|
||||
@@ -24,7 +30,7 @@ export function registerNodesLocationCommands(nodes: Command) {
|
||||
.action(async (opts: NodesRpcOpts) => {
|
||||
await runNodesCommand("location get", async () => {
|
||||
const nodeId = await resolveNodeId(opts, opts.node ?? "");
|
||||
const maxAgeMs = opts.maxAge ? Number.parseInt(opts.maxAge, 10) : undefined;
|
||||
const maxAgeMs = parseOptionalNodeNonNegativeInteger(opts.maxAge, "--max-age");
|
||||
const desiredAccuracyRaw = normalizeOptionalLowercaseString(opts.accuracy);
|
||||
const desiredAccuracy =
|
||||
desiredAccuracyRaw === "coarse" ||
|
||||
@@ -32,12 +38,14 @@ export function registerNodesLocationCommands(nodes: Command) {
|
||||
desiredAccuracyRaw === "precise"
|
||||
? desiredAccuracyRaw
|
||||
: undefined;
|
||||
const timeoutMs = opts.locationTimeout
|
||||
? Number.parseInt(opts.locationTimeout, 10)
|
||||
: undefined;
|
||||
const invokeTimeoutMs = opts.invokeTimeout
|
||||
? Number.parseInt(opts.invokeTimeout, 10)
|
||||
: undefined;
|
||||
const timeoutMs = parseOptionalNodePositiveInteger(
|
||||
opts.locationTimeout,
|
||||
"--location-timeout",
|
||||
);
|
||||
const invokeTimeoutMs = parseOptionalNodePositiveInteger(
|
||||
opts.invokeTimeout,
|
||||
"--invoke-timeout",
|
||||
);
|
||||
|
||||
const invokeParams: Record<string, unknown> = {
|
||||
nodeId,
|
||||
|
||||
@@ -3,7 +3,12 @@ import { randomIdempotencyKey } from "../../gateway/call.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
||||
import { getNodesTheme, runNodesCommand } from "./cli-utils.js";
|
||||
import { callGatewayCli, nodesCallOpts, resolveNodeId } from "./rpc.js";
|
||||
import {
|
||||
callGatewayCli,
|
||||
nodesCallOpts,
|
||||
parseOptionalNodePositiveInteger,
|
||||
resolveNodeId,
|
||||
} from "./rpc.js";
|
||||
import type { NodesRpcOpts } from "./types.js";
|
||||
|
||||
export function registerNodesNotifyCommand(nodes: Command) {
|
||||
@@ -26,9 +31,10 @@ export function registerNodesNotifyCommand(nodes: Command) {
|
||||
if (!title && !body) {
|
||||
throw new Error("missing --title or --body");
|
||||
}
|
||||
const invokeTimeout = opts.invokeTimeout
|
||||
? Number.parseInt(opts.invokeTimeout, 10)
|
||||
: undefined;
|
||||
const invokeTimeout = parseOptionalNodePositiveInteger(
|
||||
opts.invokeTimeout,
|
||||
"--invoke-timeout",
|
||||
);
|
||||
const invokeParams: Record<string, unknown> = {
|
||||
nodeId,
|
||||
command: "system.notify",
|
||||
|
||||
@@ -8,7 +8,14 @@ import {
|
||||
} from "../nodes-screen.js";
|
||||
import { parseDurationMs } from "../parse-duration.js";
|
||||
import { runNodesCommand } from "./cli-utils.js";
|
||||
import { buildNodeInvokeParams, callGatewayCli, nodesCallOpts, resolveNodeId } from "./rpc.js";
|
||||
import {
|
||||
buildNodeInvokeParams,
|
||||
callGatewayCli,
|
||||
nodesCallOpts,
|
||||
parseOptionalNodeNonNegativeInteger,
|
||||
parseOptionalNodePositiveInteger,
|
||||
resolveNodeId,
|
||||
} from "./rpc.js";
|
||||
import type { NodesRpcOpts } from "./types.js";
|
||||
|
||||
export function registerNodesScreenCommands(nodes: Command) {
|
||||
@@ -31,11 +38,12 @@ export function registerNodesScreenCommands(nodes: Command) {
|
||||
await runNodesCommand("screen record", async () => {
|
||||
const nodeId = await resolveNodeId(opts, opts.node ?? "");
|
||||
const durationMs = parseDurationMs(opts.duration ?? "");
|
||||
const screenIndex = Number.parseInt(opts.screen ?? "0", 10);
|
||||
const screenIndex = parseOptionalNodeNonNegativeInteger(opts.screen ?? "0", "--screen");
|
||||
const fps = Number.parseFloat(opts.fps ?? "10");
|
||||
const timeoutMs = opts.invokeTimeout
|
||||
? Number.parseInt(opts.invokeTimeout, 10)
|
||||
: undefined;
|
||||
const timeoutMs = parseOptionalNodePositiveInteger(
|
||||
opts.invokeTimeout,
|
||||
"--invoke-timeout",
|
||||
);
|
||||
|
||||
const invokeParams = buildNodeInvokeParams({
|
||||
nodeId,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import type { Command } from "commander";
|
||||
import type { OperatorScope } from "../../gateway/method-scopes.js";
|
||||
import {
|
||||
parseStrictNonNegativeInteger,
|
||||
parseStrictPositiveInteger,
|
||||
} from "../../infra/parse-finite-number.js";
|
||||
import { createLazyImportLoader } from "../../shared/lazy-promise.js";
|
||||
import { resolveNodeFromNodeList } from "../../shared/node-resolve.js";
|
||||
import { normalizeLowercaseStringOrEmpty } from "../../shared/string-coerce.js";
|
||||
@@ -63,6 +67,35 @@ export function buildNodeInvokeParams(params: {
|
||||
return invokeParams;
|
||||
}
|
||||
|
||||
function hasOptionalValue(value: unknown): boolean {
|
||||
return value !== undefined && value !== null && value !== "";
|
||||
}
|
||||
|
||||
export function parseOptionalNodePositiveInteger(value: unknown, flag: string): number | undefined {
|
||||
if (!hasOptionalValue(value)) {
|
||||
return undefined;
|
||||
}
|
||||
const parsed = parseStrictPositiveInteger(value);
|
||||
if (parsed === undefined) {
|
||||
throw new Error(`${flag} must be a positive integer.`);
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
export function parseOptionalNodeNonNegativeInteger(
|
||||
value: unknown,
|
||||
flag: string,
|
||||
): number | undefined {
|
||||
if (!hasOptionalValue(value)) {
|
||||
return undefined;
|
||||
}
|
||||
const parsed = parseStrictNonNegativeInteger(value);
|
||||
if (parsed === undefined) {
|
||||
throw new Error(`${flag} must be a non-negative integer.`);
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
export function unauthorizedHintForMessage(message: string): string | null {
|
||||
const haystack = normalizeLowercaseStringOrEmpty(message);
|
||||
if (
|
||||
|
||||
Reference in New Issue
Block a user