Skip to main content
高级 exec 审批主题:safeBins 快路径、解释器/运行时绑定,以及向聊天频道转发审批(包括原生投递)。 有关核心策略和审批流程,请参见 Exec 审批

安全二进制(仅 stdin)

tools.exec.safeBins 命名的是仅 stdin 二进制程序(例如 cut),它们在 allowlist 模式下运行,不需要显式 allowlist 条目。安全二进制会拒绝位置文件参数和类路径令牌,因此它们只能处理传入的流。请将其视为面向流过滤器的窄型快速路径,而不是通用信任列表。
不要将解释器或运行时二进制程序(例如 python3noderubybashshzsh)加入 safeBins。如果某个命令按设计可以 执行代码、运行子子命令或读取文件,请优先使用显式 allowlist 条目, 并保持审批提示开启。自定义安全二进制必须在 tools.exec.safeBinProfiles.<bin> 中定义显式配置文件。
默认安全二进制: cutuniqheadtailtrwc grepsort 不在默认列表中。如果你选择启用它们,请为其非 stdin 工作流保留显式 allowlist 条目。对于安全二进制模式下的 grep,请使用 -e/--regexp 提供模式;位置参数形式会被拒绝,因此不能通过歧义性位置参数 夹带文件操作数。

argv 验证与被拒绝的标志

验证仅根据 argv 形状确定(不检查主机文件系统是否存在),这可防止因允许/拒绝差异而出现 文件存在性探测行为。默认安全二进制会拒绝面向文件的选项;长选项采用 fail-closed 验证(未知标志和含糊的缩写会被拒绝)。默认二进制中可识别的只读布尔标志(例如 wc -ltr -duniq -c)会被接受,而未识别的短标志仍然 fail-closed,并转入人工审批。 按安全二进制配置文件被拒绝的标志:
  • grep: --dereference-recursive--directories--exclude-from--file--recursive-R-d-f-r
  • jq: --argfile--from-file--library-path--rawfile--slurpfile-L-f
  • sort: --compress-program--files0-from--output--random-source--temporary-directory-T-o
  • tail: --follow--retry-F-f
  • wc: --files0-from
安全二进制还会在执行时强制将 argv 令牌视为字面文本(仅 stdin 段不进行通配符展开,也不进行 $VARS 展开),因此像 *$HOME/... 这样的模式不能被用来偷偷读取文件。awksedjq 作为安全二进制时始终被拒绝,因为它们的语义无法被验证为仅 stdin:jq 可以读取环境数据,并从模块或启动文件加载 jq 代码。对于这些工具,请使用显式 allowlist 条目或审批提示,而不是 safeBins

可信二进制目录

安全二进制必须从可信二进制目录中解析(系统默认值加上可选的 tools.exec.safeBinTrustedDirs)。PATH 条目永远不会被自动信任。 默认可信目录有意保持最小:/bin/usr/bin。如果你的安全二进制可执行文件位于 包管理器/用户路径中(例如 /opt/homebrew/bin/usr/local/bin/opt/local/bin/snap/bin), 请显式将它们添加到 tools.exec.safeBinTrustedDirs

Shell 串联、包装器与多路复用器

当每个顶级段都满足 allowlist(包括安全二进制或技能自动允许)时,允许 Shell 串联 (&&||;)。重定向在 allowlist 模式下仍不受支持。命令替换 ($() / 反引号)在 allowlist 解析期间会被拒绝,包括双引号内;如果你需要字面量 $() 文本,请使用单引号。 在 macOS companion-app 审批中,包含 Shell 控制或展开语法 (&&||;|`$<>())的原始 Shell 文本 会被视为 allowlist 未命中,除非 Shell 二进制本身已被加入 allowlist。 对于 Shell 包装器(bash|sh|zsh ... -c/-lc),请求范围内的环境变量覆盖会被缩减为一个小的显式 allowlist(TERMLANGLC_*COLORTERMNO_COLORFORCE_COLOR)。 对于 allowlist 模式中的 allow-always 决策,透明分发包装器 (例如 envflocknicenohupstdbuftimeout)会保留 内部可执行文件路径而不是包装器路径。Shell 多路复用器 (busyboxtoybox)在 shell applet(shash 等)上会以同样方式解除包装。 如果包装器或多路复用器无法安全解除包装,则不会自动持久化任何 allowlist 条目。 如果你将 python3node 之类的解释器加入 allowlist,请优先设置 tools.exec.strictInlineEval=true,这样内联 eval 仍需要显式审批。在 strict 模式下, allow-always 仍可持久化良性的解释器/脚本调用,但内联 eval 载体不会自动持久化。

安全二进制与 allowlist 的对比

配置位置:
  • safeBins 来自配置(tools.exec.safeBins 或每个代理的 agents.entries.*.tools.exec.safeBins)。
  • safeBinTrustedDirs 来自配置(tools.exec.safeBinTrustedDirs 或每个代理的 agents.entries.*.tools.exec.safeBinTrustedDirs)。
  • safeBinProfiles 来自配置(tools.exec.safeBinProfiles 或每个代理的 agents.entries.*.tools.exec.safeBinProfiles)。按代理的配置文件键会覆盖全局键。
  • allowlist 条目存放在主机本地的 approvals 文档中,位于 agents.<id>.allowlist 下(或通过 Control UI / openclaw approvals allowlist ...)。
  • safeBins 中出现解释器/运行时二进制但没有显式配置文件时,openclaw security audit 会以 tools.exec.safe_bins_interpreter_unprofiled 发出警告。
  • openclaw doctor --fix 可以将缺失的自定义 safeBinProfiles.<bin> 条目脚手架化为 {}(之后请复查并收紧)。解释器/运行时二进制不会自动脚手架化。
自定义配置文件示例:

解释器/运行时命令

带审批的解释器/运行时运行会刻意保持保守:
  • 始终绑定精确的 argv/cwd/env 上下文。
  • 直接脚本文件和直接运行时文件形式会尽力绑定到一个具体的本地 文件快照。
  • 仍会解析为单个直接本地文件的常见包管理器包装器形式(例如 pnpm execpnpm nodenpm execnpx)会在绑定前展开。
  • 如果 OpenClaw 无法为某个解释器/运行时命令精确识别一个具体的本地文件 (例如包脚本、eval 形式、运行时特定加载器链,或歧义的多文件 形式),则会拒绝带审批的执行,而不是声称它并不具备的语义覆盖。
  • 对于这些工作流,请优先使用沙箱、单独的主机边界,或显式受信任的 allowlist/完整工作流,由操作员接受更宽泛的运行时语义。
当需要审批时,exec 工具会立即返回一个 approval id。使用该 id 来关联后续已批准运行的系统事件(Exec finished,以及在配置了时的 Exec running)。 如果在超时前没有收到决定,请求会被视为审批超时,并 表现为终端主机命令拒绝。对于带有来源会话的主代理异步审批,OpenClaw 还会恢复该会话并附带一个内部 followup,这样代理就能观察到该命令没有运行,而不是在之后修复缺失结果。待处理的 exec 审批默认在 30 分钟后过期。

后续投递行为

已批准的异步 exec 完成后,OpenClaw 会向同一会话发送一个 followup agent 回合。 被拒绝的异步审批会使用相同的主会话 followup 路径来传递拒绝状态,但它们不会注册 更高权限的运行时交接,也不会运行命令。没有可恢复主会话的拒绝会被抑制,或者在存在安全直达路由时通过该路由报告。
  • 如果存在有效的外部投递目标(可投递频道加上目标 to),后续投递会使用该频道。
  • 在仅 webchat 或无外部目标的内部会话流程中,后续投递保持仅会话内(deliver: false)。
  • 如果调用方显式请求严格的外部投递但没有可解析的外部频道,请求会以 INVALID_REQUEST 失败。
  • 如果启用了 bestEffortDeliver 且无法解析任何外部频道,则投递会降级为仅会话内,而不是失败。

第三方客户端的最小作用域

网关审批解析受专用的 operator.approvals 作用域保护。这同时适用于仅限所有者的 exec.approval.resolve 方法和不区分类型的 approval.resolve 方法;operator.write 并不包含它。仪表板和集成应仅请求其所使用方法所需的作用域。应将审批解析访问视为与远程执行同等级别的权限,并有意识地授予 operator.approvals,即使客户端只展示一个小型审批界面也是如此。

审批转发到聊天频道

你可以将 exec 审批提示转发到任何聊天频道(包括插件频道),并通过 /approve 来批准它们。这使用标准的外向投递管道。 配置:
在聊天中回复:
/approve 命令同时处理 exec 审批和插件审批。如果 ID 不匹配任何待处理的 exec 审批,它会自动改为检查插件审批。这个回退仅限于“未找到审批”的失败;真正的 exec 审批拒绝/错误不会被静默地重试为插件审批。

插件审批转发

插件审批转发使用与 exec 审批相同的投递管道,但在 approvals.plugin 下有自己独立的 配置。启用或禁用其中之一不会影响另一个。关于插件编写行为、请求字段和决策语义,请参见 插件权限请求
其配置形状与 approvals.exec 相同:enabledmodeagentFiltersessionFiltertargets 的工作方式一致。 支持共享交互式回复的频道会为 exec 和 插件审批渲染相同的审批按钮。不支持共享交互式 UI 的频道会退回为带有 /approve 说明的纯文本。插件审批请求可能会限制可用决策:审批界面使用请求声明的决策集,而网关会拒绝提交未被提供的决策。

任意频道上的同聊审批

当 exec 或插件审批请求来自可投递的聊天界面时,默认可以在同一聊天中用 /approve 批准它。这适用于 Slack、Matrix、Microsoft Teams 以及类似的可投递聊天,此外还适用于现有的 Web UI 和终端 UI 流程,并使用该会话的正常频道认证模型。如果发起审批的聊天本来就可以发送命令并接收回复,那么审批请求就不再需要单独的原生投递适配器来保持待处理状态。 Discord、Telegram 和 QQ bot 也支持同聊 /approve,但即使禁用了原生审批投递,这些频道仍会使用其解析出的审批者列表进行授权。

原生审批投递

某些频道也可以充当原生审批客户端:Discord、Slack、Telegram、Matrix 和 QQ bot。原生客户端在共享的同聊 /approve 流程之上,增加了审批者私信、来源聊天广播,以及频道特定的交互式审批体验。 当原生审批卡片/按钮可用时,该原生 UI 是面向代理的主要路径。除非工具结果表明聊天审批不可用,或者手动审批是唯一剩余路径,否则代理不应再额外回显重复的纯聊天 /approve 命令。 如果配置了原生审批客户端,但发起审批的频道没有可用的原生运行时,OpenClaw 会保留本地确定性的 /approve 提示可见。如果原生运行时处于活动状态并尝试投递但没有任何目标收到卡片,OpenClaw 会发送一条同聊回退通知,包含精确的 /approve <id> <decision> 命令,以便请求仍可被解决。 通用模型:
  • host exec policy 仍然决定是否需要 exec 审批
  • approvals.exec 控制将审批提示转发到其他聊天目的地
  • channels.<channel>.execApprovals 控制 Discord、Slack、Telegram、QQ bot 以及类似的 频道特定原生客户端是否启用
  • Slack 插件审批在请求来自 Slack 且 Slack 插件审批者可解析时,可以使用 Slack 的原生审批客户端;即使 Slack exec 审批被禁用,approvals.plugin 也可以将插件审批路由到 Slack 会话或目标
  • 当请求来自 Google Chat 空间或线程,并且能从 dm.allowFromdefaultTo 解析出稳定的 users/<id> 审批者时,Google Chat 原生审批卡可处理 exec 和插件审批;它们不使用 reaction 事件来做决策
  • WhatsApp 和 Signal 的 reaction 审批投递受 approvals.execapprovals.plugin 约束;它们没有 channels.<channel>.execApprovals 区块
当满足以下条件时,原生审批客户端会自动启用“优先私信”投递:
  • 该频道支持原生审批投递
  • 可以从显式的 execApprovals.approvers 或所有者身份中解析出审批者, 例如 commands.ownerAllowFrom
  • channels.<channel>.execApprovals.enabled 未设置或为 "auto"
enabled: false 设为显式禁用某个原生审批客户端。将 enabled: true 设为在审批者可解析时强制启用它。公开的来源聊天投递仍通过 channels.<channel>.execApprovals.target 显式开启。当原生 target 启用来源聊天投递时,审批提示会包含命令文本。 FAQ: 为什么聊天审批有两个 exec 审批配置?
  • Discord: channels.discord.execApprovals.*
  • Slack: channels.slack.execApprovals.*
  • Telegram: channels.telegram.execApprovals.*
  • QQ bot: channels.qqbot.execApprovals.*
  • Google Chat: 使用 channels.googlechat.dm.allowFromchannels.googlechat.defaultTo 配置稳定的审批者;不需要 execApprovals 区块
  • WhatsApp: 使用 approvals.execapprovals.plugin 将审批提示路由到 WhatsApp
  • Signal: 使用 approvals.execapprovals.plugin 将审批提示路由到 Signal
原生客户端特定路由:
  • Telegram 默认将审批发送给审批者私信(target: "dm")。切换为 channelboth,也会在发起审批的 Telegram 聊天/话题中显示审批提示。对于 Telegram 论坛话题,OpenClaw 会保留话题上下文用于审批提示和审批后的跟进。
  • Discord 和 Telegram 的审批者可以是显式指定的(execApprovals.approvers)或从 commands.ownerAllowFrom 推断;只有已解析的审批者才能批准或拒绝。
  • Slack 审批者可以是显式指定的(execApprovals.approvers)或从 commands.ownerAllowFrom 推断。Slack 插件审批私信使用来自 allowFrom 和账户默认路由的 Slack 插件审批者,而不是 Slack exec 审批者。Slack 原生按钮会保留审批 ID 类型,因此 plugin: ID 可以在不需要第二层 Slack 本地回退的情况下解析插件审批。
  • Google Chat 原生卡片会在消息文本中保留手动 /approve 回退,但卡片按钮回调只携带不透明的动作令牌;审批 ID 和决策会从服务器端待处理状态中恢复。
  • 当匹配的顶层转发族路由到 WhatsApp 时,WhatsApp 表情审批可同时处理 exec 和插件提示。原生来源提示直接绑定;共享目标模式投递会将相同的有类型审批元数据绑定到被接受的 WhatsApp 消息回执上。
  • 仅当匹配的顶层转发族已启用并路由到 Signal 时,Signal 反应审批才会同时处理 exec 和插件提示。直接的同聊 Signal exec 审批可以在没有显式审批者的情况下抑制本地 /approve 回退;但 Signal 反应解析仍需要来自 channels.signal.allowFromdefaultTo 的显式 Signal 审批者。
  • Matrix 原生私信/频道路由和反应快捷方式可同时处理 exec 和插件审批;插件授权仍来自 channels.matrix.dm.allowFrom。Matrix 原生提示会在第一个提示事件中包含 com.openclaw.approval 自定义事件内容,这样支持 OpenClaw 的 Matrix 客户端就能读取结构化审批状态,而原生客户端则保留纯文本 /approve 回退。
  • 原生 Discord 和 Telegram 审批按钮会在传输私有的回调数据中携带明确的 exec 或 plugin 所有者类型,并且只解析该所有者。旧版缺少类型的 /approve 控件仍然是一条有限的兼容路径:它们只会尝试执行操作者可能批准的所有者类型,仅在收到“未找到审批”结果后继续,并且绝不会从审批 ID 推断所有权。
  • 请求者不需要是审批者。
  • 如果没有任何操作员 UI 或已配置的审批客户端能够接受该请求,提示会回退到 askFallback
诸如 /diagnostics/export-trajectory 之类的敏感 owner-only 组命令,对审批提示和最终结果使用私有的 所有者路由。OpenClaw 会先尝试在所有者运行该命令的同一界面上进行私有路由。如果该界面没有私有所有者路由, 它会回退到 commands.ownerAllowFrom 中第一个可用的所有者路由,因此即使 Telegram 是配置的主要私有界面, Discord 组命令仍然可以把审批和结果发送到所有者的 Telegram 私信。群聊只会收到一条简短确认。 参见:

官方移动端操作员应用

官方 iOS 和 Android 应用在使用 operator.admin 连接时,也可以查看 Gateway 拥有的待处理 exec 审批;或者当其配对的 operator.approvals 设备被请求显式指定为目标时,也可以查看。它们会读取与 控制界面相同的已清理持久记录,提交带类型感知的决策,并显示 Gateway 的标准首答结果。 Apple Watch 会通过配对的 iPhone 镜像这些审批提示,并支持 allow-once 和 deny 操作。直接的 Watch Gateway 模式 不会查看审批。 丢失 resolve 确认不会使提交的选择成为权威结果: 应用会禁用控制项并再次读取记录。如果别的界面已经胜出,应用会显示那个已记录的决策。待处理提示始终绑定到发出它们的 Gateway,因此切换活动 Gateway 不会重定向旧的审批 ID。

macOS IPC 流程

安全说明:
  • Unix socket mode 0600, token stored in the exec_approvals_config row of state/openclaw.sqlite.
  • Same-UID peer check.
  • Challenge/response (nonce + HMAC token + request hash) + short TTL.

常见问题

在审批目标上,什么时候会使用 accountIdthreadId

当某个频道配置了多个身份,而审批提示必须通过其中一个特定账号发出时,使用 accountId。当目标支持话题或线程,并且提示应停留在该线程中而不是顶级聊天中时,使用 threadId 一个具体的 Telegram 场景是:带有论坛话题的运维超级群组,以及两个 Telegram 机器人账号。to 值指定超级群组,accountId 选择机器人账号,threadId 选择论坛话题:
在该配置下,转发的执行审批会由 ops-bot Telegram 账号发布到聊天 -1001234567890 的话题 77 中。没有 accountId 的目标会使用该频道的默认账号,而没有 threadId 的目标会发布到顶级目标位置。

当审批发送到某个会话时,该会话中的任何人都可以批准吗?

不可以。会话投递只决定提示出现在哪里,它本身并不会授权该聊天中的每个参与者都能批准。 对于通用的同聊天 /approve,发送者必须已经被授权可在该频道会话中执行命令。如果频道暴露了明确的审批批准者,那么即使他们在该会话中并非其他命令的授权用户,也可以授权 /approve 动作。 某些频道更严格。Discord、Telegram、Matrix、Slack 原生审批 DM,以及类似的原生审批客户端,会使用它们解析出的批准者列表来进行审批授权。例如,一个 Telegram 论坛话题中的审批提示可能对话题中的所有人可见,但只有从 channels.telegram.execApprovals.approverscommands.ownerAllowFrom 解析出的数字 Telegram 用户 ID 才能批准或拒绝它。

相关内容