












Agent 能写代码、能跑代码——那怎么防止它给自己开一道后门?
CodeCoder 可以自己写 Capability 并执行。关于 Tool、Skill、Capability 三分的完整框架和各自的安全边界,见篇 2「三分架构」。
这意味着它能给自己的代码库新增功能、启动常驻服务、甚至写一段自动化脚本来批量操作文件。这是"自我进化"的核心能力——但也正是这里藏着最大的安全隐患。
最极端的场景是:agent 写了一段 Capability,内容是给自己所有工具授予永久权限,然后执行它。如果这个操作成功,以后 agent 干任何事情都不再需要你批准。
这个问题不是假设——任何允许 agent 自修改的系统都必须回答一个根本问题:大门开在哪里? 如果 agent 能写新代码、能跑新代码——怎么在这两个动作之间设置一道不可绕过的安全闸门?
这是自修改安全架构的核心洞察,一句话概括所有设计。
CodeCoder 有三个 generate_* 工具:generate_skill、generate_prompt、generate_capability。它们做的事本质上都是"写一个文件到指定目录"。generate_skill 写一个 .md 文件到 skills/,generate_capability 写代码 + manifest 到 capabilities/。
这些操作的权限级别是什么?write_file 级别。 跟 agent 写一个普通的 README 文件没有任何区别。不需要额外授权,不需要用户确认。
那真正的安全闸门在哪里?在执行侧。run_capability 触发的权限检查才是真正的闸门。执行一个 Capability 时,系统检查三件事:
换句话说:写是廉价的、可逆的、不需要批准的;执行是昂贵的、需要显式授权的。 这个不变量贯穿 Tool、Skill、Capability 三层。
| 操作 | 权限级别 | 需要授权? |
|---|---|---|
| 写一个 Skill 文件 | write_file | 不需要 |
| 写一个 Capability 代码 + manifest | write_file | 不需要 |
| 执行一个 Capability(Shell 环境) | run_capability:@shell | 需要 |
| 执行一个 Capability(Wasm 环境) | run_capability:@wasm | 需要 |
| 执行一个 Capability(Docker 环境) | run_capability:@docker | 需要 |
有人会问:那 agent 如果写一个 Capability,manifest 里声明 Docker 环境,但实际代码里直接调用系统调用呢?答案在第二道闸门上。
Capability 的执行环境分成三级,信任成本依次递增:
不同的环境有不同的信任天花板。CodeCoder 的规定是:@shell 环境的 Capability,最高信任级别是 AlwaysThisSession(本会话内不再询问),永远不能升到 AlwaysThisProject(永久信任,跨 session 不询问)。
为什么?想想 AlwaysThisProject 意味着什么:你把一个 Shell 环境 Capability 加入项目 allowlist,以后每次它执行,系统都不会再问你。如果这个 Capability 有任何安全问题,你等于永久授权了一个潜在的漏洞。Shell 环境的破坏力太大了——它能改任意文件、能联网、能格式化磁盘——让它在项目级别自动授权等于取消了所有安全保护。
Wasm 和 Docker 环境的 Capability 可以升到更高信任级别。因为 Wasm 的沙箱是编译时隔离的,Docker 的容器有文件系统隔离——即使 Capability 里面有恶意代码,它能造成的破坏也被限制在沙箱内。这才是可以授予永久信任的前提。
有了环境分级和天花板规则,还有一个陷阱:如果指定的环境不可用,怎么办?
假设 agent 写了一个 Capability,manifest 声明 Environment=Docker。但你的服务器上没有装 Docker。系统该怎么做?
选项 A:报错,告诉 agent "Docker 不可用,执行失败" 选项 B:偷偷落到 Shell 环境运行
CodeCoder 选 A。不允许静默降级。 选 B 意味着:你批准了"在 Docker 里跑这个脚本",实际上它是在宿主 Shell 里执行的。你的安全决策被系统悄悄覆盖了。这不是小问题——这意味着天花板规则也一起被绕过了:Shell 环境不该升到项目级别信任,但如果你以为它是在 Docker 里跑的……
这条规则同样适用于 Wasm。如果 Wasm 运行时不可用,报错。不降级。不静默。
来看一个完整的信任链路是什么样的:
generate_capability 写代码 + manifest。这一步只需要 write_file 权限,不收额外闸门run_capability 触发权限检查。如果 key 不在项目的预授权列表中,弹窗确认AlwaysThisSession,会话内不再询问。如果选了 Once,每次都问。如果选的是 Docker 环境,可以升到 AlwaysThisProject(@shell 环境最高只能到 AlwaysThisSession,见篇 6「有用户 vs. 无用户」中关于无用户模式下权限模型的讨论)注意最后一步:文件变更自动撤销信任。 这防止了一个隐蔽的绕过方式:先写一个无害的 Capability 拿到永久信任,再把代码改成有害的。因为 manifest mtime 变了,信任重置。
安全闸门不是免费的。每增加一道闸门,agent 的自主性就被削减一分。自修改系统的核心矛盾正在于此:你希望 agent 能自我进化,但每次进化都意味着你少控制了一步。 CodeCoder 的设计选择偏向收紧闸门——这对安全是有利的,但对效率是有成本的。
第一个代价是执行延迟。run_capability 需要检查 manifest 完整性、环境可用性、权限 key 匹配、信任级别验证——每一步都是毫秒级的开销。对于 OneShot 脚本(跑一次就结束),这些延迟可以忽略。但对于高频调用的 OnDemand Capability,反复的权限检查可能累积成明显的延迟。
第二个代价是用户疲劳。当 agent 生成新 Capability 并首次执行时,系统弹窗询问用户。如果用户频繁创建新 Capability,"首次执行弹窗"的高频出现会导致用户不再仔细阅读弹窗内容,直接点"同意"——这反而削弱了安全闸门的效果。这不是 CodeCoder 独有的问题,这是任何权限系统在真实使用中的共性困境。
第三个代价是信任模型的复杂度。四级权限(None → Ask → SessionAllowlist → ProjectAllowlist)加上环境级别的天花板规则,构成了一个足够表达复杂策略但也足够让用户困惑的体系。非技术用户可能不理解"为什么 Shell 环境不能永久信任而 Docker 环境可以",从而在配置阶段做出不安全的决定。
承认这些代价不是否定安全闸门的必要性——而是承认安全与效率之间的平衡永远是一个需要持续调整的活参数,而不是一次设计就解决的静态问题。
下一篇,我们把"做计划"和"做记录"分开来看——agent 为什么需要两个独立的持久化结构来管理同一个任务。
自修改系统的信任设计可以抽象为三条原则,适用于任何场景(不仅是 agent,也包括微服务热加载、插件系统、脚本引擎):
这三条不是 CodeCoder 独有的——它们是任何允许自修改的系统的安全底线。下一次你设计一个"用户可以上传脚本并运行"的功能时,可以用这三个问题做安全检查清单。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。