执行审批
执行审批是伴侣应用/节点主机的安全护栏,控制沙箱化代理能否在真实主机(gateway 或 node)上运行命令。可以理解为一道安全联锁:只有策略 + 允许列表 +(可选的)用户批准全部同意时,命令才被允许执行。执行审批是在工具策略和提权门控之外的额外检查(除非提权设为 full,那会跳过审批)。生效策略取 tools.exec.* 和审批默认值中更严格的那个;如果审批字段被省略,使用 tools.exec 的值。
如果伴侣应用 UI 不可用,任何需要提示的请求由 ask 回退决定(默认:拒绝)。
适用范围
执行审批在执行主机上本地强制执行:
- 网关主机 → 网关机器上的
openclaw进程 - 节点主机 → 节点运行器(macOS 伴侣应用或无头节点主机)
信任模型说明:
- 经网关认证的调用者是该网关的受信操作者。
- 配对的节点将受信操作者能力扩展到节点主机上。
- 执行审批降低意外执行风险,但不是按用户的认证边界。
- 批准的节点主机运行绑定规范执行上下文:规范 cwd、精确 argv、存在时的环境绑定、适用时的固定可执行路径。
- 对于 shell 脚本和直接解释器/运行时文件调用,OpenClaw 还会尝试绑定一个具体的本地文件操作数。如果该绑定文件在批准后、执行前发生变更,运行会被拒绝而非执行漂移的内容。
- 这个文件绑定是尽力而为的,不是每个解释器/运行时加载器路径的完整语义模型。如果审批模式无法识别出恰好一个具体的本地文件来绑定,它会拒绝签发审批支持的运行,而不是假装完全覆盖。
macOS 分层:
- 节点主机服务通过本地 IPC 将
system.run转发给 macOS 应用。 - macOS 应用强制审批 + 在 UI 上下文中执行命令。
设置和存储
审批存储在执行主机上的本地 JSON 文件中:
~/.openclaw/exec-approvals.json
示例结构:
{
"version": 1,
"socket": {
"path": "~/.openclaw/exec-approvals.sock",
"token": "base64url-token"
},
"defaults": {
"security": "deny",
"ask": "on-miss",
"askFallback": "deny",
"autoAllowSkills": false
},
"agents": {
"main": {
"security": "allowlist",
"ask": "on-miss",
"askFallback": "deny",
"autoAllowSkills": true,
"allowlist": [
{
"id": "B0C8C0B3-2C2D-4F8A-9A3C-5A4B3C2D1E0F",
"pattern": "~/Projects/**/bin/rg",
"lastUsedAt": 1737150000000,
"lastUsedCommand": "rg -n TODO",
"lastResolvedPath": "/Users/user/Projects/.../bin/rg"
}
]
}
}
}
策略开关
安全级别(exec.security)
- deny:阻止所有主机执行请求。
- allowlist:仅允许在允许列表中的命令。
- full:允许一切(等同于提权)。
Ask(exec.ask)
- off:从不提示。
- on-miss:仅在允许列表不匹配时提示。
- always:每次命令都提示。
Ask 回退(askFallback)
如果需要提示但没有可达的 UI,回退策略决定:
- deny:阻止。
- allowlist:仅允许列表匹配时放行。
- full:放行。
允许列表(按代理)
允许列表是按代理隔离的。如果有多个代理,在 macOS 应用中切换要编辑的代理。模式匹配是大小写不敏感的 glob 匹配。模式应该解析到二进制路径(仅基础名的条目会被忽略)。旧版 agents.default 条目在加载时会迁移到 agents.main。
示例:
~/Projects/**/bin/peekaboo~/.local/bin/*/opt/homebrew/bin/rg
每个允许列表条目追踪:
- id 稳定的 UUID,用于 UI 标识(可选)
- 上次使用时间戳
- 上次使用的命令
- 上次解析的路径
自动允许技能 CLI
启用自动允许技能 CLI 后,已知技能引用的可执行文件在节点上(macOS 节点或无头节点主机)被视为已列入允许列表。这通过网关 RPC 的 skills.bins 获取技能二进制列表。如果你想要严格的手动允许列表,请禁用它。
重要的信任说明:
- 这是一个隐式便捷允许列表,和手动路径允许列表条目是分开的。
- 适用于网关和节点在同一信任边界内的受信操作者环境。
- 如果你需要严格的显式信任,保持
autoAllowSkills: false并只使用手动路径允许列表条目。
安全二进制(仅 stdin)
tools.exec.safeBins 定义了一小组仅 stdin 的二进制(比如 jq),可以在允许列表模式下运行而无需显式允许列表条目。安全二进制拒绝位置文件参数和路径样的令牌,所以只能操作输入流。把它当作流过滤器的窄通道快捷路径,而非通用信任列表。不要把解释器或运行时二进制(比如 python3、node、ruby、bash、sh、zsh)加入 safeBins。如果命令设计上能执行代码、运行子命令或读取文件,优先使用显式允许列表条目并保持审批提示启用。自定义安全二进制必须在 tools.exec.safeBinProfiles.<bin> 中定义显式配置。验证完全基于 argv 形状(不做主机文件系统存在性检查),防止 allow/deny 差异泄露文件存在信息。文件导向的选项对默认安全二进制会被拒绝(比如 sort -o、sort --output、sort --files0-from、sort --compress-program、sort --random-source、sort --temporary-directory/-T、wc --files0-from、jq -f/--from-file、grep -f/--file)。安全二进制还对破坏仅 stdin 行为的选项强制执行显式的每二进制标志策略(比如 sort -o/--output/--compress-program 和 grep 递归标志)。长选项在安全二进制模式下采用失败关闭验证:未知标志和歧义缩写会被拒绝。各安全二进制配置拒绝的标志:
grep:--dereference-recursive,--directories,--exclude-from,--file,--recursive,-R,-d,-f,-rjq:--argfile,--from-file,--library-path,--rawfile,--slurpfile,-L,-fsort:--compress-program,--files0-from,--output,--random-source,--temporary-directory,-T,-owc:--files0-from
安全二进制还强制在执行时将 argv 令牌视为字面文本(不做 glob 展开和 $VARS 扩展),防止用 * 或 $HOME/... 之类的模式偷偷读取文件。安全二进制还必须从受信的二进制目录解析(系统默认值加上可选的 tools.exec.safeBinTrustedDirs)。PATH 条目永远不会被自动信任。默认受信安全二进制目录有意保持最小:/bin、/usr/bin。如果你的安全二进制可执行文件在包管理器/用户路径下(比如 /opt/homebrew/bin、/usr/local/bin、/opt/local/bin、/snap/bin),请显式添加到 tools.exec.safeBinTrustedDirs。允许列表模式下不自动允许 shell 链接和重定向。
Shell 链接(&&、||、;)在每个顶层段都满足允许列表(包括安全二进制或技能自动允许)时允许。重定向在允许列表模式下不受支持。命令替换($()/反引号)在允许列表解析时被拒绝,包括双引号内的;如果需要字面的 $() 文本,使用单引号。在 macOS 伴侣应用审批中,包含 shell 控制或扩展语法(&&、||、;、|、`、$、<、>、(、))的原始 shell 文本被视为允许列表未命中,除非 shell 二进制本身在允许列表中。对于 shell 包装器(bash|sh|zsh ... -c/-lc),请求范围的环境覆盖被缩减为一个小的显式允许列表(TERM、LANG、LC_*、COLORTERM、NO_COLOR、FORCE_COLOR)。对于允许列表模式下的始终允许决策,已知的分发包装器(env、nice、nohup、stdbuf、timeout)会持久化内部可执行路径而非包装器路径。Shell 多路复用器(busybox、toybox)对 shell applet(sh、ash 等)也会解包,持久化内部可执行文件而非多路复用器二进制。如果包装器或多路复用器无法安全解包,则不自动持久化允许列表条目。
默认安全二进制:jq、cut、uniq、head、tail、tr、wc。
grep 和 sort 不在默认列表中。如果选择启用,为它们的非 stdin 工作流保留显式允许列表条目。在安全二进制模式下使用 grep 时,用 -e/--regexp 提供模式;位置模式形式被拒绝,防止文件操作数被伪装成歧义的位置参数。
安全二进制 vs 允许列表
| 主题 | tools.exec.safeBins | 允许列表(exec-approvals.json) |
|---|---|---|
| 目标 | 自动允许窄范围的 stdin 过滤器 | 显式信任特定可执行文件 |
| 匹配类型 | 可执行文件名 + 安全二进制 argv 策略 | 解析后的可执行路径 glob 模式 |
| 参数范围 | 受安全二进制配置和字面令牌规则限制 | 仅路径匹配;参数由你自行负责 |
| 典型示例 | jq、head、tail、wc | python3、node、ffmpeg、自定义 CLI |
| 最佳用途 | 管道中的低风险文本转换 | 任何有更广泛行为或副作用的工具 |
配置位置:
safeBins来自配置(tools.exec.safeBins或每代理的agents.list[].tools.exec.safeBins)。safeBinTrustedDirs来自配置(tools.exec.safeBinTrustedDirs或每代理的agents.list[].tools.exec.safeBinTrustedDirs)。safeBinProfiles来自配置(tools.exec.safeBinProfiles或每代理的agents.list[].tools.exec.safeBinProfiles)。每代理的配置键覆盖全局键。- 允许列表条目位于主机本地的
~/.openclaw/exec-approvals.json,在agents.<id>.allowlist下(或通过控制界面 /openclaw approvals allowlist ...)。 openclaw security audit在解释器/运行时二进制出现在safeBins中但缺少显式配置时会发出tools.exec.safe_bins_interpreter_unprofiled警告。openclaw doctor --fix可以为缺失的自定义safeBinProfiles.<bin>条目生成{}骨架(之后检查并收紧)。解释器/运行时二进制不会被自动生成骨架。
自定义配置示例:
{
tools: {
exec: {
safeBins: ["jq", "myfilter"],
safeBinProfiles: {
myfilter: {
minPositional: 0,
maxPositional: 0,
allowedValueFlags: ["-n", "--limit"],
deniedFlags: ["-f", "--file", "-c", "--command"],
},
},
},
},
}
控制界面编辑
使用控制界面 → Nodes → Exec approvals 卡片来编辑默认值、每代理覆盖和允许列表。选择范围(Defaults 或某个代理),调整策略,添加/移除允许列表模式,然后保存。UI 显示每个模式的上次使用元数据,方便你保持列表整洁。
目标选择器选择 Gateway(本地审批)或 Node。节点必须通告 system.execApprovals.get/set(macOS 应用或无头节点主机)。如果节点还未通告执行审批,直接编辑其本地的 ~/.openclaw/exec-approvals.json。
CLI:openclaw approvals 支持网关或节点编辑(参见 审批 CLI)。
审批流程
当需要提示时,网关广播 exec.approval.requested 给操作者客户端。控制界面和 macOS 应用通过 exec.approval.resolve 解决它,然后网关将批准的请求转发给节点主机。
对于 host=node,审批请求包含规范的 systemRunPlan 负载。网关在转发批准的 system.run 请求时使用该计划作为权威的命令/cwd/会话上下文。
解释器/运行时命令
审批支持的解释器/运行时运行是有意保守的:
- 精确的 argv/cwd/env 上下文始终被绑定。
- 直接 shell 脚本和直接运行时文件形式会尽力绑定到一个具体的本地文件快照。
- 常见的包管理器包装器形式如果仍然解析到一个直接本地文件(比如
pnpm exec、pnpm node、npm exec、npx),会在绑定前解包。 - 如果 OpenClaw 无法为解释器/运行时命令识别出恰好一个具体的本地文件(比如包脚本、eval 形式、运行时特定的加载器链或歧义的多文件形式),审批支持的执行会被拒绝,而不是声称它没有的语义覆盖。
- 对于这些工作流,优先使用沙箱、单独的主机边界,或操作者接受更广泛运行时语义的显式受信允许列表/full 工作流。
需要审批时,exec 工具会立即返回一个审批 ID。使用该 ID 关联后续系统事件(Exec finished / Exec denied)。如果在超时前没有收到决定,请求被视为审批超时并以拒绝原因呈现。
确认对话框包含:
- 命令 + 参数
- cwd
- 代理 ID
- 解析后的可执行路径
- 主机 + 策略元数据
操作:
- Allow once → 立即运行
- Always allow → 加入允许列表 + 运行
- Deny → 阻止
审批转发到聊天频道
你可以将执行审批提示转发到任何聊天频道(包括插件频道),然后用 /approve 批准。这使用正常的出站投递管道。
配置:
{
approvals: {
exec: {
enabled: true,
mode: "session", // "session" | "targets" | "both"
agentFilter: ["main"],
sessionFilter: ["discord"], // 子串或正则
targets: [
{ channel: "slack", to: "U12345678" },
{ channel: "telegram", to: "123456789" },
],
},
},
}
在聊天中回复:
/approve <id> allow-once
/approve <id> allow-always
/approve <id> deny
内置聊天审批客户端
Discord 和 Telegram 也可以作为显式的执行审批客户端,有频道级配置。
- Discord:
channels.discord.execApprovals.* - Telegram:
channels.telegram.execApprovals.*
这些客户端是选择启用的。如果频道没有启用执行审批,OpenClaw 不会仅因为对话发生在那里就把该频道当作审批面。
共同行为:
- 只有配置的审批者可以批准或拒绝
- 请求者不需要是审批者
- 当频道投递启用时,审批提示包含命令文本
- 如果没有操作者 UI 或配置的审批客户端能接受请求,提示回退到
askFallback
Telegram 默认向审批者发私信(target: "dm")。你可以切换到 channel 或 both 让审批提示也出现在发起的 Telegram 聊天/话题中。对于 Telegram 论坛话题,OpenClaw 会为审批提示和批准后的跟进保留话题。
参见:
macOS IPC 流程
Gateway -> Node Service (WS)
| IPC (UDS + token + HMAC + TTL)
v
Mac App (UI + approvals + system.run)
安全说明:
- Unix socket 模式
0600,token 存储在exec-approvals.json中。 - 同 UID 对等检查。
- 挑战/响应(nonce + HMAC token + 请求哈希)+ 短 TTL。
系统事件
执行生命周期通过系统消息呈现:
Exec running(仅当命令超过运行通知阈值时)Exec finishedExec denied
这些在节点报告事件后发布到代理的会话。网关主机执行审批在命令完成时(以及可选地运行超过阈值时)发出相同的生命周期事件。审批门控的执行复用审批 ID 作为这些消息中的 runId,方便关联。
影响
- full 权限很大;尽可能使用允许列表。
- ask 让你保持知情,同时仍允许快速批准。
- 每代理允许列表防止一个代理的审批泄露到其他代理。
- 审批只适用于来自授权发送者的主机执行请求。未授权的发送者无法发出
/exec。 /exec security=full是授权操作者的会话级便捷功能,设计上跳过审批。要彻底阻止主机执行,把审批安全级别设为deny或通过工具策略拒绝exec工具。
相关文档: