跳转至

执行审批 — 高级

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

安全二进制文件(仅限 stdin)

tools.exec.safeBins 指定仅限 stdin 的二进制文件(例如 cut),这些二进制文件可以在允许列表模式下运行,而无需显式允许列表条目。安全二进制文件会拒绝位置文件参数和路径样式的令牌,因此它们只能操作传入流。请将其视为流过滤器的狭窄快速路径,而不是通用信任列表。

Warning

请勿将解释器或运行时二进制文件(例如 python3、node、ruby、bash、sh、zsh)添加到 safeBins。如果某个命令在设计上可以评估代码、执行子命令或读取文件,请优先使用显式允许列表条目,并保持启用审批提示。自定义安全二进制文件必须在 tools.exec.safeBinProfiles.<bin> 中定义显式配置档。

默认安全二进制文件:

cut, uniq, head, tail, tr, wc

grep 和 sort 不在默认列表中。如果你选择启用,请为其非 stdin 工作流保留显式允许列表条目。对于安全二进制文件模式下的 grep,请使用 -e/--regexp 提供模式;位置参数模式形式会被拒绝,因此文件操作数无法作为模糊的位置参数被夹带。

argv 验证与拒绝的标志

验证仅基于 argv 形状是确定性的(不检查主机文件系统是否存在),从而防止通过允许/拒绝差异产生文件存在预言机行为。默认安全二进制文件拒绝面向文件的选项;长选项验证采用失败关闭(未知标志和模糊缩写会被拒绝)。默认二进制文件中被识别的只读布尔标志(例如 wc -l、tr -d、uniq -c)会被接受,而未识别的短标志保持失败关闭,并回落到已配置的审批策略,包括 mode=auto 中的自动审查者。

按安全二进制文件配置档拒绝的标志:

  • 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

安全二进制文件还会强制将仅限 stdin 段的 argv 令牌在执行时视为字面文本(不进行 glob 展开,也不进行 $VARS 展开),因此诸如 * 或 $HOME/... 之类的模式无法用于夹带文件读取。awk、sed 和 jq 始终被拒绝作为安全二进制文件,因为其语义无法验证为仅限 stdin:jq 可以读取环境数据,并从模块或启动文件中加载 jq 代码。对于这些工具,请改用显式允许列表条目或审批提示,而不是 safeBins。

受信任的二进制文件目录

安全二进制文件必须从受信任的二进制文件目录解析(系统默认值加上可选的 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)。

对于允许列表模式中的 allow-always 决策,透明分发包装器(例如 env、flock、nice、nohup、stdbuf、timeout)会持久化内部可执行文件路径,而不是包装器路径。Shell 多路复用器(busybox、toybox)以相同方式针对 shell 小程序(sh、ash 等)进行解包。如果包装器或多路复用器无法安全解包,则不会自动持久化任何允许列表条目。

如果你将 python3 或 node 等解释器加入允许列表,请优先使用 tools.exec.strictInlineEval=true,以便内联 eval 仍然需要审查者或显式审批。在严格模式下,allow-always 仍可以持久化无害的解释器/脚本调用,但内联 eval 载体不会自动持久化。

安全二进制文件与允许列表

主题 tools.exec.safeBins 允许列表(SQLite 执行审批文档)
目标 自动允许狭窄的 stdin 过滤器 显式信任特定可执行文件
匹配类型 可执行文件名 + 安全二进制文件 argv 策略 已解析的可执行文件路径 glob,或用于通过 PATH 调用的命令的裸命令名 glob
参数范围 受安全二进制文件配置档和字面令牌规则限制 默认按路径匹配;可选 argPattern 可限制解析后的 argv
典型示例 head、tail、tr、wc jq、python3、node、ffmpeg、自定义 CLI
主题 tools.exec.safeBins 允许列表(SQLite exec 审批文档)
最佳用途 流水线中的低风险文本转换 任何具有更广泛行为或副作用的工具

配置位置:

  • 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)。按代理的 profile 键会覆盖全局键。
  • 允许列表条目位于主机本地审批文档的 agents.<id>.allowlist 下(或通过 Control UI / openclaw approvals allowlist ...)。
  • openclaw security audit 会在解释器/运行时可执行文件出现在 safeBins 中且没有显式 profile 时,以 tools.exec.safe_bins_interpreter_unprofiled 发出警告。
  • openclaw doctor --fix 可以将缺失的自定义 safeBinProfiles.<bin> 条目自动创建为 {}(之后需审查并收紧)。解释器/运行时可执行文件不会被自动创建。

自定义 profile 示例:

{
  tools: {
    exec: {
      safeBins: ["myfilter"],
      safeBinProfiles: {
        myfilter: {
          minPositional: 0,
          maxPositional: 0,
          allowedValueFlags: ["-n", "--limit"],
          deniedFlags: ["-f", "--file", "-c", "--command"],
        },
      },
    },
  },
}

解释器/运行时命令

基于审批的解释器/运行时运行有意保持保守:

  • 始终绑定精确的 argv/cwd/env 上下文。
  • 在网关上,整个普通外部调度链——每个原始包装器可执行文件和最终命令段可执行文件——在人工或自动审查之前绑定,并在启动前重新检查。节点主机在本地策略评估期间捕获相同的身份,并在调度前重新检查它们。受保护可执行文件仅使用解析后的真实路径身份;可写可执行文件还使用内容哈希。解析发生变化(包括 PATH 中更靠前的新可执行文件)会拒绝该运行。仅身份绑定不会限制其他符合条件的 allow-always 决策。
  • 直接 shell 脚本和直接运行时文件形式会尽力绑定到一个具体的本地文件快照。
  • 仍然解析为一个直接本地文件的常见包管理器包装器形式(例如 pnpm exec、pnpm node、npm exec、npx)会在绑定前解包。
  • 如果 OpenClaw 无法为解释器/运行时命令识别恰好一个具体的本地文件(例如包脚本、eval 形式、运行时特定的加载器链或含糊的多文件形式),则会拒绝基于审批的执行,而不是声称其不具备的语义覆盖。
  • 对于这些工作流,优先使用沙箱、独立的主机边界,或显式受信任的允许列表/完整工作流,由操作员接受更广泛的运行时语义。

节点可执行文件身份绑定仅限于一次调用。远程人工审批计划保留其现有的直接可执行文件固定和单个脚本操作数快照;它不会在审批等待期间携带 shell 包装器内每个可执行文件的身份。对于这些内部命令,身份重新验证从已批准的调用到达节点的本地策略评估时开始,因此不会检测到在远程审批等待期间更早发生的替换。

在 mode=auto 中,经审查者批准的未固定执行要求完整调度链被身份绑定。授权计划必须成功,每个候选项必须使用直接传输,并且每个包装器和最终可执行文件都必须有记录的可执行文件操作数。被策略阻止或不完整的信任计划无法建立资格。固定命令保留其现有审查行为。

Shell -c 包装器、带赋值的 env、xcrun、BusyBox/Toybox 小程序、shell builtin/command/exec 调度,以及其他完整链无法绑定的任何形式,会跳过自动审查,并给出 Exec auto-review skipped: dispatch chain cannot be bound。当现有绑定检查成功时,这些形式走一次性人工审批路径。普通命令和透明且无赋值的 env 在所有可执行文件身份均被绑定时仍符合条件。审查远程节点命令的网关接受已准备好的固定直接命令,包括节点自身围绕一个带静态参数的直接绝对可执行文件的规范 POSIX shell 传输。该传输是节点的调度器;用户提供的 shell 或调度包装器、裸可执行文件名以及未加引号的 glob 仍需要人工,因为网关无法检查节点的内存中可执行文件绑定。现有绑定拒绝仍然有效。每个外部包装器自身的可执行文件仍必须可解析。Shell 载体仅在 shell 命令位置免于文件绑定;外部调度也会将这些名称绑定为可执行文件。没有持久化数据模型变更:绑定在审批生命周期内保持在内存中。

在 mode=auto 中,用户提供的 POSIX 登录或交互式 shell 包装器会跳过审查者。可绑定命令(例如 bash -lc 'printf ok')需要人工审批,因为其隐式启动文件位于操作数绑定之外。现有绑定拒绝优先:网关已经拒绝交互式代码加载选项,例如 -i、--interactive 以及组合的 -ic,因此这些命令保持被拒绝,而不是创建人工审批请求。这不会改变网关的普通 shell 启动快照。

当需要审批时,exec 工具会立即返回一个审批 id。使用该 id 关联后续已批准运行的系统事件(Exec finished,以及配置后的 Exec running)。如果在超时前没有收到决定,则请求被视为审批超时,并作为终端主机命令拒绝呈现。对于具有源会话的主代理异步审批,OpenClaw 还会通过内部后续恢复该会话,使代理观察到命令未运行,而不是之后修复缺失的结果。待处理的 exec 审批默认在 30 分钟后过期。

后续投递行为

当已批准的异步 exec 完成后,OpenClaw 会向同一会话发送一个后续 agent 轮次。 被拒绝的异步审批使用相同的主会话后续路径来传递拒绝状态,但不会注册提权运行时交接,也不会运行该命令。对于没有可恢复主会话的拒绝,会被抑制,或者在存在安全直接路由时通过该路由报告。

  • 如果存在有效的外部投递目标(可投递渠道加上目标 to),后续投递将使用该渠道。
  • 在仅 webchat 或无外部目标的内部会话流程中,后续投递保持仅限会话(deliver: false)。
  • 如果调用方显式请求严格外部投递,但没有可解析的外部渠道,请求将以 INVALID_REQUEST 失败。
  • 如果启用了 bestEffortDeliver 且无法解析外部渠道,投递会降级为仅限会话,而不是失败。

第三方客户端的最小权限范围

Gateway 审批解析由专用的 operator.approvals 权限范围保护。这同时适用于所有者特定的 exec.approval.resolve 方法和与类型无关的 approval.resolve 方法;operator.write 不会涵盖它。仪表板和集成应仅请求其使用方法所需的权限范围。应将审批解析访问视为远程执行级别的权限,并审慎授予 operator.approvals,即使客户端仅展示一个小型审批 UI。

审批转发到聊天渠道

你可以将 exec 审批提示转发到任意聊天渠道(包括插件渠道),并使用 /approve 批准它们。这使用常规出站投递管道。

对于用户已授权的命令,代理首先通过工具请求执行。执行主机决定是否需要进行审批。代理仅从实际待处理的审批中继 /approve 命令,并使用确切的请求 ID 和回复说明。单独的 /approve 或虚构的 ID 无法批准命令。allow-once 授权仅覆盖其特定命令;后续命令会收到各自的策略决定。

配置:

{
  approvals: {
    exec: {
      enabled: true,
      mode: "session", // "session" | "targets" | "both"
      agentFilter: ["main"],
      sessionFilter: ["discord"], // substring or regex
      targets: [
        { channel: "slack", to: "U12345678" },
        { channel: "telegram", to: "123456789" },
      ],
    },
  },
}

在聊天中回复:

/approve <id> allow-once
/approve <id> allow-always
/approve <id> deny

/approve 命令同时处理 exec 审批和插件审批。如果 ID 不匹配待处理的 exec 审批,它会自动改为检查插件审批。此回退仅限于“approval not found”失败;真实的 exec 审批拒绝/错误不会静默重试为插件审批。

插件审批转发

插件审批转发使用与 exec 审批相同的投递管道,但在 approvals.plugin 下拥有自己独立的配置。启用或禁用其中一项不会影响另一项。有关插件编写行为、请求字段和决策语义,请参阅 插件权限请求。

{
  approvals: {
    plugin: {
      enabled: true,
      mode: "targets",
      agentFilter: ["main"],
      targets: [
        { channel: "slack", to: "U12345678" },
        { channel: "telegram", to: "123456789" },
      ],
    },
  },
}

配置结构与 approvals.exec 相同:enabled、mode、agentFilter、 sessionFilter 和 targets 的工作方式相同。

支持共享交互式回复的渠道会为 exec 审批和插件审批渲染相同的审批按钮。没有共享交互式 UI 的渠道会回退为带有 /approve 说明的纯文本。插件审批请求可能会限制可用的决策:审批界面使用请求中声明的决策集,Gateway 会拒绝提交未被提供的决策的尝试。

任意渠道的同聊天审批

当 exec 或插件审批请求源自可投递的聊天界面时,默认情况下,同一聊天可以使用 /approve 批准它。这适用于 Slack、Matrix、Microsoft Teams 和类似的可投递聊天,并使用该会话的正常渠道身份验证模型。Web UI 支持两种审批类型;终端 UI 仅支持插件审批。如果源聊天已经可以发送命令并接收回复,审批请求不再需要单独的原生投递适配器才能保持待处理状态。

Discord、Telegram 和 QQ bot 也支持同聊天 /approve,但这些渠道在禁用原生审批投递时,仍会使用其解析出的审批人列表进行授权。

原生审批投递

某些渠道也可以充当原生审批客户端:Discord、Slack、Telegram、Matrix 和 QQ bot。 原生客户端在共享的同聊天 /approve 流程之上,增加了审批人 DM、源聊天扇出以及特定于渠道的交互式审批 UX。

当原生审批卡片/按钮可用时,该原生 UI 是面向代理的主要路径。除非工具结果表示聊天审批不可用或手动审批是唯一剩余路径,否则代理不应同时回显重复的纯聊天 /approve 命令。

如果配置了原生审批客户端,但源渠道没有激活的原生运行时,OpenClaw 会保持本地确定性的 /approve 提示可见。如果原生运行时处于活动状态并尝试投递,但没有目标接收到卡片,OpenClaw 会发送一条同聊天回退通知,其中包含确切的 /approve <id> <decision> 命令,以便请求仍能被解决。

通用模型:

  • 主机 exec 策略仍决定是否需要 exec 审批
  • approvals.exec 控制将审批提示转发到其他聊天目的地
  • channels.<channel>.execApprovals 控制是否启用 Discord、Slack、Telegram、QQ bot 和类似 的特定于渠道的原生客户端
  • 当请求来自 Slack 且 Slack 插件审批人解析成功时,Slack 插件审批可以使用 Slack 的原生审批客户端;即使 Slack exec 审批已禁用,approvals.plugin 也可以将插件审批路由到 Slack 会话或目标
  • 当稳定的 users/<id> 审批人从 dm.allowFrom 或 defaultTo 解析时,Google Chat 原生审批卡片会处理源自 Google Chat 空间或线程的 exec 和插件审批;它们不使用反应事件进行决策
  • WhatsApp 和 Signal 的反应审批投递由 approvals.exec 和 approvals.plugin 控制;它们没有 channels.<channel>.execApprovals 块

对于具有 execApprovals 块的频道,通过设置 enabled: true 或 "auto" 并配置可解析的审批人来启用原生投递。默认值因 频道而异:Discord 和 Slack 需要显式启用;Telegram 将未设置视为 "auto"。审批人可以来自 execApprovals.approvers 或频道支持的所有者配置,例如 commands.ownerAllowFrom。

设置 enabled: false 可显式禁用原生审批客户端。公共来源聊天投递仍通过 channels.<channel>.execApprovals.target 保持显式。当原生 target 启用来源聊天投递时, 审批提示会包含命令文本。

常见问题:为什么聊天审批有两个 exec 审批配置?

  • Discord: channels.discord.execApprovals.*
  • Slack: channels.slack.execApprovals.*
  • Telegram: channels.telegram.execApprovals.*
  • QQ bot: channels.qqbot.execApprovals.*
  • Google Chat: 使用 channels.googlechat.dm.allowFrom 或 channels.googlechat.defaultTo 配置稳定的审批人;无需 execApprovals 块
  • WhatsApp: 使用 approvals.exec 和 approvals.plugin 将审批提示路由到 WhatsApp
  • Signal: 使用 approvals.exec 和 approvals.plugin 将审批提示路由到 Signal

原生客户端特定路由:

  • Telegram 默认使用审批人私信(target: "dm")。切换到 channel 或 both 也可在来源 Telegram 聊天/主题中显示审批提示。对于 Telegram 论坛主题,OpenClaw 会为审批提示和审批后的后续消息保留该主题。
  • Discord 和 Telegram 审批人可以显式指定(execApprovals.approvers)或从 commands.ownerAllowFrom 推断;只有已解析的审批人可以批准或拒绝。
  • Slack 审批人可以显式指定(execApprovals.approvers)或从 commands.ownerAllowFrom 推断。Slack plugin 审批私信使用来自 allowFrom 和账户默认路由的 Slack plugin 审批人,而不是 Slack exec 审批人。Slack 原生按钮保留审批 id 类型,因此 plugin: id 可以解析 plugin 审批,而无需第二层 Slack 本地回退层。
  • Google Chat 原生卡片在消息文本中保留手动 /approve 回退,但卡片按钮 回调只携带不透明操作令牌;审批 ID 和决定从 服务器端待处理状态恢复。
  • WhatsApp 表情审批在匹配的顶层转发族 路由到 WhatsApp 时处理 exec 和 plugin 提示。原生来源提示直接绑定;共享目标模式 投递将相同的类型化审批元数据绑定到已接受的 WhatsApp 消息回执。
  • Signal 反应审批仅在匹配的顶层转发族 已启用并路由到 Signal 时处理 exec 和 plugin 提示。直接同聊天 Signal exec 审批可以 在没有显式审批人的情况下抑制本地 /approve 回退;Signal 反应解析 仍需要来自 channels.signal.allowFrom 或 defaultTo 的显式 Signal 审批人。
  • Matrix 原生 DM/频道路由和反应快捷方式处理 exec 和 plugin 审批; plugin 授权仍来自 channels.matrix.dm.allowFrom。Matrix 原生提示 在第一个提示事件中包含 com.openclaw.approval 自定义事件内容,因此支持 OpenClaw 的 Matrix 客户端可以读取结构化审批状态,而标准客户端保留纯文本 /approve 回退。
  • 原生 Discord 和 Telegram 审批按钮在传输私有回调数据中携带显式的 exec 或 plugin 所有者类型, 并且只解析该所有者。缺少类型的旧 /approve 控件仍是有界兼容路径:它们只尝试操作者可能批准的拥有者类型, 仅在审批未找到结果后继续,并且绝不从审批 ID 推断所有权。
  • 请求者不需要是审批人。
  • 如果没有操作员 UI 或已配置的审批客户端可以接受请求,提示将回退到 askFallback。

敏感的所有者专用群命令,例如 /diagnostics 和 /export-trajectory,对审批提示和最终结果使用私有 所有者路由。OpenClaw 首先尝试在所有者运行命令的同一界面上使用私有路由。如果该界面没有私有所有者路由,它会回退 到 commands.ownerAllowFrom 中第一个可用的所有者路由,因此当 Telegram 是配置的主要私有接口时,Discord 群命令 仍可将审批和结果发送到所有者的 Telegram DM。群聊只会收到简短确认。

参见:

官方移动操作员应用

官方 iOS 和 Android 应用也可以使用 operator.admin 连接,或者当它们的配对 operator.approvals 设备被请求显式指定时,审查 Gateway 拥有的待处理 exec 审批。它们读取与 Control UI 使用的相同净化持久记录,提交类型感知的决定,并显示 Gateway 的规范 首个回答结果。Apple Watch 通过配对的 iPhone 镜像这些审批提示,支持允许一次和拒绝操作。直接 Watch Gateway 模式 不审查审批。

丢失的解析确认不会使已提交的选择成为权威: 应用会禁用控件并再次读取记录。如果另一个界面 胜出,应用会显示该已记录决定。待处理提示仍绑定到发出它们的 Gateway,因此切换活动 Gateway 无法重定向旧审批 ID。

macOS IPC 流程

Gateway -> Node Service (WS)
                 |  IPC (UDS + token + HMAC + TTL)
                 v
             Mac App (UI + approvals + system.run)

安全说明:

  • Unix socket 模式 0600,令牌存储在 state/openclaw.sqlite 的 exec_approvals_config 行中。
  • 同 UID 对等检查。
  • 挑战/响应(nonce + HMAC 令牌 + 请求哈希)+ 短 TTL。

常见问题

何时会在审批目标上使用 accountId 和 threadId?

当频道配置了多个身份,且审批提示必须经由其中一个特定账户发出时,请使用 accountId。当目标支持话题或线程,且提示应保留在该线程内而不是顶层聊天中时,请使用 threadId。

一个具体的 Telegram 场景是一个带有论坛话题和两个 Telegram 机器人账户的运营超级群组。to 值指定超级群组,accountId 选择机器人账户,threadId 选择论坛话题:

{
  approvals: {
    exec: {
      enabled: true,
      mode: "targets",
      targets: [
        {
          channel: "telegram",
          to: "-1001234567890",
          accountId: "ops-bot",
          threadId: "77",
        },
      ],
    },
  },
  channels: {
    telegram: {
      accounts: {
        default: {
          name: "Primary bot",
          botToken: "env:TELEGRAM_PRIMARY_BOT_TOKEN",
        },
        "ops-bot": {
          name: "Operations bot",
          botToken: "env:TELEGRAM_OPS_BOT_TOKEN",
        },
      },
    },
  },
}

在该配置下,转发的 exec 审批将由 ops-bot Telegram 账户发布到聊天 -1001234567890 的话题 77 中。未设置 accountId 的目标会使用频道的默认账户,未设置 threadId 的目标会发布到顶层目标。

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

不。会话投递仅控制提示出现的位置。它本身并不会授权该聊天中的每个参与者进行批准。

对于通用的同聊天 /approve,发送者必须已经获得该频道会话中的命令授权。如果频道公开了明确的审批人,这些审批人可以授权 /approve 操作,即使他们在该会话中原本没有命令授权。

某些频道更严格。Discord、Telegram、Matrix、Slack 原生审批私信以及类似的原生审批客户端会使用其解析出的审批人列表进行审批授权。例如,Telegram 论坛话题中的审批提示可能对话题中的所有人可见,但只有从 channels.telegram.execApprovals.approvers 或 commands.ownerAllowFrom 解析出的数字 Telegram 用户 ID 才能批准或拒绝它。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw