供应链保护
自主编码代理会不断安装软件包 —— 搭建一个项目会拉取数百个依赖项,而人类甚至从未读过它们的名字,更不用说变更日志了。这使得软件包注册表成为敌意代码进入沙箱的最宽通道:一个仿冒拼写的名字、一个刚被劫持的发布版本,或一个恶意的 postinstall 脚本,都会在 npm install 完成的那一刻以代理的完整权限运行。Bromure Agentic Coding 将每一次软件包获取都视为不受信任的输入,并在任何一个字节到达虚拟机之前,在宿主机上对其应用逐工作区的策略。
本章解释哪些内容被拦截、流水线中的每一项检查、检查触发时会发生什么,以及如何在安全日志中读取结果。该窗格的逐字段设置参考位于 供应链设置。
为何软件包安装是攻击面
沙箱已经将代理包裹其中:它无法触及你的 Mac,其凭据也都是诱饵(参见 凭据)。沙箱本身无法做到的,是判断代理拉入代码的来源。供应链攻击恰恰利用了这一空隙:
- 新发布版本劫持。 某位维护者的账户被攻破,随即发布了一个恶意版本。这类版本通常在数小时至数天内被检测并撤回 —— 这正是 时效门 拒绝比某一截止点更年轻版本的原因。
- 已知易受攻击的版本。 代理将某个依赖解析到了一个已发布安全公告的版本。OSV 检查会捕获这些情况。
- 恶意软件、仿冒拼写和恶意安装脚本。 从一开始就为作恶而构建的软件包。socket.dev 和 Delpi 提供方会对其进行标记或过滤,而安装脚本剥离则直接移除了最常见的执行途径。
强制执行在设计上就位于宿主机侧。代理服务器在代理看到响应之前就对响应应用策略,因此虚拟机内运行的任何东西 —— 包括一个完全被攻陷的代理 —— 都无法放松规则。虚拟机内的 .npmrc 和 pip.conf 只能在代理服务器已经提供的基础上进一步收紧,绝不能放宽。信誉服务的 API 密钥(socket.dev、Delpi)仅保存在宿主机上,从不导出到虚拟机中。
拦截如何工作
虚拟机发出的每一个网络请求都会经过宿主机侧的 MITM 代理服务器(参见 概念)。代理服务器识别对主要软件包注册表的请求,并对每一个请求进行分类:
- 元数据 —— 某个软件包的版本清单(一个 npm packument、PyPI 的 JSON API 或
/simple/索引、一个 Cargo 稀疏索引条目,等等)。时效门在这里通过改写清单来运作。 - 制品 —— 某一特定版本的可下载文件:一个 npm
.tgz、一个 Python wheel 或 sdist、一个.crate、.gem、.nupkg,或 Go 模块.zip。OSV 查询、socket.dev 检查、脚本剥离和硬拦截都发生在制品获取时。 - 透传 —— 这些宿主机上的其他一切(搜索、认证)。原样放行。
只要工作区启用了至少一个供应链层(或该工作区已在 bromure.io 注册,此时即便关闭了所有强制执行层,获取行为仍会被观测用于遥测),拦截就会自动进行。虚拟机内无需安装或配置任何东西。
注意: 已存在于 npm 或 pip 本地缓存中的软件包永远不会触及网络,因此不会对其进行任何检查 —— 也不会记录。安装期间安全日志安静,可能只是意味着一切都来自缓存。
裁决:允许、改写、拦截、暂挂
供应链层是一个个独立的开/关切换开关,每一个都带有固定的动作 —— 该窗格没有全局的拦截/警告/允许模式。(你可能从 提示注入 熟悉的记录/询问/拦截三选一单选,以及 护栏 中的强制执行模式,都是相互独立的系统。)每一次获取都以以下四种结果之一告终:
| 结果 | 发生了什么 | 可见标记 |
|---|---|---|
| 已允许 | 响应原样透传。 | 安全日志中的绿色对勾;一行"inspecting"确认代理服务器看到了它。 |
| 已改写 | 代理服务器修改了响应:从元数据中移除过于新鲜的版本,或从压缩包中剥离安装脚本。包管理器照常继续。 | X-Bromure-Rewritten: supply-chain 响应头;日志中橙色的"stripped"行。 |
| 已拦截 | 下载被以 HTTP 451("因法律原因不可用")响应拒绝。 | X-Bromure-Block: supply-chain 头;日志中的红色行。 |
| 暂挂以待许可 | 下载暂停,同时 Bromure 询问你 —— 针对锁文件固定的透传,以及没有任何已启用信誉源能够审查的软件包。拒绝会将暂挂转为 451 拦截。 | 一条系统许可警报;该决定会被记录。 |
451 拦截携带一段以 Bromure Supply-Chain Security blocked this request: 开头的纯文本正文,其后是确切原因,例如:
Bromure Supply-Chain Security blocked this request: npm package [email protected]
published 4 hours ago — policy requires 2 days minimum
npm、pip、cargo 及其他包管理器会在其错误输出中逐字打印该正文,因此代理和你都能精确看到安装失败的原因 —— 而代理往往能够自行绕开它(例如,固定到一个更旧的版本)。选择 451 状态是有意为之,这样供应链拦截就能一眼与 护栏 使用的 403 区分开来。
各生态系统的覆盖情况
代理服务器拦截八个软件包生态系统。并非每项检查都支持每一个生态系统:
| 生态系统 | 被拦截的宿主机 | 时效门 | OSV | socket.dev | Delpi | 脚本剥离 |
|---|---|---|---|---|---|---|
| npm | registry.npmjs.org、*.npmjs.org | 是 | 是 | 是 | 是 | 是 |
| PyPI | pypi.org、files.pythonhosted.org | 是 | 是 | 是 | — | — |
| Cargo | crates.io、static.crates.io、index.crates.io | 是 | 是 | 否 | — | — |
| RubyGems | rubygems.org | 是 | 是 | 是 | — | — |
| Maven Central | repo1.maven.org、repo.maven.apache.org、search.maven.org | 否 | 是 | 是 | — | — |
| NuGet | api.nuget.org、*.nuget.org | 否 | 是 | 是 | — | — |
| Go modules | proxy.golang.org | 否 | 是 | 是 | — | — |
| Packagist | repo.packagist.org、packagist.org | 是 | 是 | 是 | — | — |
有两个值得了解的空缺:
- 时效门 —— Maven、NuGet、Go。 这三个生态系统在其标准元数据响应中不携带逐版本的发布时间戳,因此它们的元数据不经过滤地透传,而制品后备也没有数据可供依据。时效门实际上今天不会拦截它们;npm、PyPI、Cargo、RubyGems 和 Packagist 则完全覆盖。
- socket.dev —— Cargo。 socket.dev 不支持 Cargo。在启用 socket.dev 过滤的情况下,每一个
crates.io制品都不会产生裁决,因此会触发未经验证软件包的许可提示(参见 离线与降级行为)。如果你的工作区有大量 Rust 工作,要么回应这些提示,要么选择另一个提供方。
时效门
时效门拒绝比可配置天数更年轻的软件包版本,其原理是:一个刚发布的版本最有可能是刚被劫持的软件包 —— 恶意版本通常会被迅速报告并撤回,而对于常规开发来说,等过这段窗口几乎没有成本。它是唯一默认启用的层,默认最短 2 天。
它通过两个协同的机制运作:
- 元数据改写。 代理服务器从注册表的版本清单中剥离过于新鲜的版本,并将
latest和其他 dist-tag 重新指向最新的幸存版本。因此浮动引用 ——pkg@latest、semver 范围 —— 永远不会被硬拦截:包管理器会静默解析到足够老、能够通过的最新版本。从代理的视角看,比截止点更年轻的版本就仿佛尚不存在。 - 制品获取后备。 代理服务器在元数据中看到的每一个逐版本发布时间都会被缓存在内存中(最多 50,000 条)。如果代理随后直接请求一个固定的、过于新鲜的版本,该获取会被以 451 拦截,其中说明该软件包的实际时效和所需的最短值。对于 pip —— 其默认的 PEP 503 HTML 索引不携带时间戳 —— Bromure 会按需查询
https://pypi.org/pypi/<pkg>/<version>/json以获取发布时间。
要配置它,打开工作区的 供应链 窗格并使用 时效门 组:
- 切换 拒绝比截止点更年轻的软件包。
- 用步进器设置 最短时效:(0–90 天)。
- 如果某个特定软件包必须能够立即安装 —— 例如,它由你自己的团队发布 —— 用 添加条目 按钮将其添加到 豁免软件包 下。条目使用 允许列表格式:
npm:axios将豁免限定到一个生态系统,而裸写的axios则在每个生态系统中匹配该软件包名。匹配不区分大小写。
提示: npm 元数据请求会被静默升级为完整的 packument,以便时效门能看到发布时间 —— 对于 npm 的缩略元数据格式,你无需做任何事情。
OSV 漏洞检查
OSV 检查会在 api.osv.dev 上查询每个下载制品的生态系统、软件包和版本 —— 这是免费的开源漏洞数据库,聚合了 GitHub Advisory Database、PyPI 公告、Go 的漏洞数据库、RubySec 等。如果该确切版本的任何公告达到或高于你所选的严重性,下载就会被以 451 拦截。它覆盖全部八个生态系统,且不需要 API 密钥。
严重性在公告带有 GHSA 标签时取自该标签;否则 Bromure 从公告的向量字符串计算 CVSS v3 基准分数。
在 OSV 漏洞检查 组中:
- 切换 在 api.osv.dev 上查询软件包(免费,无需密钥)。
- 选择 在此严重性拦截: —— 低及以上、中及以上、高及以上,或 仅严重。
该检查默认关闭,严重性阈值默认为 高及以上 —— 正如该窗格自身所指出的,传递性子包中的一个低严重性 CVE 不应打断工作流。查询仅在制品下载时运行(而非元数据获取),结果会在应用运行期间缓存于内存,最多 16 个查询并行运行,瞬时网络错误会以退避方式重试最多 5 次。如果完全无法访问 OSV,软件包会被暂挂以待你许可,而非静默允许 —— 参见 离线与降级行为。
软件包过滤:socket.dev 和 Delpi
软件包过滤 组通过一个互斥的单选选择信誉提供方:无、socket.dev 或 Delpi。这两个提供方的工作方式根本不同 —— socket.dev 是代理服务器在放行获取前所查询的预检查询;而 Delpi 则完全替换 npm 注册表。在设计上一次只能有一个处于激活状态(同时运行两者会对每一次安装双重过滤),选择 无 会禁用两者,同时保留任何已存储的密钥。无论你选择哪个提供方,本章中的其他层 —— 时效门、OSV、脚本剥离 —— 仍会在其之上应用。
默认为 无。在该单选存在之前创建的、已经拥有 socket.dev 密钥的工作区,会被自动推断为 socket.dev。
socket.dev
socket.dev 是一项商业软件包信誉服务。在选择 socket.dev 并输入 API 密钥的情况下,代理服务器会在提供每个制品下载之前,对照 socket.dev 的问题 API 进行检查。它支持 npm、PyPI、Go、Maven、RubyGems、NuGet 和 Packagist —— 但不支持 Cargo(参见 各生态系统的覆盖情况)。
你需自备密钥:点击 API key: 字段旁的 获取 API 密钥(它会打开 socket.dev/dashboard/settings/api-tokens),创建一个令牌,并将其粘贴到安全字段中。两个拦截切换开关在输入密钥之前保持禁用,而空密钥无论切换开关如何都会完全禁用 socket.dev。密钥存储在宿主机侧的工作区 profile.json 中,从不进入虚拟机;所有查询都源自你的 Mac。
有两个独立的拦截可用:
- 拦截被攻陷的软件包(恶意安装脚本、被标记恶意软件、仿冒拼写、可疑遥测) —— 针对 socket.dev 攻击特征的供应链风险问题触发。确定性的恶意软件信号(恶意软件、已知恶意软件、GPT 检测到的恶意软件、恶搞软件包、被攻陷的 SSH 密钥)在任何严重性下都拦截。噪声更大的攻击特征信号 —— 混淆代码、可疑字符串、安装脚本、仿冒拼写、shell 访问、异常 HTTPS 使用 —— 仅在 socket.dev 将其评为高或严重时才拦截,因此一个带有良性
postinstall的软件包不会被捕获。纯质量和属性信号(新作者、环境变量读取、网络访问、遥测)则有意永不拦截。 - 拦截带有已知 CVE 的软件包 —— 针对 socket.dev 的漏洞类别,在达到或高于 CVE 拦截阈值: 选择器(与 OSV 相同的四个级别;默认 高及以上)时触发。
两个拦截都默认关闭。结果会在应用运行期间缓存,最多 16 个调用并行运行,瞬时故障会以退避方式重试 5 次。认证错误会在安全日志中以 HTTP 状态和响应正文预览的形式呈现。
注意: 如果你同时启用 OSV 和 socket.dev 的 CVE 拦截,两者都会检查每个制品 —— 这是允许的(它们是不同的层),只是在 CVE 覆盖上冗余。许多用户改为将 socket.dev 的被攻陷软件包拦截与免费的 OSV 检查搭配使用。
Delpi
Delpi 是 Lupin & Holmes(landh.tech)推出的即插即用安全 npm 注册表,它提供预先审查过的软件包。Bromure 不是查询软件包,而是将每一个 npm 注册表请求 —— 元数据、压缩包、审计,任何寻址到 registry.npmjs.org 或 *.npmjs.org 的请求 —— 重新路由到 Delpi 位于 depi-npm-proxy.landh.tech:443 的兼容 npm 的过滤注册表,附上你的密钥作为 Authorization: Bearer 头,并剥离客户机发送的任何 Authorization 头。Delpi 会改写其 packument 中的压缩包 URL 使其指向自身,因此这些后续获取也会在宿主机侧被注入密钥。
要启用它,在单选中选择 Delpi 并将你的密钥粘贴到 API key: 安全字段。当该字段为空时,会显示一条橙色警告:输入 API 密钥 —— 没有密钥 Delpi 将保持关闭。 每个被重新路由的请求都会记录在安全日志中:
[delpi] GET registry.npmjs.org/… → https://depi-npm-proxy.landh.tech
错误处理是明确的而非静默的:
- 401(密钥被拒绝)。 Bromure 会替换为一段 npm 会打印的清晰纯文本错误("Bromure: the Delpi registry rejected the configured API key…",带有头
X-Bromure-Block: delpi-auth),记录它,并针对每个工作区与密钥的组合弹出一次性 GUI 警报:"Delpi rejected your API key"。无界面的 SSH/TUI 会话不会收到 GUI 警报;它们依赖日志行和改写后的 npm 错误。 - 403(软件包被 Delpi 拦截,或密钥未获授权访问它)。 记录并原样透传,因此 npm 会报告 Delpi 自己的拒绝文本。
Delpi 仅影响 npm —— 其他七个生态系统不受其影响。Delpi 在单选中替换的是 socket.dev,而非你的本地策略:时效门、OSV 检查和脚本剥离仍会对 Delpi 提供的内容运行。
安装脚本剥离
npm 的 preinstall、install、postinstall 和 prepare 钩子会在安装时运行任意代码,是现实世界 npm 恶意软件的主力。启用 动态从 npm 压缩包中剥离 preinstall / install / postinstall / prepare(在 安装脚本 组中)后,代理服务器会实时改写每个 npm 压缩包:它对 .tgz 解压缩,定位顶层的 package/package.json,移除那四个脚本键,重新计算 tar 头校验和,并重新压缩。
由于压缩包字节发生了改变,代理服务器也会从软件包的注册表元数据中清除 dist.integrity 和 dist.shasum,这样 npm 会自行从剥离后的压缩包计算哈希,其验证对于未固定的安装仍能通过。每次剥离都会被记录("stripped install scripts from"该软件包及版本,以橙色显示);经检查确认干净的压缩包仅被打上 X-Bromure-Rewritten: supply-chain 头。一旦发生任何解析失败,原始压缩包会原样透传 —— 该层为失败开放(fail open),因为它绝不能弄坏一个格式良好的安装。
有些软件包确实需要安装脚本 —— 诸如 better-sqlite3 或 node-canvas 这样的原生绑定编译器。将它们添加到 允许以下软件包运行安装脚本(格式 npm:better-sqlite3),它们就会保留其钩子。
该切换开关默认关闭,且有两个限制:
- 仅限 npm。 PyPI sdist 不会被改写 ——
setup.py就是任意代码,因此在那里剥离不可行。 - 仅限未固定的安装。 一个在
package-lock.json中固定了完整性哈希的压缩包,无法在不导致验证失败的情况下被改写。那种情况由下一层管理。
锁文件固定的安装
一个 锁文件固定的安装 —— npm ci —— 会在锁文件中固定每个压缩包的完整性哈希。Bromure 无法在不破坏哈希验证的情况下改写那些压缩包,因此它唯一的选择是原样透传它们,或者拦截它们。默认情况下它们会静默透传。
如果你想有发言权,在 锁文件固定的安装 组中启用 在原样透传锁文件固定的压缩包(npm ci、pip --require-hashes)之前提示。一批中第一个锁文件固定的获取(对于 npm,通过 npm-command: ci 请求头检测到)随即会弹出一个宿主机许可对话框,标题为 透传来自工作区"…"的 npm ci(锁文件固定的安装)?,带有按钮 允许 15 分钟、允许一次、在本会话剩余时间内允许 和 不允许。整批并发的获取会汇聚到那一个提示上并遵循你的决定;拒绝会以 451 拦截该获取,且该拒绝会被记住 60 秒,以便重试不会再次提示。
注意: 尽管 UI 标签提及了
pip --require-hashes,目前检测仅对 npm 的npm-command: ci头实现 —— pip 哈希固定的安装不会触发该提示。(它们也从不会被改写,因为 PyPI 制品永不被修改。)
许可提示与授权
所有供应链询问用户的路径 —— 锁文件透传以及下文描述的未经验证软件包暂挂 —— 都会经过一个带有突发汇聚的共享许可代理:针对相同范围的并发请求会等待在单个对话框上,而非堆叠警报。每个提示都提供同样的四个决定:
| 决定 | 效果 |
|---|---|
| 不允许 | 以 451 拦截;记住 60 秒,自动拒绝即时重试。 |
| 允许一次 | 放行这一个请求(或汇聚的突发)。 |
| 允许 15 分钟 | 授予该范围 15 分钟。 |
| 在本会话剩余时间内允许 | 授予该范围直至应用退出。 |
活动的授权与拒绝在审批 UI(窗口 → 凭据审批… 区域)中列为 供应链决定 —— 与护栏决定相分离 —— 你可以在那里提前撤销某个授权。授权仅存于内存,不会在应用重启后保留。
在远程 SSH/CLI 会话中没有 GUI 对话框:同样的问题会在工作区的 tmux 内渲染为一个选择器。不作答即为拒绝。参见 远程访问。
配置策略
供应链策略按工作区配置,位于 编辑工作区 窗口的 供应链 窗格中(侧边栏中黄色的运输箱图标)。完整的逐字段参考位于 供应链设置;默认值一览:
| 设置 | 默认值 |
|---|---|
| 拒绝比截止点更年轻的软件包(时效门) | 开启,最短时效: 2 天,无豁免 |
| 在 api.osv.dev 上查询软件包(免费,无需密钥) | 关闭;在此严重性拦截: 高及以上 |
| 软件包过滤 | 无(无 socket.dev 或 Delpi 密钥) |
| 拦截被攻陷的软件包 / 拦截带有已知 CVE 的软件包(socket.dev) | 关闭;CVE 拦截阈值: 高及以上 |
| 动态从 npm 压缩包中剥离 preinstall / install / postinstall / prepare | 关闭,允许列表为空 |
| 在原样透传锁文件固定的压缩包之前提示 | 关闭(静默透传) |
三个操作细节:
-
编辑即时生效。 存储工作区会立即将新策略推送到正在运行的会话 —— 代理服务器按请求读取它,因此永远不需要重启虚拟机。每个新的或更改后的策略都会在安全日志中以一行摘要确认,例如:
[supply-chain] policy engaged for 1a2b3c4d: age-gate=2d osv=high socket.dev=compromised+cve=high strip-scripts配置错误会直接在该摘要中标出:
socket.dev=key-set-but-no-toggle表示已输入密钥但两个拦截均未启用,而delpi=selected-but-no-key表示已选择 Delpi 但因缺少密钥而关闭。 -
存储位置。 策略存放于工作区位于
~/Library/Application Support/BromureAC/profiles/<id>/下的profile.json中(仅写入非默认字段)。socket.dev 和 Delpi 的 API 密钥也存储在那里 —— 仅在宿主机侧,在 UI 中显示为安全字段,永不复制到虚拟机中。没有供应链专属的环境变量或启动参数。 -
远程配置。 SSH/TUI 远程菜单以相同字段暴露相同的 供应链 窗格,因此无界面实例无需 GUI 即可配置。参见 远程访问。
安全日志窗口
供应链流水线所做的一切都可在安全日志中看到 —— 打开 窗口 → 安全日志…。它是代理服务器发出的每一个安全事件的实时尾随:供应链查询和裁决、451 拦截、脚本剥离、策略生效确认和 Delpi 重新路由,以及提示注入检测、Fusion 启用/停用、LLM 路由变更、远程访问事件和工作树行。整个应用共用一个窗口;再次选择该菜单项会将其带到前台。
各行以颜色编码,以便你一眼读懂一次安装:
| 颜色 | 含义 | 标记 |
|---|---|---|
| 蓝色 | 出站查询(OSV、socket.dev、发布时间后备) | → |
| 绿色 | 干净裁决 —— 软件包通过 | ✓ |
| 红色 | 拦截或失败(451、错误) | ✗ |
| 橙色 | 从压缩包中剥离了安装脚本 | "stripped" |
| 强调色 | 策略生效/更改 | [supply-chain] |
该窗口有一个 筛选… 字段用于收窄视图(输入软件包名、生态系统,或你关心的行中的任意文本)、一个 自动滚动 复选框(默认开启;取消勾选可在阅读时停止跟随尾部)、一个清空缓冲区的 清除 按钮,以及一个显示条目计数的页脚("42 entries",或在筛选生效时显示"7 of 42 entries")。一条空状态消息会解释何时会出现条目。
要看它的实际效果:打开该窗口,在会话内运行任意安装(npm install、pip install、cargo add、gem install……),并观看条目流式涌入。一次完全干净的安装仍会为每个制品产生"inspecting"行 —— 这是有意为之,这样一个安静的流水线就能与一个被绕过的流水线区分开来。
注意: 缓冲区是一个上限约为 5,000 行的内存环形缓冲区,不会在应用重启后保留。每一行也会被镜像到应用的 stderr,因此从终端启动
bromure-cli(或捕获其日志输出)能为你提供一份持久的副本。该窗口以 820×460 打开,可缩小至 720×360。
离线与降级行为
流水线的信誉检查依赖于从你的 Mac 发出的出站调用(绝不从虚拟机):OSV 用 api.osv.dev、socket.dev 用 api.socket.dev、PyPI 发布时间后备用 pypi.org、Delpi 用 depi-npm-proxy.landh.tech。当一个已启用的源无法产生裁决时 —— 重试后网络仍中断、HTTP 错误、限速、认证失败或不受支持的生态系统 —— Bromure 会 失败关闭(fail closed),而非静默允许。
下载暂停,并有一个 未经验证软件包暂挂 逐软件包和版本地询问你,系统警报标题为 透传来自工作区"…"的未经验证软件包?:Bromure 无法到达该源以审查该软件包,安装它意味着接受一个未对照你所配置的信誉源检查过的软件包。四个标准许可决定适用;拒绝(包括在无 GUI 可用且远程提示超时时)会以 451 拦截。暂挂按 package@version 键控,因此一次批准绝不会覆盖整个依赖图。
这是有意的保护:没有它,一个能对信誉服务诱发限速的攻击者就可能悄悄溜过未经检查的软件包。实际后果如下:
- 完全离线工作 且启用了 OSV 或 socket.dev,意味着每个未缓存的软件包都会有一个提示。在离线期间禁用这些层 —— 时效门无需连接即可从已缓存的元数据继续工作。
- socket.dev 加 Cargo 意味着每个 crate 都有一个提示,因为 socket.dev 根本无法审查 Cargo(授权按 package@version 键控,因此一次会话授权只覆盖那一个 crate 版本)。
- PyPI 发布时间后备是唯一的例外: 在纯网络错误时它会为那一次获取失败开放(该失败不缓存,因此下次会重试该查询)。时效门的元数据改写不受影响。
攻陷检测
供应链检查降低了敌意代码进入沙箱的几率;攻陷检测则捕获确实进来的敌意代码试图行动的那一刻。它是凭据系统而非软件包流水线的一部分 —— 诱饵凭据和令牌交换的完整图景在 凭据 中 —— 但它的警报在此记录,因为一个被投毒的软件包是它最可能的触发因素。
虚拟机发出的每一个出站请求都会在单次遍历中被扫描,查找凭据交换层铸造的诱饵("伪造")令牌。一个限定于某一宿主机(比如 github.com)的伪造令牌,出现在发往任何其他宿主机的请求中,就是凭据外泄的特征。当这种情况发生时:
- 代理服务器以 451 拦截该请求 —— 目的地永远收不到一个字节。
- 虚拟机被立即暂停。如果会话已分离,它会被强制重新附加并显现。冻结的会话画面泛红。
- 出现一条关键警报,标题为 此环境可能已被攻陷,说明 Bromure 检测到一次将会话凭据从指定工作区泄露到一个并非为其铸造的宿主机的出站尝试,且虚拟机已被暂停 —— 每次泄露带有一行详情:一段令牌预览(如
sk-a…f9q3)、该凭据的名称、它被铸造所针对的宿主机,以及观测到它正前往的宿主机。
该警报提供三种响应:
- 关机(默认)—— 停止虚拟机并将工作区标记为 已攻陷。
- 保存以供调查 —— 你选择一个文件夹;Bromure 导出
disk.img(虚拟机系统磁盘的副本)、home.tar.gz(工作区主目录),以及每个共享文件夹的shares/<name>.tar.gz,然后关机并将工作区标记为已攻陷。RAM 状态会被丢弃。 - 继续 —— 接受风险并恢复;如果再次发生,检测器会重新触发。Esc 和 Cmd-. 有意不映射到 继续 —— 恢复一个可能敌对的虚拟机必须是一次明确的点击。
一个被标记为已攻陷的工作区在你明确擦除它之前会拒绝再次启动,而擦除流程会警告共享文件夹不会被擦除 —— 它们可能仍持有受污染的文件,因此请手动审查它们。
无需配置任何东西:对于声明了宿主机范围的凭据,检测始终开启。没有固定宿主机的手动凭据("任意宿主机")永远无法触发它,而 Claude/Codex 令牌使用一种宽松的同注册域匹配(一个为 api.anthropic.com 铸造的令牌在另一个 anthropic.com 宿主机上出现不会报警)。一次只显示一条警报;其打开期间的重复事件会被丢弃。
企业级可见性
对于已在 bromure.io 注册的工作区,每一次元数据和制品获取还会向企业事件流发出一个 supply_chain.fetch 事件,携带生态系统、软件包、版本、请求类型、结果(allowed、rewritten、blocked 或 stripped),以及原因类型(age_gate、osv、socket_compromised、socket_cve、verify_unavailable、lockfile_denied、scripts_stripped)。仅注册本身就赋予管理员组织范围的软件包下载可见性 —— 一份每个代理所安装一切的清单 —— 即便在关闭了所有强制执行层的工作区上也是如此。
注册如何工作,以及管理员在另一端看到什么,都在 企业 中介绍。