4. SBOM生成工具链选型与集成
SBOM 生成工具链的选型,本质上不是“哪个工具最好”的问题,而是“在什么制品形态、什么构建阶段、什么合规格式要求下,用哪个工具最合适”的问题。Syft、Trivy、cdxgen 以及 SPDX 官方工具族各有侧重:Syft 擅长对容器镜像和文件系统做深度包清单提取,Trivy 把漏洞扫描与 SBOM 生成合并在同一条命令里,cdxgen 面向源码与多语言依赖树生成 CycloneDX,SPDX 工具族则围绕 SPDX 文档的生成、校验与转换。选型时先确定三件事:制品是镜像、二进制还是源码仓库;目标格式是 SPDX 还是 CycloneDX;SBOM 是用于合规存档还是用于后续漏洞比对。
四类工具的定位与关键差异
Syft 的输入对象是容器镜像、目录、归档文件,输出 SPDX 或 CycloneDX,它的强项是“对已构建制品做成分盘点”,不依赖源码构建环境,因此适合在镜像推送后立即生成 SBOM。Trivy 的输入同样支持镜像、文件系统、仓库,但它的输出可以同时包含 SBOM 与漏洞结果,适合“一次扫描、两类产出”的流水线。cdxgen 的输入偏向源码仓库与包管理清单,它会解析 pom.xml、package-lock.json、go.mod 等文件,生成带依赖层级的 CycloneDX,适合在构建前或构建中生成“源码级 SBOM”。SPDX 工具族(如 spdx-sbom-generator、tools-python)更强调 SPDX 文档的规范性、许可证字段完整性和文档校验,适合需要严格 SPDX 合规交付的场景。
一个常见的误区是只按“支持格式数量”选型。实际工程中更关键的是:工具能否识别当前技术栈的包管理器、能否输出依赖层级、能否在离线环境运行、能否与现有 CI 变量和制品仓库对接。例如 Java 多模块项目若只扫描镜像,可能丢失构建期依赖;而纯 Go 静态二进制若只扫源码,又可能漏掉 vendor 目录之外的运行时组件。因此,工具链往往不是单选,而是“源码阶段用 cdxgen、制品阶段用 Syft、扫描阶段用 Trivy”的组合。
CI/CD 嵌入点:把生成动作放到正确的位置
嵌入点的选择决定了 SBOM 的“新鲜度”和“覆盖面”。源码提交阶段适合放 cdxgen,因为它能在依赖解析完成后立即生成 CycloneDX,并把 SBOM 作为构建产物的一部分随代码版本一起归档。镜像推送阶段适合放 Syft 或 Trivy:Syft 负责生成制品 SBOM,Trivy 负责在同一镜像上输出漏洞结果,两者可以并行执行,也可以串行执行以减少镜像拉取次数。制品归档阶段适合放 SPDX 工具族,对最终交付的 SBOM 做格式校验和许可证字段补全。
以 GitLab CI 为例,一个可用的片段如下:
stages:
- build
- sbom
- scan
generate-source-sbom:
stage: sbom
image: ghcr.io/cyclonedx/cdxgen:latest
script:
- cdxgen -o sbom-source.cdx.json -t java .
artifacts:
paths:
- sbom-source.cdx.json
generate-image-sbom:
stage: sbom
image: anchore/syft:latest
script:
- syft registry.example.com/app:${CI_COMMIT_SHA} -o spdx-json > sbom-image.spdx.json
artifacts:
paths:
- sbom-image.spdx.json
scan-image:
stage: scan
image: aquasec/trivy:latest
script:
- trivy image --format cyclonedx --output sbom-trivy.cdx.json registry.example.com/app:${CI_COMMIT_SHA}
- trivy image --exit-code 1 --severity HIGH,CRITICAL registry.example.com/app:${CI_COMMIT_SHA}
artifacts:
paths:
- sbom-trivy.cdx.json
这段配置的关键点在于:cdxgen 在源码阶段运行,Syft 在镜像阶段运行,Trivy 在扫描阶段同时产出 SBOM 和漏洞结果。三个产物都作为 artifacts 保留,后续可以统一上传到 SBOM 仓库。注意 Trivy 的 --exit-code 1 会让高危漏洞导致流水线失败,这是“扫描即门禁”的常见做法,但需要根据团队实际容忍度调整。
自动化触发策略:什么时候生成、什么时候比对
触发策略要回答两个问题:生成频率和比对时机。生成频率上,源码级 SBOM 建议每次 push 或 PR 都生成,因为依赖变更往往发生在提交阶段;制品级 SBOM 建议每次镜像推送都生成,因为镜像层可能因基础镜像更新而变化;SPDX 合规文档建议在发布标签或定时任务中生成,避免每次提交都产生大量冗余文档。比对时机上,建议在“新 SBOM 生成后”立即与基线 SBOM 做差异比对,识别新增组件、版本变化和许可证变化,而不是等到漏洞扫描才发现问题。
一个实用的差异比对逻辑是:以组件坐标(名称 + 版本 + 包管理器)为键,比较新旧 SBOM 的组件集合。新增键表示新引入依赖,删除键表示依赖移除,版本变化表示升级或降级。对于新增组件,应重点检查其许可证和已知漏洞;对于版本变化,应检查是否跨越了安全修复版本。这个逻辑可以用脚本实现,也可以借助 Syft 的 diff 子命令或 Trivy 的 SBOM 比对能力完成。
选型决策与常见组合
如果团队以容器镜像为主要交付物,推荐“Syft + Trivy”组合:Syft 负责生成标准 SBOM,Trivy 负责漏洞扫描和门禁。如果团队以源码仓库为主、需要依赖层级,推荐“cdxgen + Syft”:cdxgen 生成源码级 CycloneDX,Syft 生成制品级 SPDX,两者互补。如果合规要求严格、必须交付 SPDX 文档,则在上述组合后追加 SPDX 工具族做校验和字段补全。无论哪种组合,都应把 SBOM 作为构建产物的一部分持久化,并与制品版本建立可追溯的关联,否则 SBOM 只是一次性输出,无法支撑后续的风险治理。