主题
命令安全分级系统
本文引用的源码文件
本文引用的文件
目录
引言
本文件系统性地梳理 Agent 端的命令安全分级能力,重点覆盖以下目标:
- 命令白名单机制:内置安全命令、动态白名单扩展、自定义命令注册。
- 危险命令检测算法:敏感操作识别、路径遍历防护、参数注入检测、命令拼接攻击防护。
- 执行权限控制模型:用户角色权限、命令级别授权、沙箱环境隔离、资源访问限制。
- 安全策略配置:白名单维护、风险等级定义、审计日志记录、违规处理机制。
- 实际安全配置示例与常见威胁防护说明。
需要特别说明的是,当前仓库中“命令安全”的核心实现集中在 Agent 端,主要包含两类能力:
- 文件路径沙箱:通过
checkPath控制文件类工具可访问的目录,防止越权读取或写入。 - 命令分级管控:通过
checkCommand对 shell 命令进行黑名单/灰名单判定,输出allow、confirm、deny三级结果。
此外,Agent 还实现了命令执行器(executor.js),负责子进程创建、流式输出、超时控制、进程终止与环境变量白名单;配置文件(config.js)提供工作目录、允许路径、命令超时等运行时参数;服务端(server.js)将安全检查接入具体 API;技能执行器(skill/executor.js)在技能场景下复用路径与命令安全检查。
项目结构与安全边界
从安全视角看,Agent 端的安全相关代码分布在以下位置:
agent/src/security.js:路径沙箱与命令分级核心逻辑。agent/src/executor.js:命令执行器、超时策略、环境变量白名单、进程生命周期管理。agent/src/config.js:默认配置、工作目录、允许路径、命令超时、内存缓存与写锁。agent/src/server.js:HTTP/WebSocket 接口入口,调用checkPath与checkCommand。agent/src/skill/executor.js:技能执行时调用路径检查与命令检查。agent/test/unit/command-security-check.unit.test.js:命令黑白名单单元测试。agent/test/unit/path-security.unit.test.js:路径格式规整与硬阻止单元测试。
graph TB Server["服务端<br/>agent/src/server.js"] --> Security["安全检查<br/>agent/src/security.js"] Server --> Executor["命令执行器<br/>agent/src/executor.js"] Server --> Config["配置中心<br/>agent/src/config.js"] SkillExecutor["技能执行器<br/>agent/src/skill/executor.js"] --> Security SkillExecutor --> Executor Tests["单元测试<br/>command-security-check / path-security"] --> Security
图表来源
- agent/src/server.js#L15-L15
- agent/src/security.js#L1-L10
- agent/src/executor.js#L1-L10
- agent/src/config.js#L1-L10
- agent/src/skill/executor.js#L1-L10
- agent/test/unit/command-security-check.unit.test.js#L1-L10
- agent/test/unit/path-security.unit.test.js#L1-L10
章节来源
- agent/src/security.js#L1-L10
- agent/src/executor.js#L1-L10
- agent/src/config.js#L1-L10
- agent/src/server.js#L15-L15
- agent/src/skill/executor.js#L1-L10
- agent/test/unit/command-security-check.unit.test.js#L1-L10
- agent/test/unit/path-security.unit.test.js#L1-L10
核心组件总览
本节按职责划分,说明命令安全分级系统的核心组件及其交互方式。
路径沙箱组件
路径沙箱由 security.js 中的 checkPath 实现,主要职责包括:
- 路径格式归一化:统一 Windows MSYS 风格、盘符大小写、反斜杠、前导斜杠盘符。
- 硬阻止目录:
.ai-helper-agent下的配置文件、配对记录、MCP 配置、日志、回收站、技能状态等不可访问。 - 工作目录放行:用户当前工作目录始终可访问,即使未显式加入允许列表。
- 允许路径校验:基于
allowedPaths做前缀匹配,并通过realpath解析符号链接后再次校验。 - 错误消息国际化:通过
translate返回可读原因。
命令分级组件
命令分级由 security.js 中的 checkCommand 实现,主要职责包括:
- 黑名单判定:绝对禁止的命令,如格式化磁盘、破坏系统目录、覆盖关键系统文件、fork bomb、恶意管道执行等。
- 灰名单判定:需要确认的危险命令,如
sudo、rm -rf、chmod 777、git push -f、全局安装、网络外联、进程终止、系统服务等。 - 链式命令拆分:支持
&&、;、|、\n等控制符,逐段检测,避免敏感命令藏在非开头位置。 - 包装器剥壳:剥离
nohup、time、timeout、env、exec、xargs、sudo、doas等前缀,暴露真实命令。 - 解释器内联载荷:解析 POSIX
-c/-e、cmd/c /k、PowerShell-Command/-EncodedCommand。 - 命令替换检测:提取
$( … )与反引号内部子命令,结合只读头命令豁免降低误报。 - 路径变形归一化:折叠
//、/.、..,防止通过路径写法绕过系统目录黑名单。
命令执行器组件
命令执行器由 executor.js 实现,主要职责包括:
- Shell 选择:根据平台与环境变量选择 bash、zsh、fish、cmd、powershell 等。
- 子进程创建:使用
child_process.spawn,设置工作目录、安全环境变量、标准输入输出管道。 - 流式输出:stdout/stderr 实时广播给 WebSocket 客户端,并限制缓冲区上限。
- 超时策略:支持受管模式与非受管模式,有输出才延长,无输出则直接终止。
- 进程终止:跨平台递归终止进程组,Windows 使用
taskkill /T /PID,Unix 使用负 PID 信号。 - 运行进程缓存:记录 execId、pid、command、startTime、duration,便于调试与监控。
配置组件
配置组件由 config.js 实现,主要职责包括:
- 默认值:端口、主机、工作目录、允许路径、命令超时、文件大小限制、RAG 网络策略、记忆嵌入配置。
- 内存缓存:按 mtime 刷新,避免频繁读取磁盘。
- 写锁:并发写保护,防止 TOCTOU 问题。
- 自动允许:skills 目录与工作目录自动加入允许列表。
- 敏感信息落盘混淆:apiKey、token 落盘前加密。
服务端集成
服务端 server.js 在多个 API 中调用 checkPath 与 checkCommand,例如:
- 文件读写 API:先校验路径,再执行操作。
- 命令执行 API:先校验命令,再交给 executor 执行。
- 技能执行 API:技能脚本中涉及文件与命令时,同样走安全检查。
章节来源
- agent/src/security.js#L109-L229
- agent/src/security.js#L231-L760
- agent/src/executor.js#L198-L386
- agent/src/config.js#L15-L39
- agent/src/config.js#L107-L149
- agent/src/server.js#L799-L799
- agent/src/server.js#L1038-L1054
- agent/src/skill/executor.js#L86-L119
架构概览
下图展示命令安全分级系统在 Agent 端的整体架构:
sequenceDiagram
participant Client as "调用方"
participant Server as "服务端<br/>server.js"
participant Security as "安全检查<br/>security.js"
participant Executor as "命令执行器<br/>executor.js"
participant Config as "配置中心<br/>config.js"
Client->>Server : "请求执行命令或访问文件"
Server->>Config : "加载 workdir / allowedPaths / commandTimeout"
Server->>Security : "checkPath(path)"
Security-->>Server : "{ allowed, reason, resolved }"
alt 路径被拒绝
Server-->>Client : "返回路径拒绝原因"
else 路径允许
Server->>Security : "checkCommand(command, force)"
Security-->>Server : "{ safe, level, reason }"
alt 命令 deny
Server-->>Client : "返回命令被拒绝"
else 命令 confirm
Server-->>Client : "请求用户确认"
Client-->>Server : "确认或取消"
opt 用户确认
Server->>Security : "checkCommand(command, true)"
Security-->>Server : "{ safe : true, level : 'allow' }"
Server->>Executor : "executeCommand(command)"
Executor-->>Server : "execId + 流式输出"
Server-->>Client : "转发 stdout/stderr/exit"
end
else 命令 allow
Server->>Executor : "executeCommand(command)"
Executor-->>Server : "execId + 流式输出"
Server-->>Client : "转发 stdout/stderr/exit"
end
end
图表来源
- agent/src/server.js#L799-L799
- agent/src/security.js#L109-L229
- agent/src/security.js#L691-L758
- agent/src/executor.js#L198-L386
- agent/src/config.js#L107-L149
详细组件分析
路径沙箱与白名单机制
路径沙箱的核心是 checkPath,其流程如下:
flowchart TD
Start(["函数入口"]) --> ValidateInput["校验输入是否为字符串"]
ValidateInput --> InputValid{"输入有效?"}
InputValid --> |否| ReturnInvalid["返回路径无效"]
InputValid --> |是| ExpandHome["展开 ~ 到主目录"]
ExpandHome --> NormalizeFormat["统一路径格式"]
NormalizeFormat --> ResolvePath["解析绝对路径"]
ResolvePath --> HardBlocked["检查硬阻止目录"]
HardBlocked --> IsHardBlocked{"命中硬阻止?"}
IsHardBlocked --> |是| ReturnForbidden["返回禁止访问代理文件"]
IsHardBlocked --> |否| CheckMemoryDir["检查记忆存储目录"]
CheckMemoryDir --> MemoryAllowed{"是否记忆目录?"}
MemoryAllowed --> |是| ReturnAllowed["返回允许"]
MemoryAllowed --> |否| CheckWorkdir["检查工作目录"]
CheckWorkdir --> WorkdirAllowed{"是否工作目录?"}
WorkdirAllowed --> |是| RealpathCheck["realpath 硬阻止检查"]
RealpathCheck --> ReturnWorkdirAllowed["返回允许"]
WorkdirAllowed --> |否| CheckAllowedPaths["检查 allowedPaths"]
CheckAllowedPaths --> PrefixMatch{"前缀匹配?"}
PrefixMatch --> |否| ReturnNotAllowed["返回不在允许目录"]
PrefixMatch --> |是| RealpathAgain["realpath 二次校验"]
RealpathAgain --> FinalCheck["最终硬阻止与前缀检查"]
FinalCheck --> ReturnFinal["返回允许或拒绝"]
图表来源
关键设计要点
- 硬阻止目录不可绕过:
.ai-helper-agent下的配置文件、日志、回收站等绝对禁止访问。 - 工作目录永远放行:即使用户未把 workdir 加入 allowedPaths,也允许访问。
- 符号链接防护:通过
realpath解析真实路径后再校验,防止通过软链接绕过。 - 跨平台兼容:Windows 盘符大小写不敏感比较,MSYS 路径转换,反斜杠统一为正斜杠。
- 允许路径动态扩展:
allowedPaths来自配置,默认包含工作目录与 skills 目录。
章节来源
- agent/src/security.js#L109-L229
- agent/src/config.js#L138-L149
- agent/test/unit/path-security.unit.test.js#L10-L106
命令白名单机制
当前系统中,“白名单”主要体现在两个层面:
- 路径白名单:
allowedPaths配置项决定文件类工具可访问的目录。 - 命令白名单:并非传统意义上的“允许列表”,而是通过黑名单/灰名单反向推导“允许”——未被拦截的命令视为允许。
内置安全命令
内置安全命令是指那些未被黑名单或灰名单捕获的命令,例如:
ls -lagit statusnpm run buildcat README.mdps aux | grep node
这些命令在单元测试中被明确断言为 allow。
动态白名单扩展
动态白名单扩展主要通过以下方式实现:
- 工作目录放行:workdir 始终允许,无需显式加入 allowedPaths。
- 记忆目录放行:MEMORY_DIR 始终允许,供 Agent 内部管理。
- skills 目录自动加入:配置加载时自动将 skills 目录加入 allowedPaths。
自定义命令注册
当前仓库中没有显式的“自定义命令注册表”。但可以通过以下方式间接实现:
- 技能系统:通过 SKILL.md 定义技能,技能执行器会调用
checkPath与checkCommand。 - MCP 工具注册:MCP 协议可扩展工具,但命令执行仍走安全检查。
- 配置扩展:通过 allowedPaths 扩展文件访问范围,但不改变命令分级逻辑。
章节来源
- agent/test/unit/command-security-check.unit.test.js#L28-L32
- agent/src/security.js#L142-L162
- agent/src/config.js#L138-L149
- agent/src/skill/executor.js#L86-L119
危险命令检测算法
危险命令检测由 checkCommand 实现,核心算法如下:
flowchart TD
Start(["函数入口"]) --> ValidateCommand["校验命令是否为字符串且非空"]
ValidateCommand --> Valid{"命令有效?"}
Valid --> |否| ReturnDeny["返回 deny"]
Valid --> |是| CollectScopes["收集子命令范围"]
CollectScopes --> SubstScopes["计算命令替换送检集合"]
SubstScopes --> BlacklistCheck["黑名单逐段检查"]
BlacklistCheck --> Blocked{"命中黑名单?"}
Blocked --> |是| ReturnBlocked["返回 deny"]
Blocked --> |否| CheckAgentFiles["检查是否引用代理敏感文件"]
CheckAgentFiles --> Forbidden{"命中硬阻止?"}
Forbidden --> |是| ReturnForbidden["返回 deny"]
Forbidden --> |否| ForceMode{"force 模式?"}
ForceMode --> |是| ReturnAllow["返回 allow"]
ForceMode --> |否| GraylistCheck["灰名单逐段检查"]
GraylistCheck --> GrayHit{"命中灰名单?"}
GrayHit --> |是| ReturnConfirm["返回 confirm"]
GrayHit --> |否| ReturnSafe["返回 allow"]
图表来源
敏感操作识别
黑名单与灰名单覆盖了以下敏感操作:
- 删除操作:
rm -rf /、rm -rf /usr、rm -rf /etc、rm -rf /var等。 - 提权操作:
sudo、doas、runas。 - 权限变更:
chmod 777、chmod 000、chown。 - 网络外联:
curl、wget、nc、ssh。 - 进程操作:
kill、pkill、killall。 - 系统服务:
systemctl、launchctl、crontab、sysctl。 - 脚本执行:外部脚本解释器执行
.sh、.py、.js等。 - Windows 特定:
rmdir /s、del /f、icacls、reg add/delete/import/load/restore、net user/localgroup、sc stop/delete/config、taskkill /f、PowerShellRemove-Item -Recurse、-EncodedCommand。
路径遍历防护
路径遍历防护主要通过以下方式实现:
- 路径归一化:折叠
//、/.、..,防止通过路径写法绕过系统目录黑名单。 - realpath 校验:文件访问时解析真实路径后再校验,防止符号链接绕过。
- 硬阻止目录:
.ai-helper-agent下的关键文件绝对禁止访问。
参数注入检测
参数注入检测主要通过以下方式实现:
- 命令替换提取:提取
$( … )与反引号内部子命令,单独送检。 - 引号内扫描:即使拆分器不拆引号内容,也会扫描引号内的替换表达式。
- 只读头命令豁免:
echo、printf、cat、head、tail、wc、ls、pwd、date、whoami、hostname、uname、id、env、printenv、which、type等只读命令的替换直接放行。
命令拼接攻击防护
命令拼接攻击防护主要通过以下方式实现:
- 链式命令拆分:支持
&&、;、|、\n等控制符,逐段检测。 - 包装器剥壳:剥离
nohup、time、timeout、env、exec、xargs、sudo、doas等前缀。 - 解释器内联载荷:解析 POSIX
-c/-e、cmd/c /k、PowerShell-Command/-EncodedCommand。 - 深度限制:嵌套解释器最多递归 3 层,防止死循环。
章节来源
- agent/src/security.js#L285-L384
- agent/src/security.js#L394-L560
- agent/src/security.js#L653-L683
- agent/src/security.js#L691-L758
- agent/test/unit/command-security-check.unit.test.js#L35-L116
执行权限控制模型
当前系统中的执行权限控制模型主要包括:
- 用户角色权限:未实现细粒度角色权限控制,主要通过
force参数模拟“用户已确认”。 - 命令级别授权:通过
checkCommand返回allow、confirm、deny三级结果。 - 沙箱环境隔离:通过
checkPath限制文件访问范围,通过buildSafeEnv限制环境变量。 - 资源访问限制:通过
MAX_OUTPUT_BUFFER限制输出缓冲区大小,通过超时策略限制命令执行时间。
用户角色权限
当前系统未实现多角色权限模型,但可通过以下方式模拟:
- 管理员模式:通过
force=true跳过灰名单检查,但仍保留黑名单。 - 普通用户模式:默认行为,所有灰名单命令需用户确认。
命令级别授权
命令级别授权由 checkCommand 实现:
- allow:安全命令,直接执行。
- confirm:危险命令,需用户确认。
- deny:绝对禁止命令,不可绕过。
沙箱环境隔离
沙箱环境隔离由 checkPath 与 buildSafeEnv 共同实现:
- 文件沙箱:限制文件访问范围为 workdir 与 allowedPaths。
- 环境变量沙箱:仅允许白名单中的环境变量,防止泄露宿主敏感信息。
资源访问限制
资源访问限制由 executor.js 实现:
- 输出缓冲区限制:最大 5MB,超过后截断并标记。
- 超时策略:受管模式下,有输出才延长,无输出则直接终止。
- 进程树终止:跨平台递归终止进程组,防止僵尸进程。
章节来源
- agent/src/security.js#L691-L758
- agent/src/executor.js#L118-L156
- agent/src/executor.js#L165-L186
- agent/src/executor.js#L298-L323
安全策略配置
安全策略配置主要由 config.js 提供,关键配置项包括:
- workdir:工作目录,默认
.ai-helper-agent/workspace。 - allowedPaths:允许路径列表,默认包含 workdir 与 skills 目录。
- commandTimeout:命令超时时间,默认 300000ms(5分钟)。
- fileMaxSize:文件大小限制,默认 50MB。
- ragUrlIngest.allowPrivateNetwork:RAG URL 导入内网访问策略。
- memoryEmbedding.enabled:长期记忆语义召回开关。
白名单维护指南
- 添加允许路径:通过 API 更新
allowedPaths,或使用 UI 切换工作目录。 - 移除允许路径:通过 API 从
allowedPaths中移除。 - 注意:workdir 始终允许,无需显式加入 allowedPaths。
风险等级定义
- 低风险:
allow,直接执行。 - 中风险:
confirm,需用户确认。 - 高风险:
deny,绝对禁止。
审计日志记录
当前系统未实现完整的审计日志记录,但可通过以下方式间接获取:
- 命令执行日志:executor.js 会记录 execId、command、startTime、duration。
- 错误日志:security.js 与 executor.js 会抛出错误消息。
- WebSocket 输出:stdout/stderr 实时广播,可用于外部审计。
违规处理机制
- 路径违规:返回
security.pathInvalid、security.forbiddenAgentFile、security.pathNotInAllowedDir、security.cannotResolvePath、security.pathOutOfRange、security.pathCheckError。 - 命令违规:返回
security.commandInvalid、security.commandBlocked。 - 用户确认失败:灰名单命令在未确认时被拒绝。
章节来源
- agent/src/config.js#L15-L39
- agent/src/config.js#L107-L149
- agent/src/security.js#L109-L229
- agent/src/security.js#L691-L758
依赖关系分析
命令安全分级系统的依赖关系如下:
graph TB Security["security.js"] --> Config["config.js"] Security --> I18n["i18n.js"] Executor["executor.js"] --> Config["config.js"] Executor --> I18n["i18n.js"] Server["server.js"] --> Security["security.js"] Server --> Executor["executor.js"] SkillExecutor["skill/executor.js"] --> Security["security.js"] SkillExecutor --> Executor["executor.js"] Tests["单元测试"] --> Security["security.js"]
图表来源
- agent/src/security.js#L1-L10
- agent/src/executor.js#L1-L10
- agent/src/server.js#L15-L15
- agent/src/skill/executor.js#L1-L10
章节来源
- agent/src/security.js#L1-L10
- agent/src/executor.js#L1-L10
- agent/src/server.js#L15-L15
- agent/src/skill/executor.js#L1-L10
性能与资源控制
命令安全分级系统在性能与资源控制方面采取了以下措施:
- 路径检查优化:先做前缀匹配,再做 realpath 校验,减少不必要的系统调用。
- 命令检查优化:黑名单优先,快速拒绝高危命令;灰名单仅在必要时触发。
- 输出缓冲区限制:最大 5MB,防止长时间命令占用过多内存。
- 超时策略优化:受管模式下,有输出才延长,无输出则直接终止,避免僵尸进程。
- 进程树终止:跨平台递归终止进程组,确保资源释放。
章节来源
- agent/src/security.js#L164-L178
- agent/src/security.js#L708-L719
- agent/src/executor.js#L96-L114
- agent/src/executor.js#L165-L186
- agent/src/executor.js#L500-L549
常见威胁与防护措施
本节列举常见安全威胁及对应的防护措施。
威胁 1:路径遍历攻击
- 威胁描述:通过
../、//、/.等路径写法绕过沙箱限制。 - 防护措施:
- 路径归一化:折叠冗余分隔符与相对路径段。
- realpath 校验:解析真实路径后再校验。
- 硬阻止目录:
.ai-helper-agent下的关键文件绝对禁止访问。
威胁 2:命令注入攻击
- 威胁描述:通过
$( … )、反引号、环境变量等方式注入恶意命令。 - 防护措施:
- 命令替换提取:提取
$( … )与反引号内部子命令,单独送检。 - 引号内扫描:扫描引号内的替换表达式。
- 只读头命令豁免:降低误报,避免正常用法被拦截。
- 命令替换提取:提取
威胁 3:命令拼接攻击
- 威胁描述:通过
&&、;、|等控制符拼接敏感命令。 - 防护措施:
- 链式命令拆分:逐段检测,避免敏感命令藏在非开头位置。
- 包装器剥壳:剥离
sudo、doas等前缀,暴露真实命令。 - 解释器内联载荷:解析
-c/-e、/c /k、-Command/-EncodedCommand。
威胁 4:提权绕过
- 威胁描述:通过
sudo、doas、runas等提权包装器绕过黑名单。 - 防护措施:
- 包装器剥壳:剥离提权前缀,暴露真实命令。
- 黑名单优先级:绝对禁止命令不受 force 影响。
威胁 5:系统文件破坏
- 威胁描述:通过
rm -rf /、mkfs、dd of=/dev/等命令破坏系统文件。 - 防护措施:
- 黑名单覆盖:绝对禁止破坏性命令。
- 路径变形归一化:防止通过路径写法绕过。
章节来源
- agent/src/security.js#L457-L479
- agent/src/security.js#L521-L560
- agent/src/security.js#L653-L683
- agent/test/unit/command-security-check.unit.test.js#L350-L401
故障排查
本节提供常见故障的排查思路。
问题 1:路径访问被拒绝
- 可能原因:
- 路径不在 allowedPaths 中。
- 路径命中硬阻止目录。
- 路径包含符号链接,指向被禁止的位置。
- 排查步骤:
- 检查
allowedPaths配置。 - 检查路径是否属于
.ai-helper-agent目录。 - 使用
realpath检查真实路径。
- 检查
问题 2:命令被拒绝
- 可能原因:
- 命令命中黑名单。
- 命令命中灰名单且未确认。
- 命令引用了代理敏感文件。
- 排查步骤:
- 检查命令是否属于黑名单。
- 检查是否需要用户确认。
- 检查命令是否引用
.ai-helper-agent目录。
问题 3:命令执行超时
- 可能原因:
- 命令长时间无输出。
- 超时策略设置为严格模式。
- 排查步骤:
- 检查命令是否有输出。
- 调整
commandTimeout配置。 - 检查 executor.js 的超时策略。
章节来源
结论
命令安全分级系统通过路径沙箱与命令分级两大核心机制,构建了较为完整的安全防护体系。其优势在于:
- 路径沙箱:通过硬阻止目录、工作目录放行、允许路径校验、符号链接防护,有效防止文件越权访问。
- 命令分级:通过黑名单/灰名单、链式命令拆分、包装器剥壳、解释器内联载荷解析、命令替换检测,有效识别危险命令。
- 执行器安全:通过环境变量白名单、输出缓冲区限制、超时策略、进程树终止,保障执行环境安全。
然而,系统仍存在一定局限:
- 未实现细粒度角色权限控制:仅通过
force参数模拟用户确认。 - 未实现完整审计日志:仅通过错误日志与 WebSocket 输出间接获取。
- 未实现自定义命令注册表:需通过技能系统或 MCP 协议间接扩展。
未来改进方向包括:
- 引入 RBAC 角色权限模型。
- 完善审计日志记录与导出功能。
- 支持自定义命令注册与动态白名单扩展。
- 增强命令语义分析,减少误报与漏报。