From 233483d2b913038592c863785e1bc815336a5b69 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 14 Feb 2026 13:25:28 +0100 Subject: [PATCH] refactor(security): centralize dangerous tool lists --- src/acp/client.ts | 19 +--------------- src/gateway/tools-invoke-http.ts | 17 +-------------- src/security/audit.ts | 6 ++++-- src/security/dangerous-tools.ts | 37 ++++++++++++++++++++++++++++++++ 4 files changed, 43 insertions(+), 36 deletions(-) create mode 100644 src/security/dangerous-tools.ts diff --git a/src/acp/client.ts b/src/acp/client.ts index 6d35a8089f1..e5fc447dd8a 100644 --- a/src/acp/client.ts +++ b/src/acp/client.ts @@ -10,24 +10,7 @@ import { spawn, type ChildProcess } from "node:child_process"; import * as readline from "node:readline"; import { Readable, Writable } from "node:stream"; import { ensureOpenClawCliOnPath } from "../infra/path-env.js"; - -/** - * Tools that require explicit user approval in ACP sessions. - * These tools can execute arbitrary code, modify the filesystem, - * or access sensitive resources. - */ -const DANGEROUS_ACP_TOOLS = new Set([ - "exec", - "spawn", - "shell", - "sessions_spawn", - "sessions_send", - "gateway", - "fs_write", - "fs_delete", - "fs_move", - "apply_patch", -]); +import { DANGEROUS_ACP_TOOLS } from "../security/dangerous-tools.js"; const SAFE_AUTO_APPROVE_KINDS = new Set(["read", "search"]); diff --git a/src/gateway/tools-invoke-http.ts b/src/gateway/tools-invoke-http.ts index b6ecac7d4ea..de74df6c258 100644 --- a/src/gateway/tools-invoke-http.ts +++ b/src/gateway/tools-invoke-http.ts @@ -22,6 +22,7 @@ import { logWarn } from "../logger.js"; import { isTestDefaultMemorySlotDisabled } from "../plugins/config-state.js"; import { getPluginToolMeta } from "../plugins/tools.js"; import { isSubagentSessionKey } from "../routing/session-key.js"; +import { DEFAULT_GATEWAY_HTTP_TOOL_DENY } from "../security/dangerous-tools.js"; import { normalizeMessageChannel } from "../utils/message-channel.js"; import { authorizeGatewayConnect, type ResolvedGatewayAuth } from "./auth.js"; import { @@ -36,22 +37,6 @@ import { getBearerToken, getHeader } from "./http-utils.js"; const DEFAULT_BODY_BYTES = 2 * 1024 * 1024; const MEMORY_TOOL_NAMES = new Set(["memory_search", "memory_get"]); -/** - * Tools denied via HTTP /tools/invoke regardless of session policy. - * Prevents RCE and privilege escalation from HTTP API surface. - * Configurable via gateway.tools.{deny,allow} in openclaw.json. - */ -const DEFAULT_GATEWAY_HTTP_TOOL_DENY = [ - // Session orchestration — spawning agents remotely is RCE - "sessions_spawn", - // Cross-session injection — message injection across sessions - "sessions_send", - // Gateway control plane — prevents gateway reconfiguration via HTTP - "gateway", - // Interactive setup — requires terminal QR scan, hangs on HTTP - "whatsapp_login", -]; - type ToolsInvokeBody = { tool?: unknown; action?: unknown; diff --git a/src/security/audit.ts b/src/security/audit.ts index 0f61f03079e..104eecb7888 100644 --- a/src/security/audit.ts +++ b/src/security/audit.ts @@ -33,6 +33,7 @@ import { formatPermissionRemediation, inspectPathPermissions, } from "./audit-fs.js"; +import { DEFAULT_GATEWAY_HTTP_TOOL_DENY } from "./dangerous-tools.js"; export type SecurityAuditSeverity = "info" | "warn" | "critical"; @@ -269,8 +270,9 @@ function collectGatewayConfigFindings( .map((v) => (typeof v === "string" ? v.trim().toLowerCase() : "")) .filter(Boolean), ); - const defaultHttpDeniedTools = ["sessions_spawn", "sessions_send", "gateway", "whatsapp_login"]; - const reenabledOverHttp = defaultHttpDeniedTools.filter((name) => gatewayToolsAllow.has(name)); + const reenabledOverHttp = DEFAULT_GATEWAY_HTTP_TOOL_DENY.filter((name) => + gatewayToolsAllow.has(name), + ); if (reenabledOverHttp.length > 0) { const extraRisk = bind !== "loopback" || tailscaleMode === "funnel"; findings.push({ diff --git a/src/security/dangerous-tools.ts b/src/security/dangerous-tools.ts new file mode 100644 index 00000000000..be585913bde --- /dev/null +++ b/src/security/dangerous-tools.ts @@ -0,0 +1,37 @@ +// Shared tool-risk constants. +// Keep these centralized so gateway HTTP restrictions, security audits, and ACP prompts don't drift. + +/** + * Tools denied via Gateway HTTP `POST /tools/invoke` by default. + * These are high-risk because they enable session orchestration, control-plane actions, + * or interactive flows that don't make sense over a non-interactive HTTP surface. + */ +export const DEFAULT_GATEWAY_HTTP_TOOL_DENY = [ + // Session orchestration — spawning agents remotely is RCE + "sessions_spawn", + // Cross-session injection — message injection across sessions + "sessions_send", + // Gateway control plane — prevents gateway reconfiguration via HTTP + "gateway", + // Interactive setup — requires terminal QR scan, hangs on HTTP + "whatsapp_login", +] as const; + +/** + * ACP tools that should always require explicit user approval. + * ACP is an automation surface; we never want "silent yes" for mutating/execution tools. + */ +export const DANGEROUS_ACP_TOOL_NAMES = [ + "exec", + "spawn", + "shell", + "sessions_spawn", + "sessions_send", + "gateway", + "fs_write", + "fs_delete", + "fs_move", + "apply_patch", +] as const; + +export const DANGEROUS_ACP_TOOLS = new Set(DANGEROUS_ACP_TOOL_NAMES);