NemoClaw 安全预警与审计溯源:基于 GitHub Security Advisory 的 npm 漏洞提前预警机制
【免费下载链接】NemoClawRun agents like Hermes, LangChain Deep Agents, and OpenClaw more securely inside NVIDIA OpenShell with managed inference项目地址: https://gitcode.com/gh_mirrors/ne/NemoClaw
关联文档:docs/security/advisory-early-warning.md
导读
NemoClaw 的 npm 供应链审计采用「Reviewed Feed(已审核生态记录)」作为权威执行边界,但上游公开的安全公告往往比该记录提前数周发布,存在明显的时间盲区。本文基于 docs/security/advisory-early-warning.md 完整讲解 NemoClaw 的Advisory Early Warning(安全公告早预警)机制:如何将 GitHub Security Advisory 与已审核 npm 库存进行离线关联、如何通过 NVD 进行补充对账、以及每一次 npm audit 如何通过 provenance sidecar 固化可验证的时间线与审计身份。读完本文,你将掌握该机制的信号模型、CLI 用法、置信度编码规则与审计溯源结构,并能独立复现其扫描流程。
为什么需要「早预警」:Reviewed Feed 的时间盲区
npm audit执行的是全局「reviewed ecosystem record(已审核生态记录)」——即经过审核、映射到具体 npm 包与版本范围的记录。问题在于,这条记录往往明显滞后于上游公告:
- 以
fast-uri(GHSA-4c8g-83qw-93j6)为例:上游仓库公告在6 月 29 日发布,而全局 reviewed 记录直到7 月 21 日才传播到位; - 同一易受攻击版本在18:46 UTC审计结果为干净(clean),却在20:09 UTC被报告为 High。
这意味着仅依赖npm audit存在一个可达数周的真实检测空窗。NemoClaw 的 early-warning 关联模块正是用于缩小这个时间差:它把上游更早的公开信号变成可追踪、非阻塞的调查提示(investigation prompt),同时为每次审计记录固化 provenance(溯源证据),使这些时间线可以被留存产物证明。
三类全局公告记录
关联逻辑同时消费全球公告数据库中的三种记录类型(见 scripts/lib/advisory-early-warning.mts 顶部注释):
| 类型 | 来源 | 语义与处理路径 |
|---|---|---|
| Reviewed records | npm audit强制执行的语料 | 命中即表示包级强制即将或正在生效,信号确认 reviewed gate 能够检测到它 |
| Unreviewed records | NVD | 通常早于 curated feed 出现,但缺少已验证的 npm 映射,走 ambiguous / informational 路径,提供更早的提示 |
| Malware records | npm 恶意包公告 | 与被审核库存命中时按普通记录关联,保持非阻塞 |
直接轮询上游仓库级公告(如fastify/fast-uri的仓库公告)是最早的公开信号来源,但需要「包到仓库」的映射表;这是文档明确标注的计划扩展,而关联模块已经按同一记录形状(repository-level 与 global 记录共享结构)兼容接收。
早预警关联的工作原理
核心模块与信号结构
scripts/lib/advisory-early-warning.mts 将 GitHub Security Advisory JSON 与已审核 npm 库存关联,输出结构化信号:
{ advisoryId: string; // GHSA ID,如 GHSA-4c8g-83qw-93j6 cveId?: string; // 仅当公告带格式合法的 cve_id 时出现 package: string; // npm 包名 vulnerableRange: string; // 易受攻击版本范围 matchedVersions: string[]; // 命中库存中的具体版本 source: "upstream-ghsa"; confidence: "exact" | "ambiguous"; action: "investigate" | "informational"; }源码中的AdvisorySignal类型(advisory-early-warning.mts)定义了这一结构。可选的cveId只用于下面介绍的 NVD 补充对账,绝不参与关联判定。
库存(Inventory)从哪来
默认库存来自 ci/reviewed-npm-audit.json:
- archivePackages:每个已提交的归档包 spec(例如
openclaw@2026.9.1、@zed-industries/codex-acp@0.11.1、@tencent-weixin/openclaw-weixin@2.4.3等); - lockedGraphs:各锁定图(openclaw-runtime、mcporter-runtime、wechat-runtime、mcp-tool-discovery-runtime)的
package-lock.json中记录的已安装包身份。
从源码看(advisory-early-warning-scan.mts),CLI 会从配置中读取lockedGraphs数组,逐个拼接<directory>/package-lock.json并解析其中所有node_modules/...条目。两个解析函数分工明确:
parseInventoryFromAuditConfig:解析archivePackages的packageSpec(name@version格式);parseInventoryFromPackageLock:解析 lockfile v3 的packages表,且优先采用条目中的真实name字段——因为别名安装(npm install alias@npm:real-name)路径是别名、但公告按真实包名引用,这样处理才能命中(见 advisory-early-warning.mts)。
可离线覆盖:通过--inventory <file>传入显式的{name, version}数组,用于密闭(hermetic)离线运行。注意:格式错误的条目会直接导致运行失败,而不是被静默跳过——静默缩减库存会在无任何信号提示的情况下漏报公告(advisory-early-warning-scan.mts)。
置信度是「编码」而非「推断」
置信度不是启发式推断,而是严格的规则编码(见 advisory-early-warning.mts 的correlateAdvisories):
- 仅当npm 生态 + 包名 + 可解析的语义化版本范围三者同时命中,才产生
confidence: "exact"与action: "investigate"; - 非 npm 生态(CPE 派生)记录造成的包名碰撞、以及无法解析的版本范围,产生
confidence: "ambiguous"与action: "informational"; - Ambiguous 匹配永不阻塞或变更发布流程;
- 命中版本被证明在范围之外、或包不在库存中、或公告对象格式非法时,不产生任何信号;
- 同一公告与包的 exact / ambiguous 证据分别追踪:exact 信号只携带证明性范围与已验证的命中版本,ambiguous 证据永远不会升级为 exact。
版本判定采用公告常用的逗号分隔比较器子集(如>= 3.0.0, < 3.1.3),比较器按 AND 语义求值:任一可解析比较器为假即证明版本在范围外,即使存在不可解析的兄弟比较器;只有版本无法解析或没有可解析比较器能判定时才返回「未知」,调用方必须将其视为 ambiguous 而非确认命中(advisory-early-warning.mts)。
权威边界:谁在真正把关
npm auditgate(scripts/audit-reviewed-npm-graph.mts)在 CI 中始终保持启用,它才是 npm 包与版本范围决策的权威来源;early-warning 路径只触发调查与重扫(investigation and rescanning),绝不会替代 gate。这一边界同样写在模块头注释中:「No output of this function may block or mutate a release」。
扫描 CLI 的使用方法
scripts/advisory-early-warning-scan.mts 是该模块的 CLI 入口,只读取本地文件,无论是否发现信号都以退出码 0 结束,不修改任何输入文件或外部状态。通过--output可将信号写入本地文件:
# 列出库存中的包名(每行一个),这是公告查询的输入 node scripts/advisory-early-warning-scan.mts \ --list-packages # 将已抓取的公告记录与库存关联,输出信号 node scripts/advisory-early-warning-scan.mts \ --advisories advisories.json --output signals.json # 附加 NVD 补充对账(文件来自调用方预先抓取的 NVD 2.0 响应) node scripts/advisory-early-warning-scan.mts \ --advisories advisories.json \ --nvd-records nvd-responses.json \ --output signals.json # 离线密闭运行:用显式库存替换仓库派生的库存 node scripts/advisory-early-warning-scan.mts \ --inventory inventory.json --advisories advisories.json --output signals.json从 CLI 实现看(advisory-early-warning-scan.mts)的几个关键行为:
--list-packages模式只输出去重排序后的包名列表,直接返回;- 缺少
--advisories时打印 usage 并报错; - 无
--output时信号仅打印到 stdout(格式如GHSA-xxxx package range -> action (confidence, matched v1, v2)),带 NVD 对账时附加— NVD: corroborated (published ...)描述; - CLI 自身永远不做网络请求——公告与 NVD 响应都由调用方(未来调度工作流)抓取后以文件传入。
公告数据来源于 GitHub 的/advisoriesAPI:请求覆盖全部三种类型、使用分页,并按库存包名批次过滤affects=参数。
NVD 补充对账(Supplementary Reconciliation)
带 CVE ID 的信号可以在services.nvd.nist.gov/rest/json/cves/2.0进行对账。NVD 仅是补充来源:issue #7338 明确禁止把含混的 NVD 或 CPE 匹配当作权威 npm 映射,对账是纯信息性的,永远不改变信号的action或confidence。
scripts/lib/nvd-reconciliation.mts 解析 NVD 2.0 API 响应,记录 CVE ID、vulnStatus、发布日期与修改日期,以及被标记为 vulnerable 的 CPE 标准;然后为每个信号标注三种一致状态之一:
| 状态 | 含义 |
|---|---|
corroborated | NVD 列出相同 CVE ID 且未拒绝(vulnStatus非 rejected),说明 NVD 独立佐证该 CVE |
nvd-missing | NVD 无记录——典型于 CVE 处于保留(reserved)状态或等待 NVD 处理期间,此时更早的上游信号依然有效 |
nvd-divergent | NVD 拒绝了该 CVE ID,或返回了不同的记录;此时应调查分歧而非丢弃信号 |
实现要点(对应 nvd-reconciliation.mts 的reconcileSignalWithNvd):
deriveCveId使用CVE-\d{4}-\d{4,}模式校验,只有格式合法的 CVE ID 才会发起对账;- CPE 标准只以数量形式出现在 note 中(
X vulnerable CPE criteria on file),绝不会被当作包匹配; - NVD 对保留 ID 返回的「格式合法空响应」解析为
null,统一按nvd-missing处理; attachNvdReconciliations是纯增量操作:无 CVE ID 的信号原样透传,重复的 CVE ID 首个可解析者生效。
每次审计的 Provenance 溯源
Sidecar 结构与缓存复用条件
每次 npm audit 报告都附带一个*.provenance.jsonsidecar,覆盖coverage/reviewed-npm-audit/目录下的审计产物,以及 WeChat 锁定运行时图审计的npm-audit.provenance.json。从 scripts/lib/reviewed-npm-audit.mts 的类型定义看,sidecar 至少包含run(startedAt/finishedAt)、rawReportPath、advisoryIds等字段。
缓存复用仅在以下条件全部匹配时发生:
- 包与 lock 的字节内容一致;
- 钉死的 npm 身份一致(版本、SHA-512 SRI、归档 SHA-256);
- 固定的 Yarn audit registry 源一致;
- 命令参数一致;
- parser 身份一致。
镜像构建只接受schema version 2的 receipt,且必须绑定相同的完整 npm 身份与固定 registry(对应 scripts/audit-reviewed-npm-graph.mts 中parseAuditConfig对schemaVersion !== 2的强制校验)。
Sidecar 记录的内容清单
每次 sidecar 记录:
- Scanner 身份:
npm audit本体、精确的 npm 版本及验证过的归档完整性、Node.js 版本; - Receipt schema version 2:把审计结果绑定到该 npm 版本与完整性,消费者可以拒绝混合或未验证的 scanner 身份;
- 固定 Yarn audit registry,以及 npm 用于提交依赖图的派生 bulk advisory endpoint;npm 7 及以上没有 quick-audit fallback,请求失败时 npm 不报告任何公告数据,sidecar 会记录这一条件;
- 运行起止时间戳(ISO 8601 格式);
- 审计图标签(graph label)与提交的包 spec;
- 机器可读报告的原始路径
rawReportPath(按约定为相对 sidecar 所在目录的路径); - 从报告中提取的 GHSA advisory ID 列表(
advisoryIds); failure标记:审计尝试失败时 sidecar 仍记录该尝试,便于审计。
用连续运行定位「首次检测」
对比连续保留运行的advisoryIds,可以确定新浮现公告的最后一次可比较未检出与第一次检出。即便某次早期运行因无关发现失败,这种对比依然可行——这正是下面事后分析得以成立的基础。
#7276 事后分析:四种检测触发分类
issue #7338 对 #7276 的证据提出两个问题,答案只依赖保留证据并继承其局限;证据无法支撑「单一普适的 feed 延迟根因」结论,无法被证据证明的发现归类为「unproven(未证明)」而非「归因」。
Q1:检测触发是什么
| 发现 | 分类 | 证据 |
|---|---|---|
fast-uri(CVE-2026-13676, GHSA-4c8g-83qw-93j6) | Reviewed-mapping delay,直接证明 | 上游仓库公告 6 月 29 日已存在,但 7 月 21 日 18:46 UTC 的npm audit未报告fast-uri@3.1.2;全局 reviewed 记录 19:03 UTC 传播;20:09 UTC 同一版本审计返回 High。前后对照证明是 reviewed 包映射传播触发了检测 |
@opentelemetry/core(CVE-2026-54285, GHSA-8988-4f7v-96qf) | 审计或重扫覆盖缺口 | 其 reviewed 记录 6 月 15 日就已存在(早于检测一个多月),feed 延迟无法解释;它直到 7 月 21 日构建进入插件审计才首次浮现,说明审计覆盖或执行顺序存在缺口 |
| Jaeger propagator(CVE-2026-59892, GHSA-45rx-2jwx-cxfr) | 与 reviewed-mapping delay 一致,但未证明 | reviewed 记录 7 月 21 日 19:07 UTC 出现,首个到达该图的插件审计 20:26 UTC 报告;序列一致,但更早构建在插件审计前就停止了,缺少受控的前后对比 |
tar(CVE-2026-59873, GHSA-23hp-3jrh-7fpw) | 证据缺失,未证明 | 6 月 27 日上游披露到检测的差距真实存在;7 月 21 日 Trivy 扫描报告易受攻击的tar@7.5.11与7.5.15,reviewed 记录日期为 7 月 20 日;但未保留可比较的前置扫描,触发机制无法证明 |
Q2:理想触发与当前覆盖
理想触发是:按与任何一次构建进度无关的调度,以最早的上游公开披露为信号,对依赖库存求值。将每个已证明缺口映射到机制:
- Reviewed-mapping delay(
fast-uri,Jaeger 传播器也疑似):关联路径从提供的公告文件读取 unreviewed NVD 源记录(与 reviewed、malware 记录并列),也读取通过--nvd-records提供的预抓取 NVD 响应;计划中的调度工作流将在 #7338 sign-off 后抓取 NVD 记录并传给 CLI,每六小时运行一次——披露了库存包名的公告在 reviewed 映射存在之前就会产生信号,NVD 对账提供补充佐证;直接轮询上游仓库公告尚未实现,这是需要包到仓库映射表的计划扩展; - 审计/重扫覆盖缺口(
@opentelemetry/core及 Jaeger 结论的局限):计划中的调度扫描每六小时将每种公告类型与完整 reviewed 库存关联,与构建执行顺序无关;重扫已维护的不可变镜像摘要尚未实现,镜像扫描管线等待 #7338 所需的支持镜像范围定义; - 未证明触发(
tar):任何触发设计都无法恢复缺失的证据;现在每次 npm audit 都写入带端点、时间戳与 advisory ID 的 provenance sidecar,连续保留运行即可为未来发现建立「最后一次可比较未检出」与「第一次检出」。
验证与测试
该机制的单元测试位于 test/automation/pull-requests/advisory-early-warning.test.ts 与 test/automation/releases/nvd-reconciliation.test.ts,覆盖的关键行为包括:
- 上游公告命中受影响库存条目时输出
confidence: "exact"/action: "investigate"; - CPE 派生的非 npm 记录保持 informational 而非阻塞;
- 不可解析的 npm 范围视为 ambiguous 而非阻塞;
- 包不在库存、或库存版本在易受攻击范围之外时不产生信号;
- 同一公告与包的重复匹配合并为单个信号;
- 格式非法的公告(GHSA ID 不合法)被拒绝且不抛异常;
- 库存解析:归档包 spec、lockfile v3 已安装包、别名条目按真实包名入库、格式错误的配置条目被跳过。
当前状态与后续路线(诚实边界)
按文档标注的 Status,该功能当前的完成度是:
- 已实现:correlation 模块、扫描 CLI、审计 provenance;
- 未实现:定时调度运行与响应策略(alert 路由)是独立的后续工作,由 issue #7338 门控——需要产品与安全负责人依据 #7276 证据签核支持的历史镜像范围、重扫归属、告警目的地与响应预期后,才新增每六小时的调度工作流。
这保证了本文描述的每一个「已实现」能力都有仓库代码与测试作为依据,而每一项「计划中」的能力都被明确标注为后续扩展。
【免费下载链接】NemoClawRun agents like Hermes, LangChain Deep Agents, and OpenClaw more securely inside NVIDIA OpenShell with managed inference项目地址: https://gitcode.com/gh_mirrors/ne/NemoClaw
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考