4. 依赖清单采集与资产盘点
依赖清单采集是依赖风险治理的起点。如果连“系统里到底用了哪些第三方组件、版本是什么、由谁引入”都说不清楚,后续的漏洞匹配、许可证合规、升级决策都无从谈起。本节要解决的核心问题是:如何从代码仓库、构建产物、容器镜像、运行时环境这四个不同层次的资产中,自动、持续、可复现地提取出依赖清单,并汇总成一份可查询、可追溯的资产台账。
四个采集层次及其互补关系
依赖信息在不同阶段以不同形态存在,单一数据源都无法覆盖全貌,因此需要分层采集:
- 代码仓库层:直接读取声明式依赖文件,如
pom.xml、build.gradle、package.json、package-lock.json、requirements.txt、go.mod、Cargo.toml。优点是采集快、可在提交时触发;缺点是只反映“声明”,不反映实际解析结果,且传递依赖需要工具解析。 - 构建产物层:对构建出的 jar、war、wheel、npm 包、二进制文件做扫描,读取其中的元数据(如
META-INF/maven/、*.dist-info/)。这一层最接近“实际打包进去的东西”,能发现被 shade/relocate 的依赖。 - 容器镜像层:对镜像文件系统做扫描,覆盖 OS 包(dpkg、rpm、apk)与应用依赖,是交付形态的最终快照。
- 运行时环境层:通过进程、类加载器、动态链接库、已安装包数据库等采集实际加载的组件,能发现运行时动态引入、热更新或与镜像不一致的情况。
四层的关系可以概括为:仓库层回答“打算用什么”,构建层回答“编译进了什么”,镜像层回答“交付了什么”,运行时层回答“实际跑了什么”。四者之间的差异本身就是重要的风险信号。
工具链选型与关键参数
实际工程中,推荐以 Syft 作为主力 SBOM 生成器,配合 Trivy 做镜像扫描与漏洞匹配,Grype 作为补充。Syft 支持目录、文件、容器镜像、OCI 归档等多种输入源,输出 SPDX 或 CycloneDX 格式,便于后续归一化。
以对一个 Java 服务镜像做采集为例,典型命令如下:
# 1. 从代码仓库目录生成 SBOM(声明层)
syft dir:./my-service -o cyclonedx-json=sbom-repo.json
# 2. 从构建产物 jar 生成 SBOM(产物层)
syft ./target/my-service-1.0.0.jar -o cyclonedx-json=sbom-jar.json
# 3. 从容器镜像生成 SBOM(镜像层)
syft registry.example.com/app/my-service:1.0.0 \
-o cyclonedx-json=sbom-image.json
# 4. 用 Trivy 对镜像做 OS 包 + 应用依赖扫描
trivy image --format cyclonedx \
--output sbom-trivy.json \
registry.example.com/app/my-service:1.0.0
关键参数说明:-o cyclonedx-json 指定输出格式,便于统一解析;--scope all-layers 让 Trivy 扫描镜像所有层而非仅最终层,避免遗漏被覆盖的中间层依赖;--platform linux/amd64 在多架构镜像场景下必须显式指定,否则可能扫到错误架构的包。
归一化与去重
四层采集出的组件坐标格式不统一:Maven 用 groupId:artifactId:version,npm 用 name@version,OS 包用 name-version-release.arch。归一化时建议统一映射为 PURL(Package URL),例如:
pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1
pkg:npm/lodash@4.17.21
pkg:deb/debian/openssl@1.1.1n-0+deb11u3?arch=amd64
PURL 作为主键后,同一组件在不同层出现时即可自动去重,同时保留“来源层”标记,用于后续差异分析。去重时需注意:同一 PURL 但哈希不同,说明内容被篡改或构建不可复现,应单独标记。
资产盘点的落地形态
采集结果最终要落到一张可查询的资产台账中。建议字段包括:应用名、环境(dev/staging/prod)、组件 PURL、版本、引入路径(直接/传递)、来源层、首次发现时间、最近确认时间、差异标记。台账应支持按应用、组件、版本、许可证多维检索,并能与漏洞库做周期性匹配。
差异检测:从清单到风险信号
四层清单汇总后,最有价值的动作是做差集。例如:仓库声明了 log4j-core@2.17.0,但镜像中实际是 2.14.1,说明构建缓存或依赖仲裁出了问题;镜像中有 openssl@1.1.1n,但运行时进程加载的是 1.1.1k,说明存在动态链接库覆盖。这些差异应自动生成告警工单,纳入治理闭环。
通过上述工具链与工程实践,依赖清单采集从“人工翻文件”升级为“四层自动采集 + PURL 归一化 + 差异检测”的持续过程,为后续的漏洞治理、许可证合规和升级决策提供可信的数据底座。