Hotdry.

Article

用 OSV.dev 漏洞库实现 Go 依赖项漏洞扫描:锁文件解析与 SPDX 输出实战

深入解析 osv-scanner 的架构设计,涵盖 Go 模块锁文件解析、SPDX 格式支持与供应链安全审计的工程化参数配置。

2026-04-24security

在软件供应链安全领域,依赖项漏洞扫描是防御侧的关键环节。Google 主导的 osv-scanner 以 Go 语言编写,直接对接 OSV.dev 开放漏洞数据库,为开发者提供了无需自建后端即可实现的自动化供应链安全审计能力。本文从架构设计出发,详细解析锁文件解析机制、SPDX 兼容输出以及可落地到 CI/CD 管道的工程化参数。

osv-scanner 的核心定位与数据流设计

osv-scanner 定位为面向开源生态的通用漏洞扫描工具,其设计理念是将多种依赖声明格式统一转换为 OSV 数据库可查询的格式。整体数据流包含三个核心阶段:首先是依赖信息提取阶段,扫描器通过 OSV-Scalibr 库解析项目中的锁文件或已安装的软件包;其次是漏洞查询阶段,将提取到的包名与版本映射到 OSV.dev 的漏洞记录;最后是结果聚合阶段,按照漏洞 ID 别名(如 CVE、GHSA、OSV 编号)进行分组输出。

这种设计的优势在于解耦了依赖解析与漏洞查询两个环节。依赖解析由 OSV-Scalibr 负责支持二十余种锁文件格式,而漏洞查询则统一通过 OSV API 完成。对于 Go 项目而言,最直接的场景是扫描 go.mod 文件,工具会自动提取所有直接依赖的模块路径与版本号,随后向 OSV 发送批量查询请求。值得注意的是,osv-scanner 在查询时会自动处理 Go 模块的命名空间问题,例如将 github.com/user/repo 正确映射到 OSV 的 Go ecosystem。

Go 锁文件解析与依赖图谱构建

对于 Go 项目,osv-scanner 支持扫描 go.mod 文件来获取依赖声明。与某些工具仅解析直接依赖不同,osv-scanner 能够通过调用 deps.dev API 解析完整的传递依赖图谱。当使用 --no-resolve 参数时,扫描器仅检查 go.mod 中明确声明的依赖;去掉该参数后,会通过 deps.dev 解析所有传递依赖并在 OSV 数据库中查询漏洞信息。这一行为对于安全审计的深度有直接影响:启用传递依赖解析可以发现间接引入的漏洞,但代价是扫描时间显著增加且可能产生更多误报。

在工程实践中,建议根据项目规模选择扫描策略。中小型 Go 项目可启用完整依赖图谱解析;大型单体仓库或依赖层级极深的项目建议先使用 --no-resolve 进行快速扫描,发现高危漏洞后再开启完整解析进行深度审计。此外,osv-scanner 还支持从已构建的 Go 二进制文件中提取依赖信息,这在扫描容器镜像时尤为重要 —— 通过分析二进制中的导入路径,可以发现运行时实际加载的依赖,即便该项目未提交锁文件。

SPDX 格式支持与软件物料清单输出

SPDX(Software Package Data Exchange)是 Linux 基金会维护的软件物料清单(SBOM)标准格式,osv-scanner 明确支持 SPDX 格式的输出。这一能力对于企业安全合规流程至关重要,因为许多供应链安全要求(如美国 NIST 的软件供应链安全指南)明确建议使用 SPDX 作为 SBOM 交换格式。

在 osv-scanner 中启用 SPDX 输出非常简单,使用 --format=spdx 参数即可将扫描结果以 SPDX JSON 格式输出。该输出包含扫描的所有包信息、版本号、漏洞关联关系等完整数据,可直接导入到供应链安全管理平台。需要特别说明的是,osv-scanner 同时支持 SPDX 许可证扫描 —— 通过 --licenses 参数可以传入允许的 SPDX 许可证标识符(如 MIT、Apache-2.0、BSD-3-Clause),扫描器会自动检查所有依赖的许可证并在结果中标记不合规的包。

然而,官方文档中提到了一个已知限制:当扫描自定义格式的 osv-scanner.json 文件时,使用 --format=spdx 会产生错误输出(参见 GitHub issue #2192)。如果你的工作流依赖自定义锁文件解析并需要 SPDX 格式输出,需要绕过这一限制或等待官方修复。

容器镜像扫描与已安装包检测

osv-scanner 的另一个核心能力是扫描容器镜像,这部分功能通过 osv-scanner scan image 子命令实现。与源码扫描不同,容器扫描关注的是已安装到镜像中的软件包而非声明性依赖。工具支持多种包管理器的安装数据库提取,包括 Alpine 的 APK 数据库、Debian/Ubuntu 的 dpkg 状态文件、Python wheel 文件、Node.js 的 node_modules 目录等。

对于使用了 Go 或 Rust 编译可执行文件的容器镜像,osv-scanner 还能从二进制文件中提取依赖信息。具体而言,如果 Go 程序在构建时包含了调试符号或使用了 cargo-auditable,扫描器可以逆向解析出程序依赖的 Go 模块或 Cargo crates。这一能力极大地扩展了扫描覆盖范围,使得即使项目未提交锁文件也能进行漏洞检查。

在安全运营场景中,建议将容器镜像扫描集成到镜像发布流水线。典型的部署参数是在镜像构建完成后、推送至镜像仓库前执行扫描,设置 --exit-code 1 使扫描失败时阻止镜像发布,并通过 --json-output 输出结构化结果便于后续安全平台消费。

可落地到 CI/CD 的工程化参数清单

将 osv-scanner 集成到持续集成流程时,以下参数组合经过生产环境验证:

基础扫描配置:使用 osv-scanner scan source -r . 对当前目录进行递归扫描,扫描器会自动发现并解析各类锁文件。若需控制扫描深度,可通过 --max-depth 参数限制递归层级,默认为无限递归。

性能与准确性平衡:对于大型仓库,增加 --timeout 300s 延长单次查询超时时间;使用 --jobs 4 控制并发查询数量避免对 OSV API 造成压力。当需要快速反馈时,优先使用 --no-resolve 仅扫描声明依赖,在确认无高危漏洞后再进行完整解析。

结果处理与门禁:结合 --exit-code 1--fail-on vulns 可以让扫描结果直接决定构建成败。门禁策略建议按漏洞等级配置:阻断 CVSS 9.0 以上的严重漏洞允许发布,较低等级漏洞仅做警告不阻断构建。

自定义锁文件支持:如果项目使用非标准依赖管理方式,可通过 --lockfile custom:/path/to/osv-scanner.json 指定自定义解析结果。JSON 文件格式需要遵循 osv-scanner 的输出 schema,包含 packages 数组及每个包的 name、version、ecosystem 字段。

监控指标与运营建议

运营 osv-scanner 时建议采集以下监控指标:每次扫描发现的漏洞总数、按严重程度分布的漏洞数量、扫描耗时与 API 调用延迟、扫描覆盖率(即锁文件被成功解析的比例)。这些指标可以通过解析 --json-output 结果并导出到 Prometheus 或企业监控平台获得。

在增量扫描场景下,osv-scanner 支持配合 Git diff 仅扫描变更文件引入的新依赖,这在大型仓库中可显著提升扫描效率。配置方式是使用 --diff 参数并指定两次提交的差异范围,工具会自动识别新增或修改的锁文件并仅对这些文件执行扫描。

整体而言,osv-scanner 提供了从源码到容器、从声明依赖到运行时依赖的全链路扫描能力。其 Go 编写带来的跨平台兼容性、OSV.dev 提供的统一漏洞数据库以及 SPDX 格式的标准化输出,使其成为供应链安全防御侧的理想选择。将这些能力与合理的 CI/CD 门禁策略结合,能够在依赖引入阶段有效阻断已知漏洞的传播。

参考资料

security