Skip to content

命令执行引擎 ​

本文引用的源码文件

本文引用的文件

目录 ​

  1. 引言
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能与资源约束
  8. 故障排查指南
  9. 结论
  10. 附录:命令模板、策略配置与调优建议

引言 ​

本文件聚焦 Agent 端的「命令执行引擎」,围绕以下目标展开:

  • 命令解析与安全分级:解释黑名单、灰名单、链式命令拆分、包装器剥壳、内联载荷与命令替换扫描。
  • 参数处理与执行上下文:工作目录、安全环境变量白名单、平台 Shell 选择、输出缓冲与截断。
  • 进程树管理与监控:子进程生命周期、WebSocket 流式输出、已完成进程回放、跨平台终止。
  • 超时与预算控制:受管模式、静默即杀、延长策略、总预算封顶。
  • 沙箱隔离与权限控制:路径白名单、硬阻止目录、脚本执行权限剥离、CORS 与认证边界。
  • 并发、竞争与内存保护:运行中进程 Map、输出上限、定时器清理、孤儿进程防护。
  • 调试与可观测性:日志分类、运行进程列表、错误事件、完成缓存。

本项目为双进程架构:Chrome 扩展端(Manifest V3)通过本地 HTTP/WebSocket 调用 Agent;Agent 是独立 Node.js 服务。命令执行能力由 agent/src/server.js 暴露接口,并委托 agent/src/executor.js 管理子进程,由 agent/src/security.js 做命令与路径安全校验,agent/src/process-tree.js 提供进程树终止工具。

章节来源

项目结构 ​

命令执行相关代码集中在 Agent 端:

  • server.js:HTTP 路由、认证、WebSocket 连接、命令执行接口、状态查询。
  • executor.js:命令执行器、Shell 选择、超时策略、进程跟踪、输出流、WS 广播。
  • security.js:路径沙箱、命令黑白名单、链式命令拆分、包装器剥壳、内联载荷提取。
  • process-tree.js:跨平台进程树终止与优雅退出封装。
  • 单元测试:覆盖超时策略纯函数与命令安全检查回归用例。
graph TB
Client["扩展端 / 外部客户端"] --> Server["Agent HTTP+WebSocket 服务器<br/>server.js"]
Server --> Security["命令与路径安全层<br/>security.js"]
Server --> Executor["命令执行器<br/>executor.js"]
Executor --> OS["操作系统 Shell 与子进程"]
Server --> ProcessTree["进程树终止工具<br/>process-tree.js"]
Server --> Logger["日志系统"]

图表来源

章节来源

核心组件 ​

  • 命令执行器:负责选择 Shell、创建子进程、流式 stdout/stderr、超时裁决、进程终止、运行状态查询。
  • 安全层:负责路径白名单与硬阻止检查、命令分级(allow/confirm/deny)、链式命令与内联载荷扫描。
  • 进程树工具:提供跨平台递归终止与优雅退出封装。
  • 服务器入口:统一认证、CORS、请求体限制、命令执行接口、WebSocket 流式输出、运行进程列表上报。

章节来源

架构总览 ​

命令从扩展端进入 Agent 后,经历“认证 → 安全校验 → 执行调度 → 流式输出 → 超时/终止 → 结果回放”的完整链路。

sequenceDiagram
participant Ext as "扩展端"
participant Srv as "server.js"
participant Sec as "security.js"
participant Exe as "executor.js"
participant OS as "OS Shell/子进程"
participant WS as "WebSocket 客户端"
Ext->>Srv : "POST /api/exec含 command、cwd、timeoutMs"
Srv->>Sec : "checkCommand(command, force)"
Sec-->>Srv : "{level, reason}"
alt level=deny
Srv-->>Ext : "拒绝响应"
else level=confirm
Srv-->>Ext : "需要用户确认"
Ext-->>Srv : "force=true 或取消"
end
Srv->>Exe : "executeCommand(command, cwd, wsClient, onComplete, collectOutput, {timeoutMs})"
Exe->>OS : "spawn(shell, args, command)"
OS-->>Exe : "stdout/stderr/close/error"
Exe->>WS : "broadcast(stdout/stderr/error/exit)"
Exe->>Exe : "decideTimeoutAction(...) 裁决延长或终止"
Exe->>OS : "killProcess(execId) 必要时终止进程树"
Exe-->>Srv : "onComplete({execId, exitCode, killed, timedOut})"
Srv-->>Ext : "最终结果"

图表来源

章节来源

详细组件分析 ​

命令解析与安全分级 ​

安全层对命令进行三层处理:

  1. 子命令范围收集:原始整串 + 链式拆分 + 包装器剥壳 + 解释器内联载荷 + 命令替换内部。
  2. 黑名单判定:绝对禁止项(如破坏系统目录、格式化磁盘、fork bomb、恶意管道执行等),不可被 force 绕过。
  3. 灰名单判定:高风险但可确认的操作(sudo、rm -rf、全局安装、网络外联、进程操作、脚本执行等),可通过用户确认跳过。

关键机制包括:

  • collectCommandScopes:递归收集所有需检查的命令片段,深度上限防止无限展开。
  • splitShellSegments:按 &&、||、;、|、&、换行拆分,但保留引号、反引号、$( ) 内部内容。
  • peelCommandWrappers:剥离 nohup、time、env、sudo、doas 等前缀,暴露真实命令。
  • extractInlinePayloads:提取 POSIX -c/-e、cmd /c /k、PowerShell -Command/-EncodedCommand 中的载荷。
  • extractSubstitutions:提取反引号与 $() 内的子命令,避免引号包裹导致的安全逃逸。
  • normalizeShellPathTargets:折叠 //、/.、.. 等路径变形,防止绕过系统目录黑名单。
flowchart TD
Start(["输入命令"]) --> Collect["collectCommandScopes<br/>收集原始整串与子段"]
Collect --> Split["splitShellSegments<br/>按控制符拆分"]
Split --> Peel["peelCommandWrappers<br/>剥离 nohup/env/sudo 等包装器"]
Split --> Inline["extractInlinePayloads<br/>提取 -c/-e / /c / -Command"]
Split --> Subst["extractSubstitutions<br/>提取 $( ) 与反引号内部"]
Split --> PathNorm["normalizeShellPathTargets<br/>折叠 // /. .. 路径"]
Peel --> CheckBlack["BLACKLIST_PATTERNS 命中?"]
Inline --> CheckBlack
Subst --> CheckGray["GRAYLIST_PATTERNS 命中?"]
PathNorm --> CheckBlack
CheckBlack --> |是| Deny["返回 deny不可 force 绕过"]
CheckBlack --> |否| Force{"force 已确认?"}
Force --> |是| Allow["返回 allow"]
Force --> |否| CheckGray
CheckGray --> |是| Confirm["返回 confirm需用户确认"]
CheckGray --> |否| Allow

图表来源

章节来源

参数处理与执行上下文 ​

命令执行器在启动子进程前构建执行上下文:

  • 工作目录:优先使用传入 cwd,否则回退到配置中的 workdir。
  • Shell 选择:根据平台与环境变量选择 bash/zsh/fish/cmd/powershell,并处理 Git Bash MSYS 路径问题。
  • 环境变量白名单:仅透传 PATH、HOME、USER、COMSPEC、PSModulePath 等必要变量,强制关闭彩色输出。
  • 输出缓冲:stdout/stderr 分别维护缓冲区,单流最大 5MB,超过则标记截断并广播通知。
  • WebSocket 广播:实时推送 stdout、stderr、error、exit 消息给所有连接的客户端。
flowchart TD
Enter(["executeCommand 入口"]) --> Cwd["确定 workdir"]
Cwd --> Shell["pickShell 选择 shell 与参数"]
Shell --> Env["buildSafeEnv 构建安全环境变量"]
Env --> Spawn["spawn(child_process) 创建子进程"]
Spawn --> Stdout["stdout 数据回调"]
Spawn --> Stderr["stderr 数据回调"]
Stdout --> Buffer["appendWithLimit 追加到缓冲区"]
Stderr --> Buffer
Buffer --> Broadcast["broadcast 推送 WS 消息"]
Spawn --> Close["close/error 事件"]
Close --> Finalize["completedProcesses 缓存 + runningProcesses 清理"]

图表来源

章节来源

进程树管理与子进程监控 ​

执行器维护两个核心数据结构:

  • runningProcesses:当前运行中的进程映射,包含子进程对象、WS 客户端集合、定时器、输出缓冲、命令文本、开始时间、超时状态等。
  • completedProcesses:已完成进程的短期缓存,用于延迟连接的 WS 客户端回放全部输出与结束事件。

进程终止策略:

  • Unix:优先向进程组发送 SIGTERM,5 秒后升级为 SIGKILL。
  • Windows:使用 taskkill /T /PID 递归终止进程树,失败后强制 /F。
  • 显式 detach:允许命令脱离超时控制继续运行,适用于后台任务。
classDiagram
class RunningProcessEntry {
+string execId
+ChildProcess process
+Set~wsClient~ wsClients
+number timeoutId
+number forceKillId
+string stdoutBuf
+string stderrBuf
+boolean truncStdout
+boolean truncStderr
+string command
+number startTime
+number baseTimeout
+boolean managed
+number extensions
+boolean outputInPeriod
+boolean detached
+boolean timedOut
}
class CompletedProcessEntry {
+string stdoutBuf
+string stderrBuf
+number exitCode
+boolean killed
+boolean timedOut
+string error
+number cleanupTimeout
}
class Executor {
+Map~execId, RunningProcessEntry~ runningProcesses
+Map~execId, CompletedProcessEntry~ completedProcesses
+executeCommand()
+killProcess()
+addWsClient()
+disconnectWsClient()
+detachProcess()
+getRunningProcesses()
}
Executor --> RunningProcessEntry : "持有"
Executor --> CompletedProcessEntry : "短期缓存"

图表来源

章节来源

超时处理机制与预算控制 ​

超时策略分为两种模式:

  • 旧模式:到期直接 kill,不延长。
  • 受管模式:通过 decideTimeoutAction 决策是否延长,规则如下:
    • 若延长次数已达上限,直接 kill。
    • 若本预算期内无输出(stdout/stderr 未触发),视为挂起,直接 kill。
    • 若 nextBudget 无法增长(触及总预算封顶),kill。
    • 否则延长一次,重置 outputInPeriod,重新设置定时器。

总预算封顶为 600 秒,延长次数上限为 5 次。该逻辑为纯函数,便于单元测试验证。

flowchart TD
Start(["超时到达"]) --> Managed{"受管模式?"}
Managed --> |否| KillOld["直接 kill 进程"]
Managed --> |是| Decision["decideTimeoutAction(state)"]
Decision --> ExtensionsMax{"extensions >= MAX_TIMEOUT_EXTENSIONS?"}
ExtensionsMax --> |是| KillExtend["kill 进程"]
ExtensionsMax --> |否| OutputCheck{"outputInPeriod ?"}
OutputCheck --> |否| KillSilent["kill 进程静默即杀"]
OutputCheck --> |是| BudgetCalc["计算 currentBudget 与 nextBudget"]
BudgetCalc --> CanExtend{"nextBudget > currentBudget ?"}
CanExtend --> |否| KillCap["kill 进程触及封顶"]
CanExtend --> |是| Extend["延长一次,重置 outputInPeriod,重新 armTimeout"]

图表来源

章节来源

输出流处理、错误捕获与调试信息 ​

  • 输出流:stdout/stderr 分别流式广播,同时累积到缓冲区供回放与可选内存收集。
  • 截断通知:当缓冲区超过 5MB,广播 stdout_truncated 或 stderr_truncated 消息。
  • 错误捕获:子进程 error 事件会广播错误消息,并将进程移入 completedProcesses 缓存。
  • 调试信息:getRunningProcesses 返回运行中进程的 execId、pid、command、startTime、duration。
  • 日志分类:server.js 将日志分类为 auth、fs、exec、security、system、error,便于审计与排查。

章节来源

并发执行控制、资源竞争与内存泄漏防护 ​

  • 并发控制:每个命令生成唯一 execId,通过 Map 管理,避免重复执行冲突。
  • 资源竞争:finished 标志防止 close/error 重复处理;定时器在 clearTimers 中统一清理。
  • 内存泄漏防护:
    • 输出缓冲区上限 5MB,超限时截断并标记。
    • completedProcesses 缓存 TTL 30 秒,自动清理。
    • disconnectWsClient 在受管模式下不解除超时,避免 SW 空闲被 Chrome 终止后命令提前释放。
    • killProcess 5 秒后强制 kill,防止孤儿进程。

章节来源

依赖关系分析 ​

命令执行引擎的核心依赖如下:

  • server.js 依赖 executor.js、security.js、process-tree.js、logger.js、config.js、auth.js。
  • executor.js 依赖 child_process、fs、crypto、os、config.js、i18n.js。
  • security.js 依赖 path、fs/promises、os、config.js、i18n.js。
  • process-tree.js 依赖 child_process。
graph LR
Server["server.js"] --> Executor["executor.js"]
Server --> Security["security.js"]
Server --> ProcessTree["process-tree.js"]
Server --> Logger["logger.js"]
Server --> Config["config.js"]
Server --> Auth["auth.js"]
Executor --> ChildProc["child_process"]
Executor --> FS["fs"]
Executor --> Crypto["crypto"]
Executor --> OS["os"]
Security --> Path["path"]
Security --> FSPromises["fs/promises"]

图表来源

章节来源

性能与资源约束 ​

  • 输出缓冲上限:5MB,防止长时间命令导致内存无限增长。
  • 上传大小限制:JSON body 200MB,流式上传 200MB。
  • 搜索结果上限:单次最多 5000 条。
  • 文件读取默认上限:50MB,可通过配置调整。
  • 超时总预算封顶:600 秒,延长次数上限 5 次。
  • 进程终止宽限期:5 秒后强制 kill,防止孤儿进程。
  • 已完成进程缓存 TTL:30 秒,自动清理。

章节来源

故障排查指南 ​

常见问题与定位方法:

  • 命令被拒绝:检查 security.js 的黑名单与灰名单,确认是否命中敏感操作。
  • 命令超时:查看 executor.js 的 decideTimeoutAction 逻辑,确认是否因静默或达到延长上限被 kill。
  • 输出缺失:检查 WS 客户端是否正确连接,completedProcesses 是否已回放。
  • 进程残留:确认 killProcess 是否成功调用,Windows 下 taskkill 是否执行。
  • 路径访问失败:检查 checkPath 的白名单与硬阻止逻辑,确认 workdir 与 allowedPaths 配置。

章节来源

结论 ​

命令执行引擎通过安全层与执行器的解耦设计,实现了命令解析、安全分级、进程管理、超时控制与资源保护的完整闭环。其核心优势在于:

  • 安全分层清晰:黑名单绝对禁止,灰名单可确认,路径沙箱与硬阻止目录双重保障。
  • 执行可控性强:受管模式下的超时预算与延长策略,避免长尾任务占用资源。
  • 可观测性好:WS 流式输出、运行进程列表、日志分类与完成缓存,便于调试与审计。
  • 跨平台兼容:Windows 与 Unix 的 Shell 选择、进程终止策略均经过细致适配。

未来优化方向可考虑:

  • 动态调整超时预算与输出缓冲上限。
  • 增加命令执行配额与并发限制。
  • 提供更细粒度的命令模板与策略配置界面。

[本节为总结性内容,不直接分析具体文件]

附录:命令模板、策略配置与调优建议 ​

命令模板建议 ​

  • 只读查询类:ls、pwd、whoami、date、uname、git status、npm run build。
  • 安全写入类:仅在 workdir 内写入,避免系统目录与敏感文件。
  • 高风险操作类:sudo、rm -rf、chmod、chown、curl、wget、kill、systemctl 等,需用户确认。
  • 脚本执行类:避免直接执行外部脚本,优先使用内置工具或受控环境。

执行策略配置 ​

  • commandTimeout:基础超时时间,影响受管模式的初始预算。
  • workdir:工作目录,始终放行且不受 allowedPaths 限制。
  • allowedPaths:额外允许访问的路径白名单。
  • fileMaxSize:文件读取默认大小限制。
  • MAX_TIMEOUT_EXTENSIONS:最大延长次数。
  • TOTAL_BUDGET_CAP_MS:总预算封顶时间。

性能调优指南 ​

  • 合理设置 commandTimeout,避免过长等待。
  • 监控 runningProcesses,及时终止卡死任务。
  • 调整输出缓冲上限,平衡内存与回放需求。
  • 使用流式下载与上传,避免大文件内存膨胀。
  • 定期清理 completedProcesses 缓存,防止内存泄漏。

章节来源