跳转到主要内容

编码智能体的泄露途径是代码仓库,而非提示词

13,000 张内部图片经由编码智能体暴露,本质上是仓库卫生与智能体权限范围的失败:智能体会继承整个检出内容的历史与密钥面,因此隔离的单位是仓库边界,而非指令。

Detailed view of automated machinery with warning signals in an industrial setting.

仓库就是暴露面

当编码智能体接到一个任务时,通常会被授予对某个工作树的访问权限。这个工作树并不是一个中立的沙箱。它包含完整的提交历史、所有分支、所有标签,以及任何恰好存在的未跟踪或被忽略的产物。智能体会读取其中存在的内容。如果该仓库曾经包含过图片、凭据、数据库转储或二进制大对象,智能体就能读取它、复制它、转换它,并把它写回去。近期关于 13,000 张内部图片经由编码智能体暴露的报道,并不是一个关于巧妙提示词的故事。它是一个关于当智能体继承一个没有隔离边界的仓库时会发生什么的故事。

指令层是寻找安全性的错误位置。提示词只是建议性的。它们由一个为完成任务而优化的模型来解释,而不是为你的数据分类策略而优化。一条写着“不要读取 secrets 目录”的提示词是一种建议,而不是访问控制。仓库边界才是访问控制。它由文件系统、远程仓库以及智能体持有的凭据来强制执行。工程应当投入的地方就在这里。

为什么编码智能体属于不同的暴露类别

大多数 LLM 智能体部署与 API 交互。编码智能体与版本控制系统交互。这一差异从三个方面改变了失效模式。

第一,智能体在一个工作树内运行,对历史、分支和未跟踪产物拥有完整读取权限。一次检出并不是当前状态的快照;它是所有曾经被提交过的内容的完整记录。如果一个密钥曾被提交然后被移除,它仍然存在于对象存储中。如果一张图片是在某个功能分支上添加的,它仍然可达。智能体不需要被指示去寻找它。标准工具——git log、git grep、git cat-file——会在常规探索过程中把它暴露出来。

第二,编码智能体经常运行会生成并提交二进制产物的命令。构建输出、模型权重、截图、测试夹具、生成的报告。这些内容无法干净地做差异比较。二进制文件的文本差异要么是一行“Binary files differ”,要么是一大堆不可读的字节。审查者会跳过它们。该产物进入历史,而一旦它进入历史,它就会出现在之后的每一次克隆中。

第三,输出通道是一个带有远程仓库的版本控制系统。每一个错误都可能是公开且永久的。推送到公共远程仓库不是一次文件传输;它是一次发布事件。事后重写历史是可能的,但具有破坏性,而且它无法取消发布任何已经被拉取的内容。这种不可逆性是结构性的,而非流程性的。

这三种属性组合成一个特定的暴露类别:一个能够读取全部历史、写入不透明产物并发布到远程仓库的智能体。这与一个只能调用受限 API 的智能体有着不同的风险画像。

Aerial photo of an industrial area with warehouses in Wilson, NC. Bright daylight view.

由此得出的权限范围规则

隔离的单位是仓库边界。以下规则直接由此得出。

浅克隆并限定范围。 只需要一个包的智能体不应获得 monorepo 的历史。使用浅克隆(--depth 1)和稀疏检出(--filter=blob:none、--sparse),使工作树只包含任务所需的路径。这不是性能优化;这是对读取面的缩减。如果智能体看不到该对象,它就无法泄露该对象。

将智能体的凭据与人的凭据分离。 智能体不应持有能够读取该人可读取的每一个仓库的个人访问令牌。它应持有仅限定于特定仓库的凭据,并且该凭据应只拥有对暂存远程仓库的推送权限。暂存远程仓库是智能体撰写的提交落地的地方。晋升到规范远程仓库是一个单独的、由人把关的步骤。

将仓库视为能力面。 在决定智能体可以写什么之前,先枚举它可以读什么。这与基于能力的安全是同一套纪律:可读对象的集合定义了可外泄对象的集合。如果枚举中包含历史、分支和未跟踪文件,那么智能体的写入范围就应当相应地收窄。

诚实的权衡:浅层、限定范围的克隆会破坏某些智能体工作流。需要二分定位回归、跨历史追踪变更或对长期存活分支进行推理的智能体将会失败或产生更差的结果。缓解办法是为历史访问提供一条单独的、只读的、经过审计的路径——而不是放宽默认克隆。默认应当是窄面;例外应当是显式且有日志记录的。

不依赖智能体守规矩的检测

检测不能依赖智能体遵守指令。它必须作用于智能体产生的产物。

在提交前扫描暂存对象,而不是在推送后。 一个检查暂存内容中高熵字符串和已知图片文件头的 pre-commit 钩子,能在泄露进入历史之前捕获这一类问题。高熵扫描能捕获凭据和密钥。文件头扫描能捕获 PNG、JPEG、GIF 和 WebP 的魔数字节。两者都廉价且确定。关键在于对暂存对象运行它们,而不是对工作树运行,因为暂存对象才是将被提交的内容。

Detailed view of a drone controller joystick, showcasing precision technology.

对产物清单做差异比较,而不仅仅是对源代码差异。 每一个由智能体撰写的提交都应生成一份新增、修改和删除文件的清单,包含文件类型和大小。审查者应将该清单与源代码差异一并查看。一个向仅含源代码的仓库添加 2 M

对远程仓库的创建发出告警。 不可逆的步骤是推送到远程仓库,而最不可逆的变体是创建一个新的远程仓库。对它发出告警。将远程仓库创建视为一项需要显式批准的特权操作,与普通提交分开处理。

这对智能体工具设计意味着什么

这种失效模式不会消失,因为它是赋予智能体真实仓库的后果。工具应当让安全路径成为默认路径。

提交工具应暴露一份声明的产物清单,而不是一个 shell 逃逸口。 与其给智能体一个 shell 并指望它对正确的文件运行 git add,工具应要求智能体声明它打算提交什么:一份路径列表、它们的类型以及它们的来源。然后工具精确地提交该集合。这消除了因宽泛的 git add . 调用以及将产物丢进工作树的构建步骤而导致的意外提交这一类问题。

仓库凭据应是短期的并绑定到分支。 一个在几分钟内过期且只能推送到一个分支的凭据,限制了被攻陷智能体的爆炸半径。这与支付中限定范围的消费凭据的工作方式类似:凭据携带范围,而不是调用者。智能体不需要被信任;凭据本身受到约束。

审查工具需要一个标注智能体署名的作者字段。 由智能体撰写的提交应在元数据层面与由人撰写的提交可区分。审查者随后可以施加不同的审查力度:由人撰写的提交可能获得常规审查,而由智能体撰写的提交则理所当然地获得产物清单检查和密钥扫描。这种署名不是为了追责;它是为了路由到正确的审查流程。

边界,而非指令

内部图片经由编码智能体暴露这一事件提醒我们,智能体系统真正有趣的安全边界不是提示词。而是智能体能够触及的资源集合。对于编码智能体而言,该集合由仓库定义:它的历史、它的分支、它的未跟踪产物以及它的远程仓库。隔离意味着有意地收窄该集合,在产物上而非行为上检测违规,并设计出让窄路径成为易行路径的工具。指令是系统中最不可强制执行的部分。仓库边界才是真正起作用的部分。

封面照片:panumas nikhomkhai / Pexels。

分享XLinkedIn永久链接6 分钟阅读

这能回答的问题

Why is a prompt not a sufficient control for coding agents?

Prompts are advisory. They are interpreted by a model optimising for task completion, not for data-classification policy. The repository boundary — enforced by the filesystem, the remote, and the credential — is an actual access control.

What makes binary artifacts a special risk in agent-authored commits?

Binaries do not diff cleanly, so reviewers skip them. A build output, screenshot, or model weight can enter history unreviewed, and once it is in history it is in every subsequent clone.

How should agent credentials be scoped?

Short-lived, bound to a single repository and branch, with push rights to a staging remote only. Promotion to the canonical remote should be a separate, human-gated step.

What is the trade-off of shallow, scoped clones?

Agents that need to bisect regressions or trace changes across history will fail or produce worse results. The mitigation is a separate, read-only, audited path for history access, not a wider default clone.