软件物料清单 SBOM怎样把依赖清单绑定到构建产物怎样完成一个可复用的最小示例
很多团队已经能生成 SBOM,却仍然回答不了一个关键问题:这份依赖清单到底对应哪个构建产物?真正稳定的做法不是把 SBOM 命名成 latest-sbom.json,而是先固定产物的摘要,再把摘要、SBOM 格式、生成器版本和构建来源写入同一条发布记录。这样,镜像标签变化、文件重命名或重新构建都不会让关联关系变得模糊。
Syft 官方地址:https://oss.anchore.com/docs/guides/sbom/getting-started/
本文用一个不依赖具体 CI 平台的最小方案说明这个关系:构建阶段产生产物,扫描器针对同一个产物生成 SPDX 或 CycloneDX 文档,再用产物 digest 作为关联键保存 SBOM 和 attestation 元数据。SPDX 是开放标准,Syft 官方文档也支持从容器镜像或文件系统生成多种 SBOM 输出格式。
先绑定“产物是什么”,再记录“里面有什么”。SBOM 是依赖组成的描述,digest 是构建产物的身份,attestation 则把两者和构建来源放在一个可追踪的声明里。
先区分依赖清单与构建产物
SBOM 和构建产物分别回答两个问题。SBOM 描述组件、版本、许可证或包坐标等组成信息;构建产物可能是容器镜像、二进制压缩包或安装包。两者不能仅靠同一个字符串名称绑定,因为同一个标签可以被覆盖,同一个文件名也可能来自不同的源码提交或构建参数。
可以把最小关联模型写成三列:
| 对象 | 应记录什么 | 作用 |
|---|---|---|
| 构建产物 | 名称、类型、digest | 确认被描述的对象 |
| SBOM | 格式、文件摘要、生成器 | 确认依赖清单本身 |
| 声明记录 | 产物 digest、SBOM digest、构建来源 | 把两份对象绑定起来 |
这里的核心不是把所有信息塞进一个 JSON,而是保留两个摘要:一个指向构建产物,一个指向 SBOM 文件。任何一方被替换后,记录都能暴露出不一致。

为产物生成不可混淆的摘要
如果产物是容器镜像,发布系统通常可以拿到镜像 digest;如果产物是本地文件,则可以用 SHA-256 计算文件摘要。示例只展示绑定所需的最小字段,命令不会改变产物内容。
#!/usr/bin/env bash
set -euo pipefail
# 把实际构建产物路径集中到一个变量,避免清单和摘要指向不同文件。
ARTIFACT="dist/example-service.tar.gz"
# 计算文件摘要;摘要是后续 SBOM 关联记录中的稳定身份。
ARTIFACT_DIGEST="$(shasum -a 256 "$ARTIFACT" | awk '{print $1}')"
# 只输出摘要和文件名,不把私密环境变量写入发布记录。
printf 'artifact=%s\nsha256=%s\n' "$ARTIFACT" "$ARTIFACT_DIGEST"
如果流水线使用容器镜像,应优先使用注册表返回的 immutable digest,而不是把 service:latest 当成身份。标签适合给人阅读,digest 才适合放进 SBOM 绑定记录。若构建系统同时产生多个平台产物,还要为每个平台分别保存 digest,不能用一个总标签代替。
使用 Syft 生成 SBOM
Syft 可以扫描容器镜像或目录,并输出 SPDX、CycloneDX 等格式。为了让绑定关系可复用,建议把输入对象和输出文件名显式写出来。下面的目录扫描示例适合已经解包到工作区的应用;若扫描容器镜像,只需把输入替换为镜像引用。
#!/usr/bin/env bash
set -euo pipefail
SOURCE_DIR="build/rootfs"
SBOM_FILE="dist/example-service.spdx.json"
# 对构建产物对应的文件系统生成 SPDX JSON,不扫描另一个工作目录。
syft "$SOURCE_DIR" -o "spdx-json=$SBOM_FILE"
# 计算 SBOM 文件摘要,后续声明记录同时保存它和产物摘要。
SBOM_DIGEST="$(shasum -a 256 "$SBOM_FILE" | awk '{print $1}')"
printf 'sbom=%s\nsha256=%s\n' "$SBOM_FILE" "$SBOM_DIGEST"
如果消费方更偏好 CycloneDX,可以把输出格式改成 cyclonedx-json,但不要在关联记录里只写“SBOM 已生成”。记录格式、文件摘要和生成工具版本,才能在后续转换或重新扫描时解释差异。不同格式表达能力并不完全相同,转换时也要保留原始文件,避免只剩转换后的副本。
把 digest 写进关联记录
现在有了两个对象摘要,可以用一份小型声明记录表达绑定关系。下面是严格 JSON,不能加入注释;字段含义放在代码块后说明。
{
"subject": {
"name": "example-service.tar.gz",
"sha256": "ARTIFACT_DIGEST"
},
"sbom": {
"path": "example-service.spdx.json",
"format": "spdx-json",
"sha256": "SBOM_DIGEST"
},
"build": {
"source": "repository@commit",
"builder": "ci-pipeline",
"generator": "syft"
}
}
subject.sha256 只指向被描述的产物,sbom.sha256 指向清单文件本身,build.source 用于追溯源码或构建任务。实际系统可以把这份记录转换为签名声明或上传到制品仓库,但不要把“上传成功”误认为“绑定正确”:验证方仍然要重新计算摘要并比对声明里的值。

在 CI 中保存并审查绑定关系
在 CI 里,顺序可以概括为“构建—摘要—扫描—摘要—声明—上传”。真正要避免的是扫描工作区的源码目录,却把结果标成最终镜像;或者 SBOM 文件生成后又被修改,但声明仍使用旧摘要。
name: publish-sbom
on:
push:
tags: ["v*"]
jobs:
sbom:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build artifact
run: ./ci/build.sh
- name: Generate SBOM and binding record
run: |
# 脚本必须读取同一个构建产物,并把两个摘要写入声明。
./ci/create-sbom-binding.sh dist/example-service.tar.gz
- name: Upload release materials
uses: actions/upload-artifact@v4
with:
# 同时保存产物、SBOM 和声明,避免只上传孤立清单。
name: release-materials
path: |
dist/example-service.tar.gz
dist/example-service.spdx.json
dist/example-service.binding.json
审查这条流水线时,重点看四个可见结果:SBOM 输入是否就是最终产物、声明是否含产物和 SBOM 两个摘要、上传物是否包含三者、失败日志是否能指出具体对象。若 CI 使用专门的 SBOM Action,也要显式配置输出文件名和 artifact 名称,避免默认文件名在多任务并行时互相覆盖。
兼容边界与采用建议
这个最小方案解决的是“清单属于哪个产物”的身份问题,不等于完整的供应链安全体系。它没有自动证明构建环境可信,也没有替代漏洞扫描、许可证审查、签名密钥管理或部署准入策略。对外发布时,还应根据消费方要求选择 SPDX、CycloneDX 或其他约定格式,并确认对方是否需要签名声明、来源证明或长期归档。
采用时可以按三步推进:先为单一产物保存两个摘要;再把 SBOM 和声明上传到同一制品记录;最后再接入签名和策略检查。任何一步都要保留原始 SBOM,避免为了满足某个消费方格式而覆盖生成源文件。
- 不要用
latest、文件名或流水线编号单独作为绑定键。 - 不要只保存 SBOM 摘要而丢掉被描述产物的摘要。
- 不要扫描源码工作区后把结果标成容器镜像 SBOM。
- 不要把 JSON 格式转换当成内容等价;保留原始格式并记录转换工具。
相关问题
为什么同一个镜像标签会对应不同 SBOM?标签可能被重新推送;应使用注册表返回的不可变 digest,并让 SBOM 针对同一个 digest 生成。
本地压缩包没有镜像 digest 怎么办?对最终文件计算 SHA-256,把文件摘要作为 subject 身份,同时保存 SBOM 文件自己的摘要。
SPDX 和 CycloneDX 选哪个?先看消费方和现有仓库约定;记录中明确格式,不要把格式名称省略成笼统的“SBOM”。
生成 SBOM 就等于完成 attestation 吗?不是。SBOM 是清单,attestation 是把清单、产物身份和来源放入可追踪声明的关联层;是否签名还要看发布策略。
vLLM OpenAI 兼容服务怎样用 JSON Schema 约束结构化输出
- 上一篇
- vLLM OpenAI 兼容服务怎样用 JSON Schema 约束结构化输出
- 下一篇
- 软件物料清单 SBOM怎样把依赖清单绑定到构建产物为什么结果为空或不符合预期
-
- 科技周边 · 业界新闻 | 22分钟前 | interface WebAssembly Component Model WIT 跨语言组件 world wasmtime
- WebAssembly Component Model服务间接口怎样减少语言绑定成本为什么结果为空或不符合预期
- 182浏览 收藏
-
- 科技周边 · 业界新闻 | 25分钟前 |
- WebAssembly Component Model服务间接口怎样减少语言绑定成本怎样完成一个可复用的最小示例
- 331浏览 收藏
-
- 科技周边 · 业界新闻 | 28分钟前 |
- 软件物料清单 SBOM怎样把依赖清单绑定到构建产物怎样在批处理代码中减少额外分配
- 184浏览 收藏
-
- 科技周边 · 业界新闻 | 1天前 | 云原生 · 人工智能 · 云原生 Kubernetes AI工作流 Kubeflow 平台工程
- 云原生平台为什么开始强调端到端 AI 工作流
- 249浏览 收藏
-
- 科技周边 · 业界新闻 | 2天前 | PB脚本格式化 云原生 AI基础设施 Kubernetes 混合云 平台工程
- 2026 云原生生态有哪些值得持续观察的工程方向
- 409浏览 收藏
-
- 科技周边 · 业界新闻 | 2天前 |
- 云原生项目的许可证变化怎样影响企业采用
- 123浏览 收藏
-
- 科技周边 · 业界新闻 | 2天前 |
- AI 原生基础设施为什么需要更细的资源隔离
- 444浏览 收藏
-
- 科技周边 · 业界新闻 | 2天前 | 云原生 · 技术选型 CNCF 平台工程 云原生生态 Technology Radar
- CNCF 年度生态信息怎样帮助团队选择平台路线
- 196浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 965次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 966次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 1077次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 1057次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 888次使用
-
- Go chassis云原生微服务开发框架应用编程实战
- 2022-12-29 214浏览
-
- Go ldflags 注入构建信息的变量设计
- 2026-10-15 182浏览
-
- docker的 linux 内核可以和宿主机不一样吗?
- 2023-01-12 318浏览
-
- Go 服务上 Kubernetes 后 HPA 为什么不扩容:requests、CPU 和并发指标怎么选
- 2026-07-17 111浏览
-
- 零基础入门PolarDB-X:搭建高可用系统并联动数据大屏
- 2023-02-24 274浏览

