供应链
编码代理会安装大量软件包,而一个刚被攻陷的依赖项是毒化沙箱最直接的途径之一。供应链窗格用于配置 Bromure Agentic Coding 对每次软件包拉取所应用的逐工作区策略。正如窗格自身的描述所言:Bromure 通过宿主机 MITM 扫描每一次软件包拉取(npm、PyPI、Cargo、RubyGems、Maven、NuGet、Go 模块、Packagist),并在代理看到响应之前应用这些策略;虚拟机内的 .npmrc / pip.conf 只能进一步收紧这些设置——它们无法放宽。使用逐软件包的允许列表进行精准的例外覆盖。
该窗格将五个可独立开关的层归为一组:年龄门控、OSV 漏洞检查、软件包过滤、安装脚本以及锁文件锁定安装(后三项需向下滚动查看)。本页记录这些设置;完整的流水线——生态系统覆盖范围、安全日志、响应头以及攻陷检测——在供应链保护章节中介绍。
强制执行的工作原理
所有检查都在宿主机端的代理服务器中运行,绝不会在虚拟机内部进行。信誉服务的 API 密钥(socket.dev、Delpi)仅保存在宿主机上,绝不会导出到虚拟机中。当某项检查触发时,代理服务器会以携带纯文本原因的 HTTP 451 响应阻止下载(例如,"npm package [email protected] published 4 hours ago — policy requires 2 days minimum");npm、pip 和 cargo 会逐字打印该文本,因此代理和你都能确切看到安装失败的原因。被阻止的响应携带 X-Bromure-Block: supply-chain 头;被重写的响应携带 X-Bromure-Rewritten: supply-chain 头。
策略编辑即时生效:存储工作区会立即将新策略推送到正在运行的会话——无需重启虚拟机——并且每次更改都会在安全日志中以 [supply-chain] policy engaged 一行确认(窗口菜单 → 安全日志…)。
注意: 已存在于 npm 或 pip 本地缓存中的软件包绝不会接触网络,因此不会对它们进行任何检查或记录。安装期间安全日志保持安静,可能只是意味着一切都已缓存。
年龄门控
年龄门控会拒绝比可配置截止时间更新的软件包版本,其理论依据是:刚发布的版本最有可能是刚被攻陷的软件包。它是唯一默认开启的层。
- 拒绝比截止时间更新的软件包——主开关。默认开启。
- 最短年龄——以天为单位的截止时间(步进器,0–90)。默认:2 天。
- 豁免软件包——格式为
npm:axios(单个生态系统)或仅axios(所有生态系统)的允许列表,不区分大小写。点击添加条目追加一行。
浮动引用(pkg@latest、semver 范围)绝不会被硬性阻止:代理服务器会重写注册表元数据,使过于新的版本从代理的角度看根本不存在,软件包管理器则会解析到最新的被允许版本。只有对过于新版本的固定引用才会收到 451,其中说明软件包的年龄和所需的最短时间。
注意: Maven、NuGet 和 Go 模块在其标准元数据响应中不携带逐版本的时间戳,因此年龄门控目前不会阻止这三个生态系统。npm、PyPI、Cargo、RubyGems 和 Packagist 已完全覆盖(对于 PyPI,Bromure 会按需从 PyPI JSON API 查询发布时间)。
OSV 漏洞检查
当在 api.osv.dev 上查询软件包(免费,无需密钥)开启时,每个下载工件的生态系统、软件包和版本都会与 OSV 数据库进行核对——这是一个免费的聚合库,汇集了 GitHub Advisory Database、PyPI 公告、Go 的漏洞数据库、RubySec 等。任何达到或超过阻止的严重性级别阈值的公告都会以 451 阻止下载。
- 默认关闭——正如窗格所述,传递子软件包中的低严重性 CVE 不应打断工作流程。
- 阻止的严重性级别提供低及以上、中及以上、高及以上(默认)或仅严重。
- 覆盖全部八个生态系统;检查在工件下载时运行,而非在元数据拉取时。
软件包过滤
软件包过滤单选按钮用于选择信誉提供方:无、socket.dev 或 Delpi。这两个提供方的工作方式不同——socket.dev 是代理服务器在放行拉取之前查询的预检查询,而 Delpi 则完全替换 npm 注册表——因此二者互斥。切换时会保留已存储的 API 密钥,因此在提供方之间切换不会造成破坏。
socket.dev
选择 socket.dev 后,将 API 令牌粘贴到 API 密钥中(获取 API 密钥链接会打开 socket.dev 的令牌页面)。下方的两个开关在输入密钥之前始终保持禁用,而空密钥无论开关如何都会完全禁用 socket.dev。密钥仅存储在宿主机端,绝不会进入虚拟机。
- 阻止已被攻陷的软件包(恶意安装脚本、被标记为恶意软件、仿冒抢注、可疑遥测)——针对呈攻击形态的供应链风险信号进行阻止:任何严重性级别的确定性恶意软件指标;对于更嘈杂的信号,如混淆代码、安装脚本、仿冒抢注或 shell 访问,仅当 socket.dev 将其评为高或严重时才阻止(因此带有良性
postinstall的软件包不会被阻止)。默认关闭。 - 阻止存在已知 CVE 的软件包——针对达到或超过 CVE 阻止阈值(默认高及以上;该选择器在此开关开启前保持禁用)的 socket.dev 漏洞桶进行阻止。默认关闭。
注意: socket.dev 不支持 Cargo。启用 socket.dev 过滤后,每个 crates.io 工件都不会产生裁定结果,并会触发下文所述的未验证软件包保持机制——实际上相当于每个 crate 版本都会弹出一次提示。对于以 Rust 为主的工作区,请禁用 socket.dev,否则请准备好回应提示。
Delpi
选择 Delpi 并输入 API 密钥后(获取 API 密钥链接会打开 landh.tech),代理服务器会将每一个 npm 注册表请求——元数据、tarball、审计,以及所有发往 registry.npmjs.org 的请求——重新路由到 Delpi 位于 depi-npm-proxy.landh.tech 的 npm 兼容安全注册表,并附加由宿主机注入的 Bearer 令牌形式的密钥。Delpi 提供经过预先审核的软件包,因此整个注册表被替换,而不是逐软件包查询。其他层(年龄门控、OSV、脚本剥离)仍在其之上应用:Delpi 替换的是 socket.dev,而非本地策略。
当密钥字段为空时,会显示橙色警告:输入 API 密钥——没有密钥 Delpi 将保持关闭。 如果 Delpi 拒绝该密钥,npm 安装会以清晰的 Bromure 错误失败,拒绝行为会记录在安全日志中,并触发一次性警报。Delpi 仅影响 npm;其他生态系统不受影响。
安装脚本
即时从 npm tarball 中剥离 preinstall / install / postinstall / prepare 会移除恶意 npm 软件包用于在安装时运行代码的钩子。Bromure 会在传输过程中重写 tarball——从软件包的 package.json 中移除脚本键,重新计算 tar 校验和,重新 gzip 压缩——并从注册表元数据中清除完整性哈希,使得对于未固定的安装,npm 自身的验证仍能通过。剥离操作会在安全日志中以橙色记录;一旦解析失败,原始 tarball 会原样通过(此层采用故障放行策略,而非破坏一个格式良好的安装)。
确实需要安装脚本的绑定编译器(better-sqlite3、node-canvas 等)应放入允许运行安装脚本的软件包中,格式为 npm:better-sqlite3。
默认关闭;仅限 npm(PyPI sdist 绝不会被重写——setup.py 是任意代码)。
锁文件锁定安装
锁文件锁定安装(npm ci、pip --require-hashes)会在锁文件中固定 tarball 的完整性哈希,因此 Bromure 无法在不破坏验证的情况下重写这些 tarball。默认情况下,它们会未经修改且静默地通过。
开启在未经修改地放行锁文件锁定的 tarball 之前提示(npm ci、pip --require-hashes),即可改为收到询问:一批中的第一个锁文件锁定拉取会弹出一个宿主机对话框,标题为 Pass through npm ci (lockfile-pinned install) from workspace "<name>"?,选项为允许 15 分钟、允许一次、在本次会话剩余时间内允许以及不允许。整批都遵循该决定(并发拉取会合并到一个提示上);拒绝会以 451 阻止,并会记住 60 秒。
注意: 尽管标签提到了
pip --require-hashes,但目前仅针对 npm 的npm ci实现了检测——pip 哈希固定安装不会触发提示。
当信誉来源不可达时
如果某个已启用的信誉来源(OSV 或 socket.dev)无法产生裁定结果——网络中断、被限流、认证失败、生态系统不受支持——Bromure 会采用故障关闭策略,而非静默放行。下载会被保持,并会针对每个软件包和版本弹出对话框询问是否接受该未验证的软件包;拒绝会以 451 阻止。批准以每个 package@version 为键,因此一次回答无法覆盖整个依赖图。
这是针对攻击者诱发限流以偷渡软件包的刻意保护。它也意味着在启用 OSV 或 socket.dev 的情况下完全离线工作会对每个未缓存的软件包弹出提示——离线使用时请禁用这些层。在无头的 SSH/CLI 会话中,同样的问题会在工作区的 tmux 内提出;不回答即表示拒绝。
设置参考
| 设置 | 类型 | 默认值 |
|---|---|---|
| 拒绝比截止时间更新的软件包 | 开关 | 开 |
| 最短年龄 | 步进器,0–90 天 | 2 天 |
| 豁免软件包 | 列表(npm:axios 或 axios) | 空 |
| 在 api.osv.dev 上查询软件包(免费,无需密钥) | 开关 | 关 |
| 阻止的严重性级别(OSV) | 选择器:低 / 中 / 高及以上、仅严重 | 高及以上 |
| 软件包过滤 | 单选:无 / socket.dev / Delpi | 无 |
| API 密钥(socket.dev) | 安全字段 | 空 |
| 阻止已被攻陷的软件包 | 开关(需要 socket.dev 密钥) | 关 |
| 阻止存在已知 CVE 的软件包 | 开关(需要 socket.dev 密钥) | 关 |
| CVE 阻止阈值 | 选择器(随 CVE 开关启用) | 高及以上 |
| API 密钥(Delpi) | 安全字段 | 空(没有密钥 Delpi 保持关闭) |
| 即时从 npm tarball 中剥离 preinstall / install / postinstall / prepare | 开关 | 关 |
| 允许运行安装脚本的软件包 | 列表(npm:better-sqlite3) | 空 |
| 在未经修改地放行锁文件锁定的 tarball 之前提示 | 开关 | 关 |
在此处输入的 API 密钥保存在宿主机上工作区的 profile.json 中(绝不在虚拟机内);所有信誉调用都源自宿主机代理服务器。有关安全日志、逐生态系统的拦截细节以及与这些策略配套的攻陷检测警报,请参阅供应链保护章节。