12. 安全边界与权限模型
Coding Agent 能读写文件、运行命令、访问网络和调用模型。只要它运行在你的机器上,它就拥有进程权限范围内的能力。安全设计的第一步是诚实:不要把 prompt 当权限系统,不要把“模型应该不会这么做”当隔离边界。
威胁模型
至少考虑四类风险:
- 用户误操作:让 Agent 删除文件、覆盖改动、运行昂贵命令。
- 模型误判:模型把测试命令写成破坏性命令。
- Prompt 注入:仓库文本诱导模型泄露环境变量或绕过规则。
- 工具漏洞:路径逃逸、命令注入、并行写覆盖、日志泄露密钥。
不同风险需要不同边界。Prompt 可以降低误判概率,但不能阻止恶意工具调用。真正的边界在工具执行前。
权限门
工具调用前可以经过权限门:
type PermissionDecision =
| { type: "allow" }
| { type: "deny"; reason: string }
| { type: "confirm"; prompt: string };
read 通常允许,edit 可能根据文件状态允许,bash 根据命令分类确认或拒绝。权限门要在 tool call 进入执行器前运行。如果拒绝,应形成错误 tool result,让模型知道原因。
不要只在 UI 里拦截。SDK 和 JSON 模式也必须走同一权限门。否则用户换一个运行壳就绕过安全策略。
项目信任
第一次进入陌生仓库时,Agent 可以询问用户是否信任该项目。未信任状态下,允许只读工具,限制 bash、写文件和读取敏感路径。信任不是永久真理,应允许用户撤销。
信任策略要具体:
- 当前工作区是否可信。
- 是否允许执行项目脚本。
- 是否允许网络访问。
- 是否允许读取环境变量。
- 是否允许写工作区外路径。
把这些都压成一个“允许/拒绝”会让用户无法做细粒度判断。
沙箱与外部边界
如果你需要更强安全性,应该把工具执行放进容器、虚拟机或远程沙箱。这样即使模型请求危险命令,损害也被限制在沙箱内。沙箱不是本书必须实现的第一版,但接口要提前留好:工具执行不应该直接绑死本机文件系统和 shell。
可以把文件操作抽象成 operations:
type FileOperations = {
readText(path: string): Promise<string>;
writeText(path: string, content: string): Promise<void>;
realpath(path: string): Promise<string>;
};
本地、SSH、容器和远程运行时都可以实现同一接口。Agent 内核不需要知道工具在哪里执行。
日志也有安全边界
会话日志会保存用户输入、工具结果、文件片段和命令输出。它可能包含密钥、私有代码和错误堆栈。至少要考虑:
- 日志存储位置。
- 是否加密。
- 导出前是否脱敏。
- 上传远端前是否询问。
- UI 是否默认折叠敏感输出。
安全不是只拦命令。一个 Agent 可以不执行危险命令,却把 .env 内容写进日志或模型上下文,这同样是泄露。
练习
实现权限门和项目信任状态。
验收标准:
- 未信任项目下,写工具和 bash 默认需要确认或拒绝。
- 权限拒绝会形成
isError: truetool result。 - UI、CLI JSON 模式和 SDK 都调用同一权限门。
- 读取疑似敏感文件时有策略:拒绝、确认或脱敏。
- 工具操作通过接口抽象,未来可替换为容器或远程执行。