返回所有文章
发布于 · 作者 Renaud Deraison

为什么 Bromure Agentic Coding 不是一个沙箱

沙箱要求开发者做一笔交换,拿走那份让编码智能体值得一跑的速度——预先批准每一个依赖,维护一张域名白名单,绝不碰一个组织没审过的包。于是开发者把它关掉。Bromure Agentic Coding 拒绝这笔交换。它不去约束智能体做什么;它只在 hypervisor 处划下一条硬线,让你在里面想做什么都行。这是一篇奠基性的论述,讲清楚为什么一条边界胜过一个沙箱,以及这条边界让哪三项保证成真:没有凭证可偷、宽泛的令牌在线路上被收窄、供应链攻击在 tarball 落地之前就被挡下。——再加上这条线如今让其成真的第四项:提示注入在智能体所读的内容里就被抓住,赶在模型服从它们之前。

沙箱开出一笔交换:交出一部分让编码智能体有用的东西,作为回报它会 保你平安。开发者每一次都拒绝这笔交换——他们把沙箱关掉,或者从来 不打开——而且他们做得对。智能体的活儿,是在杂乱、未经审查的地界里 快速穿行。Bromure Agentic Coding 不约束那件事。它只划下一条硬线, 在 hypervisor 处,并让你在它里面想做什么都行。

一篇奠基性的文章——写一次,供后面的文章去指向。不是关于某一起事件。 而是关于我们为什么把 Bromure Agentic Coding 造成现在这样,以及为什么 「它是个沙箱」是我们一再纠正的那个说法。

开发者不是系统管理员。

把一个编码智能体放到你机器上的理由是速度——那种很具体的速度:在晚上 十一点,拉一个公司里谁都没审过的包,看看一个想法成不成立。试一下这个 库,跑一跑它的示例,不合适就扔掉。这不是一个要从人身上管教掉的毛病。 它就是那套工作流,也正是智能体值得拥有的全部理由。

所以任何让你预先批准每一个依赖、或者让某人维护一张智能体可触达域名 清单的模型,都在跟它所保护的东西作对。它拖慢了它被部署来促成的那唯一 一项活动——而一个挡了发布之路的控制,只有一个下场:它被关掉。一个 开发者为了把活儿干完而关掉的控制,从来就不是控制。

「跑一个私有的、审过的镜像」也以同样的方式没抓住要点:那些对试验真正 要紧的包,正是还没有人审过的那些。一个由已批准依赖组成的镜像,是一面 照着昨天那些想法的镜子。

所以问题不是怎样阻止开发者去碰未经审查的代码。他们必须碰;那就是这份 活儿。问题是怎样让他们自由地去碰它的全部,而不必把他们的钥匙串搭进去 当作交换的一部分。

为生产而造的沙箱,不合工作台的尺寸。

第一反应是网络沙箱——那些加固过的容器、NVIDIA 的、那些到处流传的 Docker-Compose 配方。它们的形状总是一样:枚举出智能体可触达的主机, 拒绝其余的。当智能体有一份已知、狭窄的活儿时,它运转得漂亮极了。 一个跟三个内部服务和一个模型端点对话的生产智能体,会永远活在一张白 名单后面,因为那张清单很短,而且不再变化。

构思没有短清单,而且它从不停止变化。没人想去维护一张白名单,把一次 试验可能触达的每一个仓库、CDN、Git 托管站和一次性 API 都列上——它 永远做不完,每个下午都在变长,而它挡掉某个正当东西的那一天,就是开 发者为了给自己解封而把它停掉的那一天。白名单本身没错;它保护的是一个 范围已经已知的智能体。而工作台的全部价值,恰恰在于范围没有已知。

拿一个具体的下午来说。一个开发者有些 Node 配置文件,已经长到了几十 兆字节,而他们在用的那个 YAML 解析器被它们噎住了。有好几个候选者 ——js-yamlyamlyaml-js——而要知道哪一个能在不撑爆堆的情况下 扛住一个 40 MB 的文件,唯一的办法就是把三个都装上、把那个文件分别 丢给每一个。提一张工单把三个库弄进私有镜像以便能给它们做基准测试, 恰恰是本末倒置:开发者想要的是先测,再把胜出者提升进镜像,而不是反 过来。预先审查正是这场试验存在着要走过去的那道闸门。

底下还有一个更安静的问题。这些工具加固的是生产里的智能体——已部署、 已限定范围、有人监管的那个。但一次供应链入侵落在的是开发者的笔记本上, 试验进行到一半,连着真凭证就躺在 ~/.aws~/.npmrc 里,因为开发者 就把它们放在那儿。这个模型最强的地方恰恰是攻击不在的地方,而在攻击 所在之处它却缺席。

猫鼠游戏式的沙箱,第二回合就输了。

另一种本能是把智能体留在宿主上,而去关押那个进程——挡住 claude 进程去读 ~/.ssh~/.aws。它感觉密不透风,直到你想起智能体的活儿 是写代码,而代码就跑在同一台机器上。

智能体搭起一个项目的骨架。然后你——或者智能体,或者第二个工具——在它 刚写好的那个仓库里跑 npm install。那个 npm install 不是那个被关押 的进程。它的 postinstall 钩子会像你笔记本上任何别的程序一样去读 ~/.aws/credentials,因为它本来就是那么回事:又一个程序,跟你跑的所有 别的东西共享着那唯一一个文件系统和那唯一一个钥匙串。

你的笔记本 — 一个文件系统,一个钥匙串进程牢笼claude✗ read ~/.ssh✗ read ~/.aws已拦下 — 感觉密不透风./repo — 智能体刚写好package.json "postinstall": "node x.js"第二个 SHELL — 没被关押$ npm install runs postinstall牢笼从没点名的另一个进程钥匙串~/.aws/credentials~/.ssh/id_rsa~/.npmrc真令牌✗ 牢笼拒绝这条路径读到真凭证 — 没人关押过这个
那个猫鼠游戏的漏洞。一个进程牢笼挡住 claude 进程去碰钥匙串(那条虚线路径,被拒绝)。但智能体真正的产出——一个带 postinstall 钩子的仓库——跑在一个牢笼从没听说过的兄弟进程里。打开第二个 shell,跑 npm install,它的 postinstall 就自由地读到了 ~/.aws/credentials 并把它外泄出去,因为在一台宿主上每个进程都共享一个文件系统和一个钥匙串。牢笼把它的边界划在了错的东西周围:危险从来不是那个被点名的进程,而是那台被共享的机器。

那个牢笼把它的边界划在了一个进程周围。危险从来不是那个进程;而是机器上 每个进程都共享着一个文件系统和一个钥匙串。于是你打补丁——把那个 shell 也关押了,然后是 node,然后是包管理器——而攻击者不断找到一扇你没锁的 门,因为在一台宿主上总还有另一个进程、另一条通往同一批秘密的路径。这就是 那场游戏,而庄家手里总还有另一扇门。

用 hypervisor,把这条线划一次。

Bromure 不再陪着玩,办法是把边界往下挪一级。智能体、它生出的那些 shell、 它装上的那些包,以及那些包跑起来的任何代码,全都活在一个每配置档一台的 Linux 虚拟机里。你的真凭证从不进去。

区别在于这条线的种类。一个进程牢笼是一条关于某个进程的策略,而一个兄弟 进程绕开了它。虚拟机边界是宾客与宿主之间的那堵墙,由 hypervisor 来强制 执行——就是那堵阻止一台 VM 去读另一台内存的同一堵墙。没有哪个系统调用能 跨越它。里面的代码没法把自己推理到你的钥匙串那里,因为那个钥匙串压根就不 在它的世界里。

而在那条线里面,你是自由的。装那个未经审查的包。跑它的 postinstall。 让智能体重写你的 shell 配置、把磁盘填满、把工具链弄坏。那台虚拟机是一次性 的,从来没装过任何要紧的东西。那份自由才是全部的要点——恰恰是沙箱拿走的 东西,也恰恰是一条干净的边界还回来的东西。你不是靠把工作台弄得更没用来 换安全的;你是靠把工作台变成一个一开始屋里就没有值钱东西的地方来得到它的。

那同一条线,正是三项保证从智能体可以推翻的建议,变成在它之下被强制执行的 事实的地方——因为虚拟机对外做的每一件事都得跨过它。

这条线让三件事成真。

每配置档一台的 VM — 想装什么、跑什么、弄坏什么都行智能体 · 生出的 shell · npm / pip · 不可信的 postinstall 代码文件系统里只有桩值 — aws_secret = stub-… _authToken = stub-… id_rsa = stub-…这里没有一样是真的,所以这里没有一样值得偷取一个包用一个凭证push / drop / deleteHYPERVISOR 边界 — 里面的代码跨不过的那唯一一条线① 拦下恶意包扫描:OSV + socket.dev冷却:< 2 天的被扣下恶意 tarball 从不落地② 没有凭证可偷stub-token ⇄ real token在线路上换宿主排查只找到占位符③ 改动前先问读取放行 · 写入暂停在宿主上询问你看到那个字面调用宿主 — 真令牌,在中介之后~/.aws · ~/.ssh · ~/.npmrc — 从不进入 VM,只在线路上换入,且只为一次你允许过的调用
一条边界,三个控制,在智能体之下被强制执行。在虚拟机里智能体想做什么都行。但每一次跨越都由 hypervisor 来中介:一次取包会被扫描(OSV + socket.dev),若它比冷却期还年轻就被扣下;一次凭证使用碰上的是一个桩值,宿主代理会在线路上把它换成真令牌、再换回去;一次改变状态的写入会暂停下来,在宿主上弹出询问。真钥匙串坐在线下,从不进入虚拟机。智能体绕不开其中任何一项,因为除了穿过它,没有别的路径能跨过这条边界。

没有凭证可偷——那个秘密从来就不在屋里。

真令牌留在宿主上。虚拟机拿到的是桩值:语法上有效、但内容在公共互联网 上毫无意义的凭证文件。当智能体发出一次需要真令牌的正当调用时,一个宿主 代理会在线路上把桩值换成真秘密,并把它从响应里换出来。一个为 ~/.aws/credentials~/.npmrcid_rsa 而横扫文件系统的被攻陷依赖, 找到的是占位符。这就是令牌交换:凭证存在着,智能体把它用在它该用的 地方,而那份可能被偷的副本,在智能体的世界够得到的任何地方都不存在。

收窄那个宽泛的令牌——使用前先问,改动前先问。

真令牌通常比任务所需更宽泛。中介把授权保持得短命且限定了范围,而且——那 真正物有所值的部分——它把读取和写入区别对待。一次读取放行。一次改变状态 的调用——一次 git push、一个 DROP TABLE、一次 AWS 的 Terminate*——会 在边界处暂停,并在宿主上向你询问,展示那个字面操作,而不是智能体写的某段 摘要。

那改变了一个宽泛令牌的含义。一个本可以删掉生产的令牌,只有在一个人看过 那个确切的调用并说了「行」之后才能这么做;印在凭证上的那个范围,不再是 爆炸半径。智能体没法预先批准自己、没法把模式降级、也没法读到那份授权—— 决定活在线的另一侧。当一个智能体在九秒内删掉了一个生产 数据库时,原理是一样的:那个想 运行命令的东西,不应该是那个判断命令安不安全的东西。

别当第一个发现的人——供应链在门口就被挡下。

挡下一个被投毒的包,最好的时机是在它落地之前。每一次取包都跨过边界,所以 代理会扫描它——OSV 查已知的 CVE,socket.dev 查那些数据库还没逮住的 东西:流氓安装脚本、误植仿冒、一小时前才发布的入侵。而且它强制执行一段 冷却期:过去两天内(可调)的任何一个发布版本,在生态系统还没跟上的 期间,就是装不上。一条蠕虫的整个窗口,就是发布撤回之间的那段空隙; 拒绝才一天大的包,就是拒绝去当那只报警的金丝雀。postinstall 钩子在进来 的路上就从 tarball 里被剥掉,哈希被修好以便安装仍能通过校验——于是落地的 那个包,落地时是惰性的。这一切都不要求开发者去审查任何东西。他们想拉什么 就拉什么;那条边界,就是那个在等着的东西。

其他方案止步之处

大多数工具只守住一层。 Bromure 全都守住。

隔离、把密钥挡在智能体之外、约束这些密钥的使用方式、扫描供应链、拦截提示注入——这个领域往往只挑其中一项。下面把同一套智能体威胁模型套用到人们常用的工具上,看看每个工具到哪儿就止步了。

防护
Dev ContainerVS Code
nonokernel sandbox
agent-vaultoctokraft
Agent VaultInfisical
Docker SandboxesmicroVM
BromureAgentic Coding
隔离边界
影响范围在哪里被挡住
同一容器,共享内核
内核白名单,无独立内核
智能体就地运行
仅代理;智能体未隔离
microVM,独立内核
硬件 VM,独立内核
把密钥挡在智能体之外
它能读到真实凭证吗?
转发 SSH agent 和 git 凭证
拦截密钥文件;部分代理
通过管道注入;无读取路径
代理在链路上附加
主机代理注入请求头
在链路上替换桩令牌
凭证范围与审批
按次限制、只读、过期、确认
不按使用范围限制
审批流程 + 出站过滤
按密钥设 TTL;拦截 shell
按端点出站过滤
域名白名单;VM 内代码仍可使用
按目的地确认 + TTL
供应链扫描
拦截恶意/易受攻击的依赖包
不扫描软件源
仅签名,不扫描依赖包
不在范围内
不在范围内
不扫描依赖包
Age-gate、OSV、socket.dev
提示注入检测
扫描不可信内容与规则文件
PromptGuard + ModernBERT
审计追踪
记录智能体做了什么
仅容器日志
不可篡改的本地审计
请求日志
请求日志
完整会话追踪,已加密
供应链清单(Enterprise)
记录获取的每个依赖包
每个依赖及裁定,可搜索
Token 用量(Enterprise)
哪些文件消耗最多 token
按文件、仓库、模型
完整 — 内置并强制 部分 — 有限或可选 无 — 未覆盖

隐藏令牌并不等于管控它的使用。Docker Sandboxes 让真实值不进入 VM,但它的代理仍会把该凭证附加到沙箱发出的任何外发请求上;于是一个在旁边被植入的恶意依赖包,即使从未看到这个值,也能拿它去访问白名单内的服务。只有 Bromure 会在依赖包运行前先扫描它,并对每一次使用加上确认、只读和 TTL 限制——在智能体无法绕过的单一边界上强制全部五项控制。

依据各项目的公开文档整理,2026 年 6 月。这里的 agent-vault 指 octokraft/agent-vault(基于管道的密钥注入),区别于 Infisical 的 Agent Vault(HTTP 凭证代理)。Docker Sandboxes 仍是实验性预览,其代理的凭证对 VM 内的任何程序都保持可用。全设备的依赖包清单与 Token 用量汇总由 Bromure Enterprise Manager 提供。这些工具更新很快——发现过时之处?告诉我们。

第四件事:数据里的一条指令不是一道命令。

上面那三项保证共用着一个值得摆到明面上的假设:它们防的都是会拿走 什么的代码——一个凭证、一个令牌、一个新鲜 tarball 的运行机会。可有 一种攻击什么都不拿。它只是告诉智能体该做什么。一行埋在智能体所读 README 里的字、一个抓取页面里的字符串、一段工具输出里的句子、一条 藏在被智能体奉为常备指令的 CLAUDE.md 里的指示——模型把它当作上下文 吞下,再把它当作指令服从。泄露那个文件。削弱那道检查。跳过那个测试。 沙箱对这一切没有任何意见,因为没有任何东西跨过它看守的哪堵墙:那条 指令是作为数据到达的,就在智能体本来就该读的内容里。

但它确实跨过了那条线——模型看到的一切都会跨过。所以从 2.4.0 起,这条边界会先读它一遍,在设备本地、在宿主 这一侧。一个本地的 PromptGuard 分类器会为流向模型的不可信内容打分 ——文件读取、网页抓取、工具输出——找出那些根本不该出现在那里的指令。 而智能体不加质疑就服从的那些规则文件——CLAUDE.mdAGENTS.mdGROK.md——会经受一道更严厉的双重检查:一次针对不可见 Unicode、 双向文本花招和「忽略此前指令」式元指令的确定性扫描,外加一个经过 微调的 ModernBERT 分类器,专抓关键词过滤会漏掉的、措辞平和的滥用。 獠牙按配置档由你来挑:把它记录进 Security Log,询问并看到被标记 的文本,或在模型看到被投毒的那段文字之前就拦截请求。没有任何东西 离开这台 Mac。

这个位置的选择,和另外三项是同一个论证。一个已经吞下注入的智能体, 不能再被信任去汇报它——注入的第一条指令,通常就是某个版本的别提这 件事。检测器不去问智能体。它在那条线的远侧读流量——智能体的说服 够不到的那一侧。

这条线在哪些地方救不了你。

一条边界是一个具体的形状,不是一句魔咒。四条实在的边缘:

配置档是长命的,所以持久化会持久存在。

一个 Bromure 配置档不是一块一次性的磁盘。一段把自己写进某条启动路径的 载荷,可能在下一次会话里醒来——醒来面对的是一个没有宿主密钥的宾客,和 一个只会说短命的、要询问的、限定范围令牌的中介。身处一个空无一物的屋子 里的存在,但终归还是存在。

一次你批准的写入,就是一次发生了的写入。

那个询问拦下的是智能体没告诉你的那次调用。它不去读你的 diff。批准一次 git push,Bromure 就把它转发出去——原则上包括一段你没注意到的被投毒 工作流。它把决定挪到你这里,并展示那个真实的操作;读它,仍然是你的活儿。

冷却期是一扇窗,不是一堵墙。

两天是按观察到的发布到撤回空隙调出来的。一个有耐心的攻击者可以蹲在一个 被攻陷的版本上熬过冷却期,到第三天就能被装上了。它饿死的是当天的蠕虫; 它不为一个仅仅是放久了的包作担保。socket.dev 和 OSV 仍然得各尽其责。

有意地去限定中介的范围。

隔离遏制的是爆炸;限定范围决定的是它本可以有多大。一个只读一个仓库的 配置档,不该握着一个能写它的令牌;一个从不发布的配置档,不该握着任何 发布令牌。这条线把秘密挡在 VM 之外——而到底存在着哪些秘密,仍然是你的 决定。

我们会守住的那条线。

这就是那份承诺。一个开发者不该为了保住自己的钥匙串,就得变成一个系统 管理员——维护一张白名单、预先批准每一个依赖、交出那份让智能体值得一跑的 速度。沙箱把安全弄成了一笔跟有用性对赌的交换,而开发者明智地一再选择 有用性。一条边界拒绝这笔交换:在里面想做什么都行,因为里面是可丢弃的, 而那四件要紧的事——你的凭证、你令牌的范围、够得到你的那些包、够得到 你模型的那些指令——是在一条里面的代码没法争辩的线上决定的。

这就是为什么「它是个沙箱」是我们会一再纠正的那个说法。沙箱约束智能体。 Bromure 约束边界,并放智能体自由。Bromure Agentic Coding 免费、开源,今天就已发布。