openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
66 lines (65 loc) • 3.79 kB
JavaScript
import { a as normalizeLowercaseStringOrEmpty } from "./string-coerce-mnp54Vah.js";
import { l as normalizeStringEntries } from "./string-normalization-WNUDCpXX.js";
import { k as splitShellArgs } from "./shell-wrapper-resolution-CFL_Vekh.js";
import { t as buildCommandPayloadCandidates } from "./risks-DRxJ1pW5.js";
import { n as analyzeShellCommand } from "./exec-approvals-analysis-B4N2JXzl.js";
//#region src/infra/exec-control-command-guard.ts
function parseExecApprovalShellCommand(raw) {
const match = raw.trimStart().match(/^\/approve(?:@[^\s]+)?\s+([A-Za-z0-9][A-Za-z0-9._:-]*)\s+(allow-once|allow-always|always|deny)\b/i);
if (!match) return null;
return {
approvalId: match[1],
decision: normalizeLowercaseStringOrEmpty(match[2]) === "always" ? "allow-always" : normalizeLowercaseStringOrEmpty(match[2])
};
}
function normalizeCommandBaseName(token) {
if (!token) return "";
return normalizeLowercaseStringOrEmpty(token.split(/[\\/]/u).at(-1)).replace(/\.(?:cmd|exe)$/u, "");
}
function stripOpenClawPackageRunner(argv) {
const commandName = normalizeCommandBaseName(argv[0]);
if (commandName === "openclaw") return argv;
if ((commandName === "pnpm" || commandName === "npm" || commandName === "yarn") && normalizeCommandBaseName(argv[1]) === "openclaw") return argv.slice(1);
if ((commandName === "pnpm" || commandName === "npm" || commandName === "yarn") && (argv[1] === "exec" || argv[1] === "dlx" || argv[1] === "run") && normalizeCommandBaseName(argv[2]) === "openclaw") return argv.slice(2);
if (commandName === "npx" || commandName === "bunx") {
let idx = 1;
while (idx < argv.length) {
const token = argv[idx];
if (token === "--") {
idx += 1;
break;
}
if (!token.startsWith("-") || token === "-") break;
idx += 1;
if ((token === "-p" || token === "--package") && idx < argv.length) idx += 1;
}
if (normalizeCommandBaseName(argv[idx]) === "openclaw") return argv.slice(idx);
}
return argv;
}
function parseOpenClawChannelsLoginShellCommand(raw) {
const argv = splitShellArgs(raw);
if (!argv) return false;
const openclawArgv = stripOpenClawPackageRunner(argv);
return normalizeCommandBaseName(openclawArgv[0]) === "openclaw" && (openclawArgv[1] === "channels" || openclawArgv[1] === "channel") && openclawArgv[2] === "login";
}
function detectUnsafeExecControlShellCommand(command) {
const rawCommand = command.trim();
const analysis = analyzeShellCommand({ command: rawCommand });
const candidates = analysis.ok ? analysis.segments.flatMap((segment) => buildCommandPayloadCandidates(segment.argv)) : normalizeStringEntries(rawCommand.split(/\r?\n/)).flatMap((line) => {
const argv = splitShellArgs(line);
return argv ? buildCommandPayloadCandidates(argv) : [line];
});
for (const candidate of candidates) {
if (parseExecApprovalShellCommand(candidate)) return "approve";
if (parseOpenClawChannelsLoginShellCommand(candidate)) return "channel-login";
}
return null;
}
function rejectUnsafeExecControlShellCommand(command) {
const unsafeKind = detectUnsafeExecControlShellCommand(command);
if (unsafeKind === "approve") throw new Error(["exec cannot run /approve commands.", "Show the /approve command to the user as chat text, or route it through the approval command handler instead of shell execution."].join(" "));
if (unsafeKind === "channel-login") throw new Error(["exec cannot run interactive OpenClaw channel login commands.", "Run `openclaw channels login` in a terminal on the gateway host, or use the channel-specific login agent tool when available (for WhatsApp: `whatsapp_login`)."].join(" "));
}
//#endregion
export { rejectUnsafeExecControlShellCommand as i, parseExecApprovalShellCommand as n, parseOpenClawChannelsLoginShellCommand as r, detectUnsafeExecControlShellCommand as t };