3、SBOM生成工具实战:syft、cdxgen与tern

好,咱们进入实战环节。前面聊了那么多SBOM的理论和标准,今天直接上手干活。我个人习惯把这三种工具搭配着用:syft 搞容器镜像,cdxgen 搞应用代码,tern 深挖基础镜像的每一层。说白了,它们覆盖了从「打包好的镜像」到「源码依赖」再到「底层OS组件」的完整链路。

嗯,先看一张总图,理清这三个工具各自负责什么。

SBOM 生成工具分工图 syft 容器镜像 / 文件系统 cdxgen 应用源码 / 项目依赖 tern 基础镜像层分析 输出格式:SPDX / CycloneDX / JSON / 自定义 💡 使用场景建议 • syft:已有容器镜像,快速获取所有包清单(含OS级依赖) • cdxgen:开发阶段,从源码锁定应用层依赖(npm/pip/maven等) • tern:需要审计基础镜像每一层引入了什么文件

3.1 syft:容器镜像的「快速扫描仪」

syft 是我用得最多的工具。为什么?因为它快,而且准。你给它一个镜像名,它直接解包分析,几秒钟就把所有包列出来。

核心能力:支持 Docker / OCI 镜像、本地文件系统、甚至 SBOM 文件本身。输出格式支持 SPDX、CycloneDX、JSON、表格等。

安装很简单,一行命令搞定:

# macOS / Linux
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin

# 或者用 Docker
docker run -v /var/run/docker.sock:/var/run/docker.sock anchore/syft:latest

实战一下。假设我们要分析一个 Node.js 应用的镜像:

# 生成 SPDX 格式的 SBOM
syft node:18-alpine -o spdx-json > node18-alpine.spdx.json

# 生成 CycloneDX 格式
syft node:18-alpine -o cyclonedx-json > node18-alpine.cdx.json

# 只看表格,快速浏览
syft node:18-alpine -o table

输出长什么样?我截取一段 SPDX 的片段给你看:

{
  "spdxVersion": "SPDX-2.3",
  "dataLicense": "CC0-1.0",
  "packages": [
    {
      "name": "libcrypto3",
      "versionInfo": "3.0.8-r4",
      "supplier": "Organization: Alpine Linux",
      "licenseConcluded": "MIT",
      "externalRefs": [
        {
          "referenceCategory": "PACKAGE-MANAGER",
          "referenceType": "purl",
          "referenceLocator": "pkg:apk/alpine/libcrypto3@3.0.8-r4?arch=x86_64"
        }
      ]
    }
  ]
}

我的经验:syft 对 Alpine 镜像支持最好,因为 apk 包管理器信息完整。如果是 Ubuntu 镜像,它会解析 dpkg 数据库,速度稍慢但结果一样可靠。我曾经在审计一个 Java 镜像时,syft 帮我发现了三个被标记为高危的 Log4j 版本——嗯,当时团队都惊了。

3.2 cdxgen:应用源码的「依赖侦探」

cdxgen 和 syft 不同。syft 看的是「已经装好的包」,cdxgen 看的是「项目里声明了哪些依赖」。它直接读你的 package.json、pom.xml、requirements.txt 这些文件,然后生成 CycloneDX 格式的 SBOM。

我个人习惯在 CI/CD 的构建阶段跑 cdxgen,这样能提前发现依赖风险,不用等到镜像打包完。

# 安装
npm install -g @cyclonedx/cdxgen

# 或者用 Docker
docker run -v $(pwd):/app cyclonedx/cdxgen -r /app -o /app/bom.json

实战一下,分析一个 Python 项目:

# 进入项目目录
cd my-python-app

# 生成 SBOM,自动识别语言
cdxgen -r . -o bom.json

# 指定输出格式(默认 CycloneDX JSON)
cdxgen -r . -o bom.xml   # XML 格式

cdxgen 支持的语言列表很全:

语言/生态 依赖文件 说明
JavaScript / Node.js package.json, package-lock.json 支持 yarn, pnpm
Python requirements.txt, Pipfile.lock, poetry.lock 自动识别虚拟环境
Java / JVM pom.xml, build.gradle, ivy.xml Maven / Gradle 都支持
Go go.mod, go.sum 需要 Go 环境
Rust Cargo.toml, Cargo.lock 直接解析 lock 文件
.NET *.csproj, packages.config NuGet 生态

注意:cdxgen 需要网络去解析某些依赖的元数据。如果你在离线环境跑,记得提前把依赖缓存好。我曾经在客户内网部署时,忘了这茬,结果等了半小时超时——嗯,从那以后我都在 CI 脚本里加了 --offline 参数。

3.3 tern:基础镜像的「层解剖刀」

tern 是个很有意思的工具。它不直接生成 SBOM,而是帮你分析容器镜像的每一层,告诉你每一层里放了什么文件、来自哪个包。这对于审计「基础镜像是否合规」特别有用。

你想想看,有时候基础镜像里多了一个莫名其妙的二进制文件,用 syft 可能只看到包名,但 tern 能告诉你这个文件是在哪一层被加进来的。

# 安装(需要 Python 3.6+)
pip install tern

# 或者用 Docker
docker run -v /var/run/docker.sock:/var/run/docker.sock -v $(pwd):/output tern report -i node:18-alpine -o /output/report

实战分析:

# 生成完整报告
tern report -i ubuntu:22.04 -o ubuntu-report

# 只查看层信息
tern report -i ubuntu:22.04 -o ubuntu-report --layer-only

tern 的输出会包含每层的文件清单和对应的包管理器信息。比如 Ubuntu 镜像,它会告诉你:

  • Layer 0:基础文件系统(来自 ubuntu:22.04 基础层)
  • Layer 1:安装了 curl、ca-certificates(来自 apt-get install)
  • Layer 2:添加了应用代码(来自 COPY 指令)

关键价值:tern 能帮你回答「这个文件到底是谁放进来的」。在合规审计中,如果发现某个敏感文件(比如 setuid 二进制),tern 可以直接定位到引入它的那一层和对应的包。

3.4 三剑客配合实战

在实际项目中,我通常这样搭配使用:

  1. 开发阶段:用 cdxgen 从源码生成应用层 SBOM,检查依赖漏洞。
  2. 构建阶段:用 syft 对构建好的镜像生成完整 SBOM,包含 OS 级依赖。
  3. 审计阶段:用 tern 对基础镜像做层分析,确认没有「幽灵文件」。

举个例子,一个典型的 CI 流水线脚本片段:

# 1. 源码依赖分析
cdxgen -r . -o bom-source.json

# 2. 构建镜像
docker build -t myapp:${CI_COMMIT_SHA} .

# 3. 镜像 SBOM
syft myapp:${CI_COMMIT_SHA} -o cyclonedx-json > bom-image.json

# 4. 基础镜像层审计
tern report -i myapp:${CI_COMMIT_SHA} -o layer-report

避坑指南:我曾经遇到过一个案例——syft 和 cdxgen 生成的 SBOM 里包版本不一致。后来发现是构建时用了 npm install --production,但 cdxgen 读的是完整的 devDependencies。所以记住:cdxgen 反映的是「项目声明」,syft 反映的是「镜像里实际装的」,两者有差异是正常的,关键是要理解差异的原因。

3.5 输出格式与工具选择建议

最后给个总结表,方便你选工具:

场景 推荐工具 输出格式 备注
已有容器镜像,快速出 SBOM syft SPDX / CycloneDX / JSON 速度最快,支持最广
源码项目,开发阶段审计 cdxgen CycloneDX (JSON/XML) 语言生态覆盖全
基础镜像层合规审计 tern 自定义报告 / JSON 层级别文件溯源
需要同时看 OS + 应用依赖 syft + cdxgen 各自输出后合并 推荐用 CycloneDX 格式统一

嗯,工具就介绍到这里。记住一点:没有完美的工具,只有合适的组合。syft 快但看不到层历史,cdxgen 准但依赖源码环境,tern 细但输出不够标准化。把它们串起来用,才能覆盖从开发到部署的完整 SBOM 生成链路。