blade-code 技术深度系列第 2 篇。本文基于源码剖析 AI Agent 的权限设计------5 种权限模式、allow/ask/deny 三级控制、基于签名的精确匹配。
问题
把 AI Agent 接入开发环境,第一个问题不是"它能做什么",而是"它不能做什么"。
场景:
- Agent 想执行
rm -rf / - Agent 想读取
.env里的密钥 - Agent 想
curl下载脚本并执行 - Agent 想
sudo提权
你会让它直接跑吗?
blade-code 从设计之初就在解决这个问题:赋予 Agent 能力的同时,保证安全。
本文内容:
- 工具分类(ReadOnly / Write / Execute)
- 5 种权限模式
- allow/ask/deny 三级控制
- 基于签名的精确匹配
一、工具分类:三种 ToolKind
blade-code 把所有工具分成三类,这是权限控制的基础:
typescript
export enum ToolKind {
ReadOnly = 'readonly', // 只读,无副作用
Write = 'write', // 文件写入
Execute = 'execute', // 命令执行,可能有副作用
}
ReadOnly 工具
只读操作,无副作用,最安全:
| 工具 | 功能 |
|---|---|
| Read | 读取文件 |
| Glob | 路径匹配 |
| Grep | 文本搜索 |
| WebFetch | 获取网页 |
| WebSearch | 网络搜索 |
| TaskOutput | 子任务输出 |
| Plan | 生成计划 |
Write 工具
文件写入,有副作用但可控:
| 工具 | 功能 |
|---|---|
| Edit | 编辑文件 |
| Write | 写入文件 |
| NotebookEdit | 编辑 Notebook |
Execute 工具
命令执行,副作用不可预测:
| 工具 | 功能 |
|---|---|
| Bash | Shell 命令 |
| Task | 子任务 |
| Skill | 调用技能 |
| SlashCommand | 斜杠命令 |
二、5 种权限模式
typescript
export enum PermissionMode {
DEFAULT = 'default',
AUTO_EDIT = 'autoEdit',
YOLO = 'yolo',
PLAN = 'plan',
SPEC = 'spec',
}
DEFAULT(默认)
平衡安全与效率:
- ✅ 自动批准:ReadOnly 工具
- ❌ 需要确认:Write 工具
- ❌ 需要确认:Execute 工具
bash
blade "帮我分析这个项目"
AUTO_EDIT
频繁编码场景:
- ✅ 自动批准:ReadOnly 工具
- ✅ 自动批准:Write 工具
- ❌ 需要确认:Execute 工具
bash
blade --mode=autoEdit "重构这个模块"
日常开发中,文件编辑最频繁。AUTO_EDIT 让 Agent 自由改代码,但执行命令仍需确认。
YOLO(危险)
完全信任 AI:
- ✅ 自动批准:所有工具
- ⚠️ 跳过所有确认
bash
blade --mode=yolo "自动修复所有 lint 错误"
适用场景:沙箱环境、演示、已验证安全的自动化脚本。
源码实现:
typescript
if (permissionMode === PermissionMode.YOLO) {
return {
result: PermissionResult.ALLOW,
matchedRule: 'mode:yolo',
reason: 'YOLO mode: automatically approve all tool invocations',
};
}
PLAN
只读模式,用于调研:
- ✅ 自动批准:ReadOnly 工具
- ❌ 拦截:Write 和 Execute 工具
- 🔵 特殊工具:ExitPlanMode(提交方案)
bash
blade --mode=plan "分析这个项目的架构"
适用场景:代码审查、架构分析、生成方案后用户批准再执行。
源码实现:
typescript
if (permissionMode === PermissionMode.PLAN) {
if (!isReadOnlyKind(toolKind)) {
return {
result: PermissionResult.DENY,
matchedRule: 'mode:plan',
reason: 'Plan mode: modification tools are blocked',
};
}
}
SPEC(Spec-Driven Development)
结构化功能开发:
- ✅ 自动批准:ReadOnly + Spec 专用工具
- ❌ 需要确认:其他 Write 和 Execute 工具
- 🔵 特殊工具:InitSpec, UpdateSpec, ValidateSpec, GetSpecContext, ExitSpecMode
- 📁 持久化:
.blade/specs/<feature>/
bash
blade --mode=spec "实现用户认证功能"
适用场景:复杂功能开发,需要 Requirements → Design → Tasks → Implementation 工作流。
模式对比
| 模式 | ReadOnly | Write | Execute | 场景 |
|---|---|---|---|---|
| DEFAULT | ✅ 自动 | ❌ 确认 | ❌ 确认 | 日常开发 |
| AUTO_EDIT | ✅ 自动 | ✅ 自动 | ❌ 确认 | 频繁编码 |
| YOLO | ✅ 自动 | ✅ 自动 | ✅ 自动 | 沙箱/演示 |
| PLAN | ✅ 自动 | ❌ 拦截 | ❌ 拦截 | 调研/审查 |
| SPEC | ✅ 自动 | ❌ 确认 | ❌ 确认 | 复杂功能 |
三、三级权限控制:allow / ask / deny
权限模式之外,blade-code 还有更细粒度的控制:
typescript
export interface PermissionConfig {
allow: string[]; // 自动批准
ask: string[]; // 需要确认
deny: string[]; // 直接拒绝
}
优先级
deny > allow > ask > 默认(ask)
typescript
// 1. 检查 deny(最高优先级)
const denyMatch = this.matchRules(signature, this.config.deny);
if (denyMatch) {
return { result: PermissionResult.DENY, ... };
}
// 2. 检查 allow
const allowMatch = this.matchRules(signature, this.config.allow);
if (allowMatch) {
return { result: PermissionResult.ALLOW, ... };
}
// 3. 检查 ask
const askMatch = this.matchRules(signature, this.config.ask);
if (askMatch) {
return { result: PermissionResult.ASK, ... };
}
// 4. 默认:需要确认
return { result: PermissionResult.ASK, ... };
默认配置
blade-code 内置了一套安全配置:
allow 列表(自动批准):
typescript
allow: [
// 系统信息命令
'Bash(pwd)', 'Bash(which *)', 'Bash(whoami)',
'Bash(hostname)', 'Bash(uname *)', 'Bash(date)', 'Bash(echo *)',
// 目录列表
'Bash(ls *)', 'Bash(tree *)',
// Git 只读
'Bash(git status)', 'Bash(git log *)', 'Bash(git diff *)',
'Bash(git branch *)', 'Bash(git show *)', 'Bash(git remote *)',
// 包管理器只读
'Bash(npm list *)', 'Bash(npm view *)', 'Bash(npm outdated *)',
'Bash(pnpm list *)', 'Bash(yarn list *)',
'Bash(pip list *)', 'Bash(pip show *)',
]
ask 列表(需要确认):
typescript
ask: [
// 网络下载(可能下载恶意代码)
'Bash(curl *)', 'Bash(wget *)', 'Bash(aria2c *)', 'Bash(axel *)',
// 危险删除
'Bash(rm -rf *)', 'Bash(rm -r *)', 'Bash(rm --recursive *)',
// 网络连接
'Bash(nc *)', 'Bash(netcat *)', 'Bash(telnet *)', 'Bash(ncat *)',
]
deny 列表(直接拒绝):
typescript
deny: [
// 敏感文件
'Read(./.env)', 'Read(./.env.*)',
// 危险命令
'Bash(rm -rf /)', 'Bash(rm -rf /*)', 'Bash(sudo *)', 'Bash(chmod 777 *)',
// Shell 嵌套(可绕过安全检测)
'Bash(bash *)', 'Bash(sh *)', 'Bash(zsh *)', 'Bash(fish *)', 'Bash(dash *)',
// 代码注入
'Bash(eval *)', 'Bash(source *)',
// 系统级操作
'Bash(mkfs *)', 'Bash(fdisk *)', 'Bash(dd *)', 'Bash(format *)', 'Bash(parted *)',
// 浏览器(可打开恶意链接)
'Bash(open http*)', 'Bash(open https*)',
'Bash(xdg-open http*)', 'Bash(xdg-open https*)',
]
设计原则
allow :只读命令无副作用,可以自动批准。pwd、ls、git status 不会改变任何东西。
ask :curl、wget 可能下载恶意代码,rm -rf 可能删数据。需要确认,但不完全禁止。
deny :.env 包含密钥,sudo 风险太高,Shell 嵌套可能绕过检测,mkfs、dd 可能造成不可逆损害。
四、基于签名的精确匹配
blade-code 的权限系统支持多种匹配模式。
签名格式
scss
ToolName(content)
例如:
Bash(git status)--- 执行 git statusRead(src/index.ts)--- 读取文件Edit(src/utils.ts)--- 编辑文件
匹配模式
- 精确匹配 :
Read(src/index.ts) - 前缀匹配 :
Read(匹配所有 Read 调用) - 通配符匹配 :
Read(*)或Bash(git *) - Glob 模式 :
Read(**/*.env)
| 规则 | 匹配 | 不匹配 |
|---|---|---|
Bash(git status) |
Bash(git status) |
Bash(git log) |
Bash(git *) |
Bash(git status), Bash(git log) |
Bash(npm install) |
Bash |
所有 Bash 命令 | Read(...) |
Read(*.env) |
Read(.env), Read(.env.local) |
Read(config.json) |
Read(**/*.ts) |
Read(src/index.ts) |
Read(package.json) |
实现
blade-code 用 picomatch 库做 glob 匹配:
typescript
private matchRule(signature: string, rule: string): MatchType | null {
// 精确匹配
if (signature === rule) return 'exact';
// 通配符匹配所有
if (rule === '*' || rule === '**') return 'wildcard';
// 工具名 glob 匹配
if (ruleToolName.includes('*')) {
if (!picomatch.isMatch(sigToolName, ruleToolName, { dot: true, bash: true })) {
return null;
}
}
// 参数 glob 匹配
if (rule.includes('*')) {
const isMatch = picomatch.isMatch(sigValue, ruleValue, { dot: true, bash: true });
if (isMatch) return ruleValue.includes('**') ? 'glob' : 'wildcard';
}
return null;
}
五、权限执行管道
blade-code 的权限检查在 PipelineStages 中实现。
优先级
YOLO 模式 > PLAN 模式 > DENY 规则 > ALLOW 规则 > 模式规则 > ASK
typescript
private applyModeOverrides(
toolKind: ToolKind,
checkResult: PermissionCheckResult,
permissionMode: PermissionMode
): PermissionCheckResult {
// 1. YOLO:全部放开
if (permissionMode === PermissionMode.YOLO) {
return { result: PermissionResult.ALLOW, ... };
}
// 2. PLAN:拒绝非只读
if (permissionMode === PermissionMode.PLAN) {
if (!isReadOnlyKind(toolKind)) {
return { result: PermissionResult.DENY, ... };
}
}
// 3. deny 规则已拒绝,不覆盖
if (checkResult.result === PermissionResult.DENY) return checkResult;
// 4. allow 规则已批准,不覆盖
if (checkResult.result === PermissionResult.ALLOW) return checkResult;
// 5. 只读工具:自动批准
if (isReadOnlyKind(toolKind)) {
return { result: PermissionResult.ALLOW, ... };
}
// 6. AUTO_EDIT + Write:自动批准
if (permissionMode === PermissionMode.AUTO_EDIT && toolKind === ToolKind.Write) {
return { result: PermissionResult.ALLOW, ... };
}
// 7. 其他:保持原结果(通常是 ASK)
return checkResult;
}
流程图
六、实战配置
项目级配置
在项目根目录创建 .blade/settings.json:
json
{
"permissionMode": "default",
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(pnpm *)",
"Bash(git commit *)",
"Bash(git push *)"
],
"ask": [],
"deny": [
"Read(config/secrets.json)",
"Bash(rm -rf node_modules)"
]
}
}
命令行切换
bash
# 启动时指定
blade --mode=autoEdit "重构这个模块"
blade --mode=plan "分析项目架构"
blade --mode=yolo "自动修复所有问题"
# 运行时切换
> /mode autoEdit
> /mode plan
> /mode default
场景推荐
| 场景 | 模式 | 原因 |
|---|---|---|
| 日常开发 | DEFAULT | 平衡安全与效率 |
| 频繁编码 | AUTO_EDIT | 减少文件编辑确认 |
| 代码审查 | PLAN | 只读不写 |
| 自动化脚本 | YOLO | 无需人工干预(确保安全) |
| 复杂功能 | SPEC | 结构化工作流 |
总结
- 工具分类:ReadOnly / Write / Execute
- 5 种权限模式:DEFAULT / AUTO_EDIT / YOLO / PLAN / SPEC
- 三级权限控制:deny > allow > ask
- 精确匹配:精确、前缀、通配符、glob
设计原则:
- 默认安全(DEFAULT 模式)
- 灵活可控(用户可切换)
- 细粒度(精确到命令级别)
- 可扩展(项目级配置覆盖全局)