灵魂之桥前传:追忆
83.49M · 2026-02-09
把 AI Agent 接入开发环境,第一个问题不是"它能做什么",而是"它不能做什么"。
场景:
rm -rf /.env 里的密钥curl 下载脚本并执行sudo 提权你会让它直接跑吗?
blade-code 从设计之初就在解决这个问题:赋予 Agent 能力的同时,保证安全。
本文内容:
blade-code 把所有工具分成三类,这是权限控制的基础:
export enum ToolKind {
ReadOnly = 'readonly', // 只读,无副作用
Write = 'write', // 文件写入
Execute = 'execute', // 命令执行,可能有副作用
}
只读操作,无副作用,最安全:
| 工具 | 功能 |
|---|---|
| Read | 读取文件 |
| Glob | 路径匹配 |
| Grep | 文本搜索 |
| WebFetch | 获取网页 |
| WebSearch | 网络搜索 |
| TaskOutput | 子任务输出 |
| Plan | 生成计划 |
文件写入,有副作用但可控:
| 工具 | 功能 |
|---|---|
| Edit | 编辑文件 |
| Write | 写入文件 |
| NotebookEdit | 编辑 Notebook |
命令执行,副作用不可预测:
| 工具 | 功能 |
|---|---|
| Bash | Shell 命令 |
| Task | 子任务 |
| Skill | 调用技能 |
| SlashCommand | 斜杠命令 |
export enum PermissionMode {
DEFAULT = 'default',
AUTO_EDIT = 'autoEdit',
YOLO = 'yolo',
PLAN = 'plan',
SPEC = 'spec',
}
平衡安全与效率:
blade "帮我分析这个项目"
频繁编码场景:
blade --mode=autoEdit "重构这个模块"
日常开发中,文件编辑最频繁。AUTO_EDIT 让 Agent 自由改代码,但执行命令仍需确认。
完全信任 AI:
blade --mode=yolo "自动修复所有 lint 错误"
适用场景:沙箱环境、演示、已验证安全的自动化脚本。
源码实现:
if (permissionMode === PermissionMode.YOLO) {
return {
result: PermissionResult.ALLOW,
matchedRule: 'mode:yolo',
reason: 'YOLO mode: automatically approve all tool invocations',
};
}
只读模式,用于调研:
blade --mode=plan "分析这个项目的架构"
适用场景:代码审查、架构分析、生成方案后用户批准再执行。
源码实现:
if (permissionMode === PermissionMode.PLAN) {
if (!isReadOnlyKind(toolKind)) {
return {
result: PermissionResult.DENY,
matchedRule: 'mode:plan',
reason: 'Plan mode: modification tools are blocked',
};
}
}
结构化功能开发:
.blade/specs/<feature>/blade --mode=spec "实现用户认证功能"
适用场景:复杂功能开发,需要 Requirements → Design → Tasks → Implementation 工作流。
| 模式 | ReadOnly | Write | Execute | 场景 |
|---|---|---|---|---|
| DEFAULT | 自动 | 确认 | 确认 | 日常开发 |
| AUTO_EDIT | 自动 | 自动 | 确认 | 频繁编码 |
| YOLO | 自动 | 自动 | 自动 | 沙箱/演示 |
| PLAN | 自动 | 拦截 | 拦截 | 调研/审查 |
| SPEC | 自动 | 确认 | 确认 | 复杂功能 |
权限模式之外,blade-code 还有更细粒度的控制:
export interface PermissionConfig {
allow: string[]; // 自动批准
ask: string[]; // 需要确认
deny: string[]; // 直接拒绝
}
deny > allow > ask > 默认(ask)
// 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 列表(自动批准):
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 列表(需要确认):
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 列表(直接拒绝):
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 的权限系统支持多种匹配模式。
ToolName(content)
例如:
Bash(git status) — 执行 git statusRead(src/index.ts) — 读取文件Edit(src/utils.ts) — 编辑文件Read(src/index.ts)Read(匹配所有 Read 调用)Read(*) 或 Bash(git *)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 匹配:
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
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;
}
flowchart TD
A[工具调用请求] --> B{YOLO 模式?}
B -->|是| C[ 直接批准]
B -->|否| D{PLAN 模式?}
D -->|是| E{只读工具?}
E -->|否| F[ 直接拒绝]
E -->|是| G[ 批准]
D -->|否| H{匹配 deny 规则?}
H -->|是| F
H -->|否| I{匹配 allow 规则?}
I -->|是| C
I -->|否| J{只读工具?}
J -->|是| C
J -->|否| K{AUTO_EDIT + Write?}
K -->|是| C
K -->|否| L[️ 请求用户确认]
在项目根目录创建 .blade/settings.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)"
]
}
}
# 启动时指定
blade --mode=autoEdit "重构这个模块"
blade --mode=plan "分析项目架构"
blade --mode=yolo "自动修复所有问题"
# 运行时切换
> /mode autoEdit
> /mode plan
> /mode default
| 场景 | 模式 | 原因 |
|---|---|---|
| 日常开发 | DEFAULT | 平衡安全与效率 |
| 频繁编码 | AUTO_EDIT | 减少文件编辑确认 |
| 代码审查 | PLAN | 只读不写 |
| 自动化脚本 | YOLO | 无需人工干预(确保安全) |
| 复杂功能 | SPEC | 结构化工作流 |
设计原则: