protobom/protobom
GitHub: protobom/protobom
protobom 是一个使用 Protocol Buffers 的通用 SBOM 工具,解决不同SBOM格式间的无损转换和编程操作问题。
Stars: 324 | Forks: 58
# protobom 文档

`protobom` 是 SBOM 数据的一种 [Protocol Buffers](https://protobuf.dev/getting-started/)
表示形式,能够无损地摄取现代
[SPDX](https://spdx.dev/) 和 [CycloneDX](https://cyclonedx.org/) 版本的文档。它包含一个从 protocol buffers 定义生成的配套 Go 库,该库也实现了这些格式的摄取器。
标准 SBOM 由读取器通过使用理解常见格式的[反序列化器](docs/unserializers.md)来读取。反序列化器从读取的 CycloneDX 或 SPDX 文档数据创建一个中立的 protobom。
protobom 可以由写入器使用知道如何生成这些文档的[序列化器](docs/serializers.md)渲染成标准 SBOM 格式。
## 支持的版本和格式
下表总结了当前 Go 语言库中支持的格式和编码。
| 格式 | 版本 | 编码 | 读取 | 写入 |
| --- | --- | --- | --- | --- |
| SPDX | 2.2 | JSON | 计划中 | - |
| SPDX | 2.2 | tag-value | 计划中 | - |
| SPDX | 2.3 | JSON | 支持 | 支持 |
| SPDX | 2.3 | tag-value | 计划中 | - |
| SPDX | 3.0 | JSON | 计划中 | 计划中 |
| CycloneDX | 1.4 | JSON | 支持 | 支持 |
| CycloneDX | 1.5 | JSON | 支持 | 支持 |
| CycloneDX | 1.6 | JSON | 支持 | 支持 |
其他读取和写入实现可能可以用[protobuf 支持的其他语言](https://protobuf.dev/getting-started/)编写。
## 用法
`protobom` 库可用于读取和写入上述任何格式的 SBOM 文档。
### 示例 1:sbom-convert 项目
https://github.com/protobom/sbom-convert 提供了一个使用该库将 SBOM 摄取到 protobom 中间格式,然后以不同格式写入新 SBOM 文档的完整示例。
### 示例 2:读入 SBOM 文档以操作特定字段
`protobom` 库是使用 Go 编程语言与 SBOM 文档交互的最佳且最简单的方式。在此示例中,我们展示了如何通过创建一个新的 protobom `Reader` 并调用 `reader.ParseFile()` 来读取 SBOM 文档文件并返回一个 protobom sbom.Document,从而轻松地以编程方式访问 SBOM。在此示例中,无论格式如何,protobom 都会处理文档的反序列化。然后,使用 protobom 的开发者可以操作一个一致的 protobom 中间表示,而实际的文件格式被抽象化。
此特定示例遍历 SBOM 文档节点列表中的每个节点,并打印每个节点的 ID、Name 和 Version。如果输入文档是 SPDX SBOM,每个 protobom Node 将描述一个 SPDX 包。如果输入文档是 CycloneDX SBOM,每个 protobom Node 将描述一个 CycloneDX 组件。使用 protobom 的开发者无需根据输入格式更改代码。protobom 库解析输入文档以呈现数据的中间格式,允许开发者操作一致的中间格式。
```
package main
import (
"fmt"
"github.com/protobom/protobom/pkg/reader"
)
func main() {
// Create a new protobom SBOM reader:
reader := reader.New()
document, err := reader.ParseFile("sbom.spdx.json")
if err != nil {
fmt.Printf("ERROR: %v\n", err)
return
}
// List all nodes in the SBOM and print some information about them:
for _, node := range document.GetNodeList().GetNodes() {
fmt.Printf("Node ID [%v]: name: %v version: %v\n", node.GetId(), node.GetName(), node.GetVersion())
}
}
```
### 示例 3:以编程方式生成 SBOM 文档
开发者可以使用 `protobom` 库基于单独的 SBOM 文档内容生成 SBOM 文档,正如 [sbom-convert](https://github.com/protobom/sbom-convert) 项目所示。
然而,`protobom` 中间表示也可用于创建新的 SBOM 文档。开发者可以创建一个新的 `protobom` 文档,并使用 Go 编程语言填充 SBOM 文档所需的字段。然后,开发者将创建一个新的 Writer 来定义 SBOM 应写入的位置以及应将 SBOM 写入的格式,并传入以编程方式定义的 SBOM 结构。protobom v0.2.0 版本包含六个为 Writer.New() 注册的序列化格式:SPDX23JSON、CDX12JSON、CDX13JSON、CDX14JSON、CDX15JSON 和 CDX16JSON。
```
package main
import (
"os"
"github.com/protobom/protobom/pkg/formats"
"github.com/protobom/protobom/pkg/sbom"
"github.com/protobom/protobom/pkg/writer"
)
func main() {
// Create a new protobom document
document := sbom.NewDocument()
// Populate some of the document metadata:
// ...for example the author:
document.Metadata.Authors = append(
document.Metadata.Authors,
&sbom.Person{Name: "John Doe"},
)
// ...and the tool that produced the SBOM:
document.Metadata.Tools = append(
document.Metadata.Tools,
&sbom.Tool{
Name: "ACME SBOM Tool",
Version: "1.0",
Vendor: "ACME Corporation"},
)
// Create a node to represent the application:
appNode := &sbom.Node{
Id: "pkg:generic/my-software@v1.0.0",
PrimaryPurpose: []sbom.Purpose{sbom.Purpose_APPLICATION},
Name: "My Software Name",
Version: "v1.0.0",
Licenses: []string{"Apache-2.0"},
LicenseConcluded: "Apache-2.0",
LicenseComments: "Apache License",
}
// Add the application node to the document's nodelist:
document.NodeList.AddRootNode(appNode)
// Create two nodes to describe files in the application
node1 := &sbom.Node{
Id: "File--usr-lib-libsoftware.so",
Type: sbom.Node_FILE,
Name: "/usr/lib/libsoftware.so",
Version: "1",
Copyright: "Copyright 2023 The ACME Corporation",
Description: "Software Lib",
}
node1.AddHash(sbom.HashAlgorithm_SHA1, "f3ae11065cafc14e27a1410ae8be28e600bb8336")
node1.AddHash(sbom.HashAlgorithm_SHA256, "4f232eeb99e1663d07f0af1af6ea262bf594934b694228e71fd8f159f9a19f32")
node1.AddHash(sbom.HashAlgorithm_SHA512, "8044d0df34242699ad73bfe99b9ac3d6bbdaa4f8ebce1e23ee5c7f9fe59db8ad7b01fe94e886941793aee802008a35b05a30bc51426db796aa21e5e91b7ed9be")
document.NodeList.AddNode(node1)
node2 := &sbom.Node{
Id: "File--usr-bin-software",
Type: sbom.Node_FILE,
Name: "/usr/bin/software",
Version: "1.0",
Copyright: "Copyright 2023 The ACME Corporation",
Description: "Software binary",
}
node2.AddHash(sbom.HashAlgorithm_SHA1, "defee82004d22fc92ab81c0c952a62a2172bda8c")
node2.AddHash(sbom.HashAlgorithm_SHA256, "ad291c9572af8fc2ec8fd78d295adf7132c60ad3d10488fb63d120fc967a4132")
node2.AddHash(sbom.HashAlgorithm_SHA512, "5940d8647907831e77ec00d81b318ca06655dbb0fd36d112684b03947412f0f98ea85b32548bc0877f3d7ce8f4de9b2c964062df44742b98c8e9bd851faecce9")
// Relate the application package and the files. This adds the nodes to
// the document graph:
document.NodeList.RelateNodeAtID(node1, appNode.Id, sbom.Edge_contains)
document.NodeList.RelateNodeAtID(node2, appNode.Id, sbom.Edge_contains)
// Now render the document to STDOUT:
w := writer.New()
// Write the SBOM to STDOUT in SPDX 2.3:
w.WriteStreamWithOptions(
document, os.Stdout, &writer.Options{Format: formats.SPDX23JSON},
)
// Write the SBOM to STDOUT in CycloneDX 1.4:
w.WriteStreamWithOptions(
document, os.Stdout, &writer.Options{Format: formats.CDX14JSON},
)
}
```
## 加入 Protobom 社区!
- 来 [OpenSSF Slack 的 `#protobom`](https://openssf.slack.com/archives/C06ED97EQ4B) 频道打招呼。
- [Protobom 社区会议](https://zoom-lfx.platform.linuxfoundation.org/meeting/93849959680?password=414218a0-8865-4cea-ba62-99443815ceff&invite=true) - 每隔一周太平洋时间上午 9 点。
- [OpenSSF 安全工具工作组会议](https://zoom-lfx.platform.linuxfoundation.org/meeting/94897563315?password=7f03d8e7-7bc9-454e-95bd-6e1e09cb3b0b) - 每隔一周太平洋时间上午 8 点。
标签:CycloneDX, EVTX分析, Go 语言, ingesters, JSON, OpenSSF, Protocol Buffers, SBOM, SBOM 管理, serializers, SPDX, unserializers, 二进制发布, 开源工具, 数据表示, 数据读写, 日志审计, 格式转换, 硬件无关, 跌倒检测, 软件物料清单, 软件透明度