15.1 Bash 执行引擎

生成模型:Claude Opus 4.6 (anthropic/claude-opus-4-6) Token 消耗:输入 ~120k tokens,输出 ~6k tokens(本节)


上一章我们全面了解了 OpenClaw 的工具系统——工具如何注册、如何执行、如何控制权限。从本章开始,我们将深入到具体工具实现层面。Bash 执行引擎是 OpenClaw 中最复杂、安全要求最高的工具:它让 AI Agent 具备了在操作系统上执行任意命令的能力,但同时也带来了巨大的安全风险。本节将从命令执行核心出发,逐步解析 PTY 终端模拟、PATH 管理以及安全审批等关键机制。


15.1.1 exec 工具的总体架构

源文件分布

Bash 工具的实现分布在多个文件中:

文件
职责

bash-tools.ts

桶文件(barrel),re-export exec 和 process 工具

bash-tools.exec.ts

exec 工具核心——命令执行引擎(~1630 行)

bash-tools.process.ts

process 工具——后台进程管理

bash-tools.shared.ts

共享工具函数(环境变量、工作目录、日志截取)

bash-process-registry.ts

进程注册表——内存中管理所有 exec 会话

shell-utils.ts

Shell 配置检测、二进制输出清理、进程树杀死

pty-dsr.ts

PTY DSR(Device Status Report)请求处理

pty-keys.ts

PTY 按键编码(send-keys、paste 操作)

exec-approvals.ts(infra/)

命令审批系统——白名单、安全策略评估

三种执行宿主模式

exec 工具支持三种执行宿主(Host),对应不同的安全级别:

衍生解释:在容器化(Containerization)技术中,Docker 是最流行的容器引擎。"sandbox" 模式下,OpenClaw 在一个 Docker 容器内执行命令,容器与宿主机文件系统和网络隔离,即使 AI 执行了恶意命令,也不会影响宿主系统。这是经典的**沙箱(Sandbox)**安全模型。

工具 Schema 定义

exec 工具通过 TypeBox 定义参数 Schema,LLM 可以理解并填入:

注意 yieldMsbackground 参数:这是 OpenClaw 的特色设计。命令执行可以先同步等待一段时间(默认 10 秒),如果命令尚未完成,就自动转入后台——这比简单的"同步阻塞到完成"或"立即后台化"更灵活。


15.1.2 命令执行核心流程

createExecTool() 工厂函数

exec 工具由工厂函数 createExecTool(defaults?) 创建。工厂接收默认配置参数(ExecToolDefaults),返回一个 AgentTool 实例。核心配置项包括:

execute 处理链

当 LLM 调用 exec 工具时,execute 函数依次执行以下步骤:


15.1.3 三种进程创建方式

runExecProcess() 根据配置选择不同的进程创建策略:

策略一:Docker 沙箱执行

关键点:

  • buildDockerExecArgs() 构建 docker exec -i [-t] -w <dir> -e KEY=VAL ... <container> sh -lc '<command>' 命令

  • 使用 -l(login shell)确保 .profile 等初始化脚本被加载,从而获得完整的 PATH

  • spawnWithFallback() 提供降级机制——如果 detached 模式 spawn 失败(某些 Node.js 版本/平台限制),自动回退到非 detached 模式

衍生解释detached: true 是 Node.js child_process.spawn() 的选项,表示子进程与父进程分离运行。在 Unix 系统上,这会创建一个新的进程组(Process Group),使得父进程退出时子进程不会被杀死。OpenClaw 需要这个特性来支持后台长时间运行的命令。

策略二:PTY 模拟终端执行

衍生解释:PTY(Pseudo Terminal,伪终端)是操作系统内核提供的一种机制,它模拟了一个终端设备。许多命令行程序(如 vimhtopless)会检测自己是否运行在终端中——如果不是(比如 stdout 是管道),它们会改变行为(禁用颜色、不显示 TUI 界面、甚至拒绝运行)。通过 PTY,OpenClaw 可以让这些程序认为自己运行在一个真正的终端里。

PTY 模式有几个特殊之处:

  1. 终端尺寸:设置为 120 列 × 30 行,这是典型的终端尺寸

  2. TERM 环境变量:默认为 xterm-256color,支持 256 色输出

  3. stdin 适配:PTY 不使用 Node.js 的 child.stdin,而是通过 pty.write() 写入

  4. EOF 信号:Unix 用 \x04(Ctrl-D),Windows 用 \x1a(Ctrl-Z)

  5. 自动降级:如果 node-pty 模块不可用(未安装或平台不支持),自动回退到普通 spawn

策略三:普通子进程执行

最简单的模式:获取系统 Shell(通过 getShellConfig()),然后用 -c 参数执行命令。


15.1.4 PTY 数据流处理

PTY 模式下,数据流的处理与普通 child_process 有显著差异。

DSR 请求过滤

衍生解释:DSR(Device Status Report)是终端控制序列标准(ANSI/VT100)的一部分。当程序向终端发送 ESC[6n 时,它期望终端回复当前光标位置(格式为 ESC[行;列R)。像 vimzsh 等程序在启动时会发送 DSR 来获取光标位置。OpenClaw 的 PTY 层需要"假装"自己是终端来回复这些查询,否则程序会一直等待回复而卡住。

DSR 处理的实现非常精练,只有 16 行:

输出格式对比

特性
普通模式
PTY 模式

stdout/stderr 分离

✓ 独立流

✗ 混合在一起

ANSI 颜色码

通常无

包含

DSR 查询

需要过滤和回复

TUI 程序支持

不支持

支持

数据事件

child.stdout.on('data')

pty.onData()


15.1.5 环境变量安全机制

宿主模式环境变量黑名单

当执行宿主为 gateway(宿主机)或 node(远程节点)时,OpenClaw 会严格检查 Agent 请求的环境变量:

衍生解释LD_PRELOAD 攻击是 Linux 系统上一种经典的代码注入技术。LD_PRELOAD 环境变量指定一个共享库(.so 文件),这个库会在所有其他库之前被加载。攻击者可以用自定义库覆盖标准库函数(如 read()write()),从而拦截或篡改程序行为。类似地,macOS 上的 DYLD_INSERT_LIBRARIES 具有相同的危害。

验证函数会拒绝任何尝试设置这些变量的请求:

注意:sandbox(Docker)模式跳过此检查——因为容器内的环境变量不会影响宿主机。

PATH 管理策略

不同模式下的 PATH 处理各不相同:

Safe Bins(安全二进制白名单)

allowlist 安全模式下,如果命令是这些"安全"工具之一(且不带文件路径参数),可以直接执行而不需要审批。isSafeBinUsage() 函数做了精细的检查:

  • 命令的可执行文件名必须在 safeBins 集合中

  • 可执行文件必须解析到具体路径(防止 PATH 注入)

  • 参数中不能包含文件路径(防止读写敏感文件)


15.1.6 命令审批系统

审批文件结构

命令审批的配置存储在 ~/.openclaw/exec-approvals.json

三层安全策略

策略
security
ask
行为

最严格

deny

拒绝一切执行

白名单

allowlist

on-miss

匹配白名单则执行,否则询问用户

全开放

full

off

直接执行(仅适用于沙箱)

minSecurity() 函数确保安全等级只能降低(取最小值):

Shell 命令分析引擎

在评估白名单时,OpenClaw 需要解析 Shell 命令来提取可执行文件路径。这是一个迷你的 Shell 解析器:

命令解析器支持:

  • 链式操作符:&&||;

  • 管道:|

  • 单引号和双引号

  • 转义字符 \

  • 拒绝的危险 token:><`$()\n

审批流程

当命令不在白名单中且 ask 不为 off 时,触发用户审批:

审批请求通过 Unix Domain Socket 传递,确保只有本机进程能参与审批。


15.1.7 后台化与产出收集

yieldMs 机制

exec 工具最巧妙的设计之一是 yieldMs——在命令执行期间,先同步等待一段时间,如果命令没完成就自动转入后台:

这个设计的好处:

  • 快速命令(如 lscat)直接返回结果,无需额外的 poll 调用

  • 长时间命令(如 npm installmake)不会阻塞 Agent 的对话循环

  • Agent 可以在等待时继续处理其他任务,然后用 process 工具检查结果

超时处理

超时后的处理分两步:

  1. 立即杀死进程(通过 killProcessTree() 杀掉整个进程树)

  2. 等待 1 秒让内核清理,然后标记会话为 failed 并收集已有输出


本节小结

  1. exec 工具是 OpenClaw 最复杂的工具,约 1630 行代码,涉及进程管理、安全审批、PTY 终端等多个子系统

  2. 三种执行宿主:sandbox(Docker 隔离)、gateway(宿主机直接执行)、node(远程节点 RPC)

  3. 三种进程创建方式:Docker exec、PTY spawn(node-pty)、普通 child_process spawn,每种都有降级策略

  4. PTY 模式需要处理 DSR 请求——伪装为终端回复光标位置查询,否则 TUI 程序会卡住

  5. 多层安全防护:环境变量黑名单、PATH 修改禁止、白名单 + safe bins、用户审批流程

  6. Shell 命令解析器支持链式操作、管道、引号,可以精确提取每个管道段的可执行文件路径

  7. yieldMs 设计让快速命令直接返回、长时间命令自动后台化,兼顾响应速度和非阻塞性

Last updated