漏洞扫描器匹配误差
漏洞扫描器的命中不是“这个资产已被证明可利用”,而是一个有时间截面的关联判断:扫描器把当时观察到的制品,经过组件识别、坐标与版本归一化,再与某一版漏洞情报和适用性规则连接起来。任何一层发生遗漏、误识别或过度近似,都可能把不适用的漏洞报成命中,也可能漏掉真实受影响的组件。
因此,误报与漏报不能脱离“真值单位”讨论。最低限度应固定:制品 digest 或精确 build、组件坐标与来源生态、二进制/源码包版本、漏洞或公告 ID、情报数据库快照、扫描器版本与配置,以及要判断的状态。工具之间有分歧只说明实现和数据链不同,不能把多数票当真值;一次“零发现”也不等于软件没有漏洞。
分层误差模型
| 层 | 扫描器实际在回答什么 | 典型误报来源 | 典型漏报来源 |
|---|---|---|---|
| 采集与范围 | 哪些文件、镜像层、锁文件或已安装包进入了扫描? | 旧层、重复制品或开发依赖被当成当前运行资产 | 未解压层、私有依赖、无权限目录或不支持的包类型未进入 inventory |
| 组件识别 | 文件和元数据属于哪个组件? | 名称碰撞、同一组件重复计数、系统包与语言包混淆 | 重打包、重命名、去元数据、vendoring 或静态链接使组件不可见 |
| 身份归一化 | 这个名称对应哪个生态、vendor、product 与 package? | 用宽泛 CPE 把同名包连到另一产品,例如语言包名称与系统产品撞名 | 缺少可匹配的 CPE、PURL 或发行版坐标,或 source/binary package 映射缺失 |
| 版本语义 | 当前版本是否落入受影响区间? | 忽略 epoch、发行版 revision、预发布语义或 vendor backport,只按上游版本阈值判断 | 错误比较器、区间端点、别名或“版本未知时跳过”使受影响版本未命中 |
| 情报与时间 | 此漏洞记录在该数据快照中如何描述? | 撤回、别名重复、过时受影响范围或多个 feed 重复并入 | 上游/供应商公告延迟、数据源覆盖不足、修改尚未同步或数据库陈旧 |
| 产品适用性 | 该供应商构建、平台、架构和配置是否真的受影响? | 已 backport 修复却版本号未升到上游修复版;可选模块未编译仍被计入 | 供应商 feed 缺失、受影响配置未被建模、补丁被下游重新引入 |
| 可达性与处置上下文 | 漏洞代码能否在此产品中执行或被攻击者控制? | 只按“依赖存在”报告,未考虑调用图、构建开关、部署路径和缓解措施 | 不健全的可达性分析或错误、陈旧、未验证的 VEX 把真实命中压掉 |
| 聚合与政策 | 多源命中如何去重、抑制和展示? | CVE alias 未归并、同一组件多个位置重复报告 | ignore rule、严重性阈值、首次匹配优先或输出截断隐藏了发现 |
这八层不是互斥分类。例如,一个系统包可先因 PURL 缺失落入 CPE fallback,再因上游阈值忽略发行版 backport,最终表现为误报。要修复的是证据链中最早失真的环节,而不是只在报告末端加白名单。
Package URL 与 CPE 的角色也不同:PURL 用 ecosystem-aware 的 type、namespace、name、version、qualifiers、subpath 标识软件包;CPE 2.3 则把命名、名称匹配、字典与 applicability language 分层。仅有一个 CPE 名称并不等于已经证明某台资产上的配置适用,详见 CPE 与 CVE 资产适用性。
三种工具代表不同工作点
| 工具 | 官方匹配链的重点 | 可观察的取舍 | 不能据此推出 |
|---|---|---|---|
| Grype | 从 SBOM/inventory 生成或补全包坐标,先用生态和发行版 matcher,再聚合去重;无法精确识别的其他包可回退到 NVD CPE,VEX/ignore 在后处理阶段生效 | 官方明确承认 CPE fallback 既可能因同名产品误报,也可能因缺少 CPE 漏报 | Grype 命中就是 NVD、供应商与制品三方共同确认;或未命中就是安全 |
| Trivy | 系统包优先使用发行版公告,语言包使用生态数据源;precise 与 comprehensive 是不同 detection priority |
默认 precise 为降低误报可能漏掉信息不足的包;comprehensive 用更多近似提高覆盖也会增加误报。第三方或自编译包不能自动继承发行版包语义 |
两种模式中较“宽”的结果更接近真值;或供应商严重性标签等于匹配准确率 |
| OSV-Scanner | 以 OSV 的 package/ecosystem/version range 为主,并可对部分语言做 call analysis | ecosystem-aware range 可避免一部分通用版本比较错误;调用分析能缩小“存在但未调用”的噪声,但受语言、构建和分析覆盖限制 | 代码不可达已经得到完备证明;或 OSV 未收录就不存在漏洞 |
工具版本、数据库 build 和默认策略会变化,这张表描述的是 2026-08-11 所核验的公开实现边界,不是永久排名。
独立研究说明了什么
- Churakova 等比较 7 个 SBOM 漏洞扫描工具和 3 组容器镜像,发现结果一致性很低、数据库差异是最大来源;作者因为缺少 ground truth,明确没有把一致性写成 precision 或 accuracy。完整数据集中,各工具发现数从 191 到 18,680;Grype 与 Trivy 的成对 Jaccard 为 0.694。这个结果证明“工具分歧很大”,不能决定谁对谁错。
- Zhao 等在 Maven 研究中使用生成项目的已知依赖清单作为一部分真值,并覆盖 21,130 个模块。工具平均漏洞报告 F1 约 0.475;补齐特殊 Maven 语义后,依赖识别的误报和漏报分别下降 34.24% 与 6.91%,漏洞报告的误报和漏报分别下降 18.28% 与 8.72%。这直接说明构建语义会传导到漏洞匹配,但结论不能外推到所有生态。
- Dann 等针对 7,024 个企业 Java 项目发现,254 个易受攻击 class 中,222 个(87%)出现过 re-bundling、143 个(56%)出现过 re-compilation;re-packaging 仅 17 个(约 6.7%)。6 个扫描器没有任何一个覆盖全部转换。他们据此构造了含 2,505 个案例的 Achilles 漏洞扫描测试套件。这是组件身份恢复的压力测试,不是现实世界 prevalence 估计。
- 一项针对 127 个路由器固件镜像的研究把内核配置、架构和受影响文件加入版本匹配后,将朴素版本命中的 68% 分类为误报;另有 19.4% 因公告缺少文件引用而无法改进。它说明配置证据很重要,也说明补充元数据仍可能不完备;不能把该比例用于普通应用扫描器。
- Imtiaz 等对 OpenMRS 的 43 个 Maven 和 5 个 npm 项目比较 9 个 SCA 工具,发现大量非重叠发现,并建议不要只依赖单一工具。它是单一系统的案例研究,不能提供跨生态总体误报率。
独立 benchmark 的首要问题不是样本多大,而是 漏洞扫描器基准真值集 如何建立:是已知依赖清单、人工验证的制品—漏洞对、可复现触发,还是仅把另一个数据库或工具当答案。若真值仍来自同一情报源,评测只能测实现一致性,不能测端到端正确性。
单条发现的验证流程
- 冻结证据。 保存制品 digest、SBOM 生成器、scanner 版本、完整配置、数据库 build/更新时间、命令和未过滤原始输出;不要只留 UI 截图或最后一张汇总表。
- 验证 inventory。 用包管理器、锁文件、build manifest、文件哈希或镜像层反查组件确实存在,并区分运行、开发、测试和残留层。
- 验证身份。 核对生态、namespace、source/binary package、vendor/product、PURL/CPE 和 alias;同名不是同一产品,坐标缺失也不是“无漏洞”。
- 重算版本。 使用对应生态或发行版的比较器,检查 epoch、revision、prerelease、受影响区间端点;对系统包查供应商公告、OVAL 和精确 package release,不能只看上游版本号。
- 核对情报。 回到 CNA/供应商/项目公告,检查 affected/unaffected 范围、别名、修改或撤回状态和数据时间;数据库聚合记录不是独立复核。
- 核对制品适用性。 查架构、编译选项、启用模块、受影响文件/函数、部署暴露面和已有缓解。此步骤可把“组件受影响”细化成“产品是否受影响”,但不自动证明可利用或不可利用。
- 审查 VEX。 只有当产品 ID 精确、声明者及来源可追溯、时间和版本足够新、状态理由可审计、冲突优先级明确时,才消费 OpenVEX 等 VEX 声明。保留被抑制结果,避免错误或高优先级旧 VEX 造成不可见漏报。
- 复扫并验证修复。 工具中 ticket 消失只是链条的再次计算;仍需确认新制品确实安装、旧层或旧节点已移除,并保存前后快照。
证据边界
- 扫描命中可以作为调查入口和风险管理信号,不等于漏洞已在此环境可利用、已被利用、严重性很高或修复期限已经成立。
- 扫描未命中不能推出组件安全;可能是采集、识别、数据覆盖、版本语义或过滤策略的漏报。
- 供应商 backport 可以推翻“版本号低于上游修复版所以必然易受影响”的推论,但供应商声明本身也不证明补丁已部署到当前制品。
- 代码可达性比“依赖存在”更接近运行上下文,却仍受动态调用、反射、插件、配置和分析覆盖限制;不可把未找到路径写成数学意义上的不可达。
- VEX 是带作者、产品、漏洞、状态和时间的可消费声明,不是自动真值。
not_affected需要理由;来源、签名、更新顺序和冲突处理会直接影响漏报风险。 - CVSS、EPSS、CISA Known Exploited Vulnerabilities Catalog 或 National Vulnerability Database 的字段解决的是严重性、概率、已知利用或数据聚合等不同问题,不能替代制品级匹配验证。
相关页面
- PyPI pytorch 与 torch 包名错配:名称与生态坐标错误造成供应链判断偏差的实例。
- Linux 内核 stable 与 LTS 维护:回移修复与上游版本号不能简单等同。
- 漏洞扫描器基准真值集、Achilles 漏洞扫描测试套件:后续研究入口。
- Grype、Trivy、OSV-Scanner:工具级实现与版本变化入口。
证据
- 原始资料快照(本地归档)
- Red Hat:Backporting Security Fixes
- NIST:CPE 2.3 specification family
- ECMA-427:Package URL
- OpenVEX specification
- Grype architecture
- Trivy vulnerability scanning
- OSV-Scanner documentation
- Churakova et al.:Vexed by VEX tools
- Zhao et al.:Software Composition Analysis for Vulnerability Detection: An Empirical Study on Java Projects
- Dann et al.:Identifying Challenges for OSS Vulnerability Scanners
基准真值的两种补充设计
- UBCIS 漏洞扫描基准 以 Trivy、Anchore、Clair 和一个 binary scanner 的并集生成候选,再人工核对包/版本、发行版适用性、backport 与 feed 冲突。CSET 2020 论文裁决 146 个候选,并用 relaxed/paranoid 两套边界处理歧义。它比纯重合度更接近 reference set,但所有工具共同漏掉的漏洞不可能进入候选,且只覆盖三张历史基础镜像的 OS packages;不能称完整 gold standard。
- SVS-TEST 漏洞扫描测试集 预先构造 16 个 CycloneDX 1.6 测试用例,覆盖 8 类场景,分别压力测试 CPE/PURL、缺版本、identifier priority、缺 identifier、非 canonical PURL、VEX 和非法 BOM。它能复现特定输入条件下的 pass、warning 与 silent failure,却不能估计这些条件在生产环境中的总体发生率,也不能把旧版本工具结果写成当前排名。
实现级校正
- NVD 的 configurations 是带 AND、OR 与少量 negate 的层级适用性表达式;单个 cpeMatch 还有 vulnerable 布尔值。把命中的 CPE 字符串或 vulnerable=true 从配置树中抽出来,不能单独证明产品受影响。Awaiting/Undergoing Enrichment 或 Rejected 记录还可能没有 configurations,缺字段也不等于不受影响。
- PURL 的 version 是不透明字符串,不是跨生态版本排序标准;type、namespace 与 qualifiers 都带生态语义。版本范围应回到 OSV schema 等数据格式和具体生态 comparator。OSV 的核心 package key 是 ecosystem + name;SEMVER、ECOSYSTEM 与 GIT range 分别需要 SemVer、生态排序或 commit graph,不能用通用字典序替代。
- Grype 的 CPE/NVD 是生态或发行版 matcher 不适用时的 fallback,并用 explicit unaffected 记录抵消部分 disclosure;不能写成直接逐节点执行原始 NVD configuration tree。
- Trivy 默认用发行版和语言生态 advisory 匹配;NVD 在当前官方文档中主要承担 severity fallback,不是默认的发行版包 CPE matcher。
- OSV-Scanner 当前查询的是规范化 name + ecosystem + version,GIT 场景优先 commit;在线与离线 matcher 的缺版本和 range 行为不同。当前 CLI 文档没有 OpenVEX/CSAF 文档输入能力,不能因其依赖库内部存在 vex/filter 就写成支持消费 VEX。