Skip to content

命令执行引擎 ​

本文引用的源码文件

本文引用的文件

目录 ​

  1. 引言
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能与资源特性
  8. 故障排查指南
  9. 安全边界与已知限制
  10. 最佳实践与安全建议
  11. 结论

引言 ​

本文面向“命令执行引擎”的完整技术文档,聚焦以下目标:

  • 解释命令安全分级系统:黑名单(deny)与灰名单(confirm)的区别,以及 force 参数的作用机制。
  • 深入说明命令解析器:splitShellSegments 链式拆分、collectCommandScopes 子命令范围收集、substitutionCheckScopes 命令替换特征检测。
  • 描述进程管理功能:进程树构建、父子关系追踪、超时控制与资源清理。
  • 说明命令执行策略:并行执行限制、输出流处理、错误恢复机制。
  • 明确安全边界:变量间接执行等静态不可判定场景。
  • 给出命令执行的最佳实践和安全建议。

本项目采用双进程架构:Chrome 扩展端(Manifest V3,src/)与独立本地代理端 Agent(Node.js,agent/src/server.js)。命令执行能力由 Agent 端实现,扩展端通过 HTTP/WebSocket 调用;Agent 源码修改后需重启进程才生效。

项目结构 ​

命令执行相关代码主要分布在 Agent 端的三个模块中:

  • agent/src/security.js:命令安全分级、路径沙箱、命令解析与模式匹配。
  • agent/src/executor.js:命令执行、进程生命周期、超时策略、WebSocket 流式输出。
  • agent/src/process-tree.js:进程树终止工具,用于 npm 等派生孙进程场景。
  • src/background/completion-verifier.js:扩展端完成度校验侧的命令构建逻辑,体现只读模板与 shQuoteArg 转义约束。
graph TB
subgraph "扩展端"
CV["completion-verifier.js<br/>构建验证命令"]
end
subgraph "Agent 端"
SEC["security.js<br/>命令安全分级 + 解析"]
EXEC["executor.js<br/>进程执行 + 超时 + 流式输出"]
PT["process-tree.js<br/>进程树终止工具"]
end
CV --> |"HTTP/WebSocket 调用"| SEC
SEC --> |"checkCommand / collectCommandScopes"| EXEC
EXEC --> |"killProcess / taskkill / SIGKILL"| PT

图表来源

章节来源

核心组件 ​

  • 命令安全分级:基于黑名单与灰名单的正则模式匹配,支持链式命令逐段检查、解释器内联载荷展开、命令替换特征检测,并区分 deny/confirm/allow 三种结果。
  • 命令解析器:splitShellSegments 按 shell 控制符拆分命令串;collectCommandScopes 递归收集原始整串、拆分子段、剥壳包装器、解释器 -c/-e 载荷、$( ) 与反引号替换;substitutionCheckScopes 对替换位置做误报纠偏。
  • 进程执行器:spawn 启动 shell,选择平台 shell,设置安全环境变量,流式广播 stdout/stderr,维护运行中进程映射与已完成缓存,提供同步执行封装。
  • 超时策略:受管模式下根据 decideTimeoutAction 决策 extend/kill,非受管模式直接 kill;支持最大延长次数与总预算封顶。
  • 进程树终止:POSIX 使用负 PID 向进程组发信号,Windows 使用 taskkill /T /PID,并提供 stopProcessTree 等待退出或强制升级。

章节来源

架构总览 ​

命令执行从扩展端到 Agent 端的典型流程如下:

sequenceDiagram
participant UI as "扩展端界面"
participant CV as "completion-verifier.js"
participant API as "Agent HTTP/WebSocket"
participant SEC as "security.js"
participant EXEC as "executor.js"
participant PT as "process-tree.js"
UI->>CV : "构建验证命令只读模板/shQuoteArg"
CV-->>UI : "command 或 reason"
UI->>API : "提交命令执行请求"
API->>SEC : "checkCommand(command, force)"
SEC-->>API : "{safe, level, reason}"
alt level=deny
API-->>UI : "拒绝执行黑名单命中"
else level=confirm
API-->>UI : "弹出确认弹窗"
UI->>API : "用户确认force=true"
API->>EXEC : "executeCommand(command, wsClient)"
else level=allow
API->>EXEC : "executeCommand(command, wsClient)"
end
EXEC->>EXEC : "spawn shell + 流式输出 + 超时控制"
EXEC->>PT : "killProcess / stopProcessTree必要时"
EXEC-->>UI : "stdout/stderr/exit/error 事件"

图表来源

详细组件分析 ​

命令安全分级系统(deny/confirm/allow 与 force) ​

  • 黑名单(deny):绝对禁止的高危模式,包括递归删除系统目录、格式化磁盘、覆盖敏感系统文件、fork bomb、shebang 直接执行、管道到解释器提权执行、根目录权限变更等。黑名单始终生效,即使 force 也不可绕过。
  • 灰名单(confirm):需要真实用户确认的模式,包括 sudo/doas/runas、全局 npm install、pip install/uninstall、chmod 777、rm -rf 变体、git push -f、shutdown/reboot/halt/poweroff、写 /etc/hosts、eval、命令替换、dd of=、shred、truncate、chmod/chown、网络外联(curl/wget/nc/telnet/ssh/scp)、进程操作(kill/pkill/killall)、系统服务(systemctl/launchctl/crontab/sysctl)、脚本解释器执行外部脚本、PowerShell 破坏性 cmdlet 与 base64 内联载荷等。
  • allow:未命中任何黑名单或灰名单时允许执行。
  • force 参数:当用户已确认(例如点击确认弹窗或关闭确认开关),可跳过灰名单检查,但黑名单仍生效。
flowchart TD
Start(["输入命令"]) --> Validate["校验命令是否为空/非法"]
Validate --> |非法| DenyInvalid["返回 deny命令无效"]
Validate --> |合法| Scopes["collectCommandScopes 收集子命令范围"]
Scopes --> Subst["substitutionCheckScopes 计算替换送检集合"]
Subst --> Blacklist{"是否命中黑名单?"}
Blacklist --> |是| DenyBlock["返回 deny命令被阻止"]
Blacklist --> |否| Force{"force 是否开启?"}
Force --> |是| AllowForce["返回 allow跳过灰名单"]
Force --> |否| Graylist{"是否命中灰名单?"}
Graylist --> |是| Confirm["返回 confirm需要用户确认"]
Graylist --> |否| AllowSafe["返回 allow安全检查通过"]

图表来源

章节来源

命令解析器:splitShellSegments、collectCommandScopes、substitutionCheckScopes ​

  • splitShellSegments:按 shell 控制符拆分命令串,同时尊重引号、反引号、$( ) 内部不拆分,避免把 "a && b" 误拆。
  • collectCommandScopes:始终包含原始整串,再追加逐段拆分结果;剥离 env/sudo/doas/nohup/time/timeout/nice/stdbuf/exec/command/xargs 等包装器前缀,递归暴露内层命令;归一化路径目标(//usr、/.//usr、/tmp/../usr);提取解释器内联载荷(POSIX -c/-e、cmd /c /k、PowerShell -Command/-EncodedCommand);提取 $( ) 与反引号替换内容;深度封顶防死循环。
  • substitutionCheckScopes:对命令替换特征做误报纠偏,排除纯输出头命令(echo/printf/cat/head/tail/wc/ls/pwd/date/whoami/hostname/uname/id/env/printenv/which/type)和无害头命令(true/false/test/cd/dir),并在引号参数内查找含替换片段,避免扩大扫描范围到整个命令串。
flowchart TD
Cmd["原始命令"] --> Split["splitShellSegments 拆分"]
Split --> Segments["子命令段集合"]
Segments --> Peel["peelCommandWrappers 剥壳包装器"]
Peel --> Recurse["collectCommandScopes 递归收集"]
Recurse --> Inline["extractInlinePayloads 提取 -c/-e /c /k /Command /EncodedCommand"]
Recurse --> Subst["extractSubstitutions 提取 $( ) 与反引号"]
Inline --> Scopes["最终子命令范围集合"]
Subst --> Scopes
Scopes --> SubstCheck["substitutionCheckScopes 替换特征纠偏"]

图表来源

章节来源

进程管理与超时控制 ​

  • 进程树构建:executor.js 使用 spawn 启动 shell,并根据平台选择 bash/zsh/fish/cmd/powershell;detached 在 Unix 上启用以支持进程组终止,在 Windows 原生控制台 shell 上禁用以避免 AllocConsole 黑窗。
  • 父子关系追踪:runningProcesses 映射 execId 到进程条目,包含 process、wsClients、缓冲区、定时器、状态字段;completedProcesses 缓存已结束进程的输出与状态,供延迟连接的 WS 客户端回放。
  • 超时控制:受管模式下,decideTimeoutAction 根据期内是否有输出决定 extend 或 kill;非受管模式到期直接 kill;支持最大延长次数与总预算封顶(默认 600000ms)。
  • 资源清理:clearTimers 清理 timeoutId 与 forceKillId;disconnectWsClient 在所有客户端断开且非受管或未显式 detach 时取消超时;detachProcess 显式声明 daemon 化取消超时;killProcess 跨平台递归终止进程树,5 秒后升级为 SIGKILL/taskkill /F。
classDiagram
class Executor {
+executeCommand(command, cwd, wsClient, onComplete, collectOutput, execOpts) string
+executeCommandSync(command, cwd, timeoutMs) Promise
+addWsClient(execId, wsClient) boolean
+disconnectWsClient(execId, wsClient) boolean
+detachProcess(execId) boolean
+killProcess(execId) boolean
+getRunningProcesses() Array
+pickShell(platform, env, exists) Object
+decideTimeoutAction(state, capMs, maxExtensions) Object
}
class ProcessEntry {
+process ChildProcess
+wsClients Set
+stdoutBuf string
+stderrBuf string
+truncStdout boolean
+truncStderr boolean
+command string
+startTime number
+baseTimeout number
+managed boolean
+extensions number
+outputInPeriod boolean
+detached boolean
+timedOut boolean
+timeoutId number
+forceKillId number
}
class ProcessTree {
+killProcessTree(child, signal) boolean
+stopProcessTree(child, options) Promise
}
Executor --> ProcessEntry : "维护 runningProcesses"
Executor --> ProcessTree : "必要时调用 killProcessTree"

图表来源

章节来源

命令执行策略:并行限制、输出流处理、错误恢复 ​

  • 并行执行限制:命令执行本身在 executor.js 中为单进程执行;并行限制更多体现在扩展端的子任务调度(dispatchSubtasksParallel),测试用例表明峰值并发应 ≤ maxParallel。
  • 输出流处理:stdout/stderr 分别流式广播,超过 5MB 缓冲上限时截断并发送 trunc 通知;completedProcesses 缓存输出以便延迟连接回放。
  • 错误恢复:进程 error/close 事件统一处理,finished 标志防止重复处理;onComplete 回调返回 exitCode/killed/error;sync 模式额外兜底超时保护,确保不会永久挂起。
flowchart TD
Spawn["spawn shell"] --> Stdout["stdout 数据事件"]
Spawn --> Stderr["stderr 数据事件"]
Stdout --> Buffer["appendWithLimit 缓冲 + 截断标记"]
Stderr --> Buffer
Buffer --> Broadcast["broadcast 到 wsClients"]
Broadcast --> Exit["close/error 事件"]
Exit --> Cleanup["clearTimers + 移入 completedProcesses"]
Cleanup --> Complete["onComplete 回调"]

图表来源

章节来源

依赖关系分析 ​

  • security.js 依赖 path/fs/promises/os/config/i18n,提供 checkPath/checkCommand/collectCommandScopes 等能力。
  • executor.js 依赖 child_process/fs/crypto/os/config/i18n,提供 executeCommand/executeCommandSync/addWsClient/disconnectWsClient/killProcess/detachProcess/getRunningProcesses/pickShell。
  • process-tree.js 依赖 child_process,提供 killProcessTree/stopProcessTree。
  • completion-verifier.js 在扩展端构建只读验证命令,体现 shQuoteArg 转义与 bad_path 防护。
graph LR
SEC["security.js"] --> PATH["path"]
SEC --> FS["fs/promises"]
SEC --> OS["os"]
SEC --> CFG["config.js"]
SEC --> I18N["i18n.js"]
EXEC["executor.js"] --> CP["child_process"]
EXEC --> FS2["fs"]
EXEC --> CRYPTO["crypto"]
EXEC --> OS2["os"]
EXEC --> CFG2["config.js"]
EXEC --> I18N2["i18n.js"]
PT["process-tree.js"] --> CP2["child_process"]
CV["completion-verifier.js"] --> SHQ["shQuoteArg只读模板"]

图表来源

章节来源

性能与资源特性 ​

  • 输出缓冲上限:5MB,超过后截断并通知前端,防止长时间命令内存无限增长。
  • 环境变量白名单:SAFE_ENV_KEYS 过滤宿主敏感信息,强制 TERM=dumb 与 FORCE_COLOR=0,减少终端颜色干扰。
  • 超时策略:decideTimeoutAction 支持 extend/kill,最多延长 MAX_TIMEOUT_EXTENSIONS 次,总预算不超过 TOTAL_BUDGET_CAP_MS。
  • 进程树终止:POSIX 优先进程组信号,Windows 使用 taskkill /T /PID,5 秒后升级为 SIGKILL /F。
  • 已完成进程缓存:COMPLETED_CACHE_TTL=30000ms,延迟连接 WS 客户端可回放输出与状态。

[本节为通用性能讨论,不直接分析具体文件]

故障排查指南 ​

  • 命令被拒绝:检查是否命中黑名单(deny),如递归删除系统目录、格式化磁盘、覆盖敏感文件等;若为灰名单(confirm),需用户确认或关闭确认开关(force)。
  • 命令执行无输出:检查 stdout/stderr 是否被截断(trunc 通知),或 WS 客户端是否正确添加(addWsClient)与回放(completedProcesses)。
  • 进程未终止:检查 killProcess 是否成功发出信号,Windows 下 taskkill /T /PID 是否失败回退 /F;POSIX 下负 PID 进程组是否存在。
  • 超时异常:检查 decideTimeoutAction 的 outputInPeriod 是否为 true,extensions 是否达到上限,baseTimeout 与 capMs 配置是否合理。
  • 路径访问失败:检查 checkPath 的 allowedPaths/workdir/MEMORY_DIR 配置,realpath 符号链接绕过防护,硬阻止路径(HARD_BLOCKED_PATHS)。

章节来源

安全边界与已知限制 ​

  • 命令通道不受 allowedPaths 路径沙箱约束:cat /etc/passwd 这类读取即外泄的动作无法在本层拦截。
  • 变量间接执行(如 S=rm; $S -rf x)与脚本文件正文内容静态不可判:不在本层解决,遇到时以注释说明而非假装防住。
  • curl/wget/kill 等条目会给常规用法带来确认弹窗,属预期摩擦,可用“本次任务放行”降低频次。
  • 黑名单与灰名单的豁免边界:SUBSTITUTION_HARMLESS_HEADS 的只读豁免仅适用于命令替换这一语法特征,不应用于黑名单与其余灰名单条目。

章节来源

最佳实践与安全建议 ​

  • 优先使用只读命令进行验证:completion-verifier.js 构建 test -e 等只读模板,并通过 shQuoteArg 转义路径,避免注入型路径。
  • 谨慎使用 force:仅在用户明确确认后启用,避免绕过灰名单确认。
  • 合理配置超时:根据任务复杂度调整 commandTimeout,受管模式下利用 decideTimeoutAction 的动态延长机制。
  • 监控输出截断:当 stdout/stderr 超过 5MB 时,前端应提示截断并记录 execId 以便回放。
  • 避免高危命令:尽量不使用 rm -rf、mkfs、dd of=/dev/、chmod 777、sudo/doas 等高危命令,确需使用时务必经过确认。
  • 注意平台差异:Windows 下 cmd/powershell/pwsh 会创建控制台窗口,应避免 detached;Unix 下 detached 有助于进程组终止。

章节来源

结论 ​

命令执行引擎通过安全分级、命令解析、进程管理与超时控制,构建了较为完善的命令执行安全体系。黑名单绝对禁止、灰名单用户确认、force 跳过灰名单的机制提供了灵活的安全策略;splitShellSegments、collectCommandScopes、substitutionCheckScopes 的组合确保了链式命令、包装器、内联载荷与命令替换的全面覆盖;executor.js 的流式输出、超时策略与进程树终止保障了执行过程的稳定性与资源可控性。尽管存在变量间接执行等静态不可判定限制,但在当前边界内,该引擎为 AI 助手提供了可靠且安全的命令执行能力。