OSV-Scanner 很容易被描述成一款漏洞 scanner。这个说法准确,范围却宽到难以形成架构判断。更精确的理解,是把它看作一条两阶段证据流水线:先从眼前的软件表面提取软件包事实,再把这些事实与用于描述受影响软件包版本或 commit range 的漏洞记录进行匹配。[1][5] 到 2026 年,它的重要性在于,很多团队已经不缺又一个声称能抽象判断风险的 dashboard。他们需要的是一种可靠方法,把 lockfile、SBOM、源码树或容器镜像连到已知开源漏洞数据,同时保留每一条发现来自哪里。

这个证据边界,是理解该项目的有效入口。OSV-Scanner 不是一整套应用安全计划,也不适合按这个尺度来评价。它不能判断每一种语言里的易受攻击函数是否都可达,不能判断某条 exploit path 是否暴露在生产环境中,也不能判断某个业务例外是否合理。它能保持第一个问题的清晰度:哪些软件包、版本、commits 和 artifacts 存在,哪些 OSV 记录与它们匹配?[1][2][5]

图片语境:题图是一张来自 Wikimedia Commons 的真实数据中心服务器机柜照片,不是生成式安全图形或示意图。这个选择有意对应 OSV-Scanner 的架构重心:它处理的是运维证据。漏洞数据只有连到真实穿过构建与运行期基础设施的软件清单之后,才开始具备用途。[8]

核心分界是先提取,再判断

官方使用文档把模型说得很直接:OSV-Scanner 先从项目、容器镜像或其他目标中提取软件包信息,再把提取出的软件包信息与已知漏洞数据库匹配。[1] 这听起来显而易见,直到把它放进漏洞扫描常见失效场景里比较。团队直接跳到严重性计数,然后才发现输入是一份过期 manifest、一份不完整 SBOM、一个与生产不同的镜像,或者一个在不同 registry 假设下解析出来的传递依赖图。

OSV-Scanner 的设计把讨论拉回输入质量。源码扫描不同于镜像扫描,因为证据不同。受支持 artifact 文档明确划出这一点:扫描源码时,scanner 会寻找 lockfile 和 manifest;扫描容器时,已经安装的 artifact 比文件系统里散落的 lockfile 更重要。[2] 这是恰当的架构边界。package-lock.json 告诉你一个项目打算解析出什么。容器层告诉你实际进入镜像的是什么。把两者当作同一种证据,会很方便,也会出错。

这也解释了 OSV-Scanner 为什么通过 OSV-Scalibr 集成提取逻辑,并暴露一份范围很广但结构化的受支持输入列表。源码扫描覆盖 Go、JavaScript、Python、Rust、Ruby、PHP、.NET、Java、Dart、Elixir、Haskell、R 以及其他生态中的 lockfile 和 manifest;镜像扫描能够提取已安装的 OS packages,以及 Go binaries、Java uber jars、Node modules、Python wheels 这类语言 artifact。[2][6] 重点不在于每个生态都已经被永久解决。重点在于 scanner 的架构从一开始就在询问:它正在看的 artifact 属于哪一种。

OSV 的数据库形态也是 scanner 架构的一部分

OSV-Scanner 与 OSV 数据库模型紧密耦合。OSV 把自己描述为使用 OSV schema 聚合漏洞数据库的系统,来源包括 GitHub Security Advisories、PyPA、RustSec、Global Security Database 以及其他数据源。[5] 它的公开 API 可以按 commit hash 或软件包版本查询已知漏洞。[5] OSV 介绍还给出了架构层面的理由:OSV 旨在帮助开发者识别会造成真实风险的已知第三方依赖漏洞,同时其基础设施通过 bisection 和版本分析,努力准确表达受影响版本。[5]

这正是 OSV-Scanner 能够比通用 CVE 字符串匹配更精确的原因。OSV schema 由 OpenSSF Vulnerability Disclosures Working Group 维护,其目标是以机器可读形式表达开源漏洞数据,并映射到软件包生态和受影响范围。[7] 对 scanner 用户来说,这个治理细节很重要。漏洞记录不只是一个标题。它是一条结构化声明,说明哪个软件包、版本范围或 commit range 受影响。

C/C++ 支持展示了这个模型的价值,也展示了它的边界。由于 C 和 C++ 往往缺少单一中心化 package-manager 真相,OSV-Scanner 可以对 submoduled 或 vendored dependencies 使用 OSV 的 commit-level 数据,也可以使用 OSV 的 determineversion API,为那些没有保留 Git histories 的 copied-in dependencies 估算版本。[2] 这是有力的设计选择,因为它尊重 C/C++ 依赖在现实中被嵌入的方式。同时,这也要求谨慎:同一份文档提醒,commit-level 数据覆盖 OSV 中大多数 C/C++ 漏洞,但部分漏洞仍会被排除在结果之外。[2] 也就是说,更好的证据不会自动变成完美证据。

输出应保留出处,而不只是统计发现数量

scanner 的输出设计继续强化同一项原则。对于每个漏洞,OSV-Scanner 会报告 OSV URL、severity score、ecosystem、package、version、可用时的 fixed version,以及软件包来源路径。[3] 对容器层扫描而言,它还可以报告软件包被引入的 layer、layer history 与 commands、base images,以及 OS 或 distro 信息。[3] 这些字段不是装饰。它们让一条发现具备可复核性。

一场以“我们有 47 个 highs”开头的分诊会,通常已经太晚,也太含混。更好的分诊问题应该更窄:来自哪个 lockfile、镜像 layer 或 SBOM 的哪个 package version 触发了哪条 OSV 记录,是否已有 fixed version?[3] OSV-Scanner 的用处,就在于让这条链保持可见。若团队把输出压平成一个红色数字,随后丢失能让 owner 修复问题的 source path,它的价值会明显下降。

调用分析是最值得关注的分诊层,因为它试图区分易受攻击的软件包和易受攻击的代码路径。在 table output 中,启用 --call-analysis=<lang> 后,OSV-Scanner 可以把 findings 分成两类:影响项目所调用代码的漏洞,以及影响项目未调用代码路径的漏洞。[3] 未调用这一类并不自动等于安全。可达性分析具有语言特异性,也不完整,还会受到运行期行为影响。但它确实在“存在于依赖树中”和“已知在这里可被利用”之间增加了一个重要中间层。

成熟的漏洞管理正是在这个中间层展开。存在性应当触发关注。可达性应当收紧优先级。生产暴露、补偿性控制和 exploit 成熟度,仍应由人和策略继续评估。OSV-Scanner 可以支撑这条顺序,不能替代它。

配置是策略开始具备可审计性的地方

OSV-Scanner 的 osv-scanner.toml 支持容易被忽略,但它是这套架构中实用的控制表面之一。配置文档允许忽略漏洞 ID,提供可选的 expiry dates、reasons,以及 package overrides;后者可以忽略 packages、忽略 vulnerabilities、忽略或覆盖 licenses,并设置 effective-until dates。[4] 这正是一款训练人们一路点击跳过噪声的 scanner,与一款能够承载可复核例外的 scanner 之间的差别。

重要规则是,ignores 应当被当作策略记录,而不是本地静音。带有理由和到期日的例外可以被复核。永久且没有理由的 ignore,只是被隐藏起来的债务。OSV-Scanner 给了团队足够的结构,让这一区分可见,但它无法强制形成治理习惯。这属于仓库 owner 的责任。

GitHub Action 的形态也指向同一方向。官方文档提供了一种 pull-request workflow,会比较 target branch scan 与 feature branch scan,并报告 PR 引入的新漏洞;同时也提供 full scheduled scan,可以在 push 或固定 schedule 上运行。[9] 这种分工是健康的。PR scanning 用于阻止新的已知漏洞流入。Scheduled scanning 用于捕捉依赖已经落地后发生的 advisory drift。把两者当作同一种控制,会制造虚假的信心。

修复建议只有在风险显式时才真正有用

OSV-Scanner 的 guided remediation 功能很强,因为它从检测推进到变更规划。文档把 osv-scanner fix 描述为一项实验性功能,可以分析完整传递图,按修复的传递漏洞数量来优先排序直接依赖升级,同时衡量 severity 与 dependency depth,并为受支持生态修改 manifests 或 lockfiles。[10] 当漏洞队列很长时,团队需要的正是这类辅助。

与这个功能绑定的警告同样重要。文档说明,在不受信任的项目上使用 guided remediation 会带来风险,因为它会触发 package-manager 行为、scripts,或项目指定的外部 registries。[1][10] 这条警告不应被当作样板文字。它定义了 scanner 输出与 build-system execution 之间的边界。一个读取证据的命令是一回事。一个运行 package manager 并重写依赖状态的命令,是另一回事。

实际采用模式由这条边界推出。围绕证据,早运行、频繁运行 OSV-Scanner。信号清晰时,用 PR checks 阻止新引入的已知漏洞。用 scheduled scans 捕捉新披露的问题。用配置文件让例外具备可审计性。在受信任仓库中使用 guided remediation,并确保 review、tests 和 package-manager 行为都处在理解范围内。

OSV-Scanner 最好的架构主张朴素而耐久:漏洞扫描应贴近依赖证据,输出应保留足够出处,让人和 policy engines 能继续做下一步决策。这不是完整安全计划。它是一套完整计划所需要的前置层;有了这一层,严重性、可达性、所有权和修复才有精确含义。

来源

  1. OSV-Scanner documentation, "Usage" - 核心 package-extraction 与 vulnerability-matching 模型、scan subcommands、output flags、offline matching、license scanning,以及 guided-remediation 警告。
  2. OSV-Scanner documentation, "Supported Artifacts and Manifests" - source 与 container 证据边界、受支持 lockfiles 和 artifacts、C/C++ commit scanning、vendored dependency 处理,以及 transitive Maven scanning。
  3. OSV-Scanner documentation, "Output" - reported fields、container layer output、formats、SARIF details,以及 call-analysis output behavior。
  4. OSV-Scanner documentation, "Configuration" - osv-scanner.toml、ignored vulnerability IDs、reasons、expiry dates,以及 package override controls。
  5. OSV.dev, "Open Source Vulnerabilities" and OSV introduction - database aggregation、OSV schema use、API queries by commit hash or package version,以及 affected-version accuracy goals。
  6. Google Security Blog, "Announcing OSV-Scanner V2" - OSV-Scalibr integration、expanded extraction support、layer-aware container scanning,以及 local HTML output。
  7. OpenSSF, "OSV Schema" - OpenSSF Vulnerability Disclosures Working Group 对 OSV schema 和 reference tooling 的维护。
  8. Wikimedia Commons, "Datacenter Server Racks (22370909788).jpg" - Carl Lender 拍摄的真实数据中心机柜照片,题图来源页。
  9. OSV-Scanner documentation, "GitHub Action" - pull-request scanning、scheduled scanning、release scanning,以及 workflow behavior。
  10. OSV-Scanner documentation, "Guided Remediation" - 实验性 fix command、graph analysis、prioritization signals、manifest 与 lockfile changes,以及 supported remediation strategies。