anthonyharrison/lib4sbom
GitHub: anthonyharrison/lib4sbom
lib4sbom 是一个用于解析、生成和验证 SPDX 与 CycloneDX 格式软件物料清单的 Python 库,提供统一的 SBOM 数据抽象层。
Stars: 39 | Forks: 19
# Lib4SBOM
Lib4SBOM 是一个用于解析和生成软件物料清单 (SBOM) 的库。它支持以
[SPDX](https://www.spdx.org) 和 [CycloneDX](https://www.cyclonedx.org) 格式创建的 SBOM。
它的开发基于这样一个假设:无论底层格式如何,拥有一个通用的 SBOM 抽象对开发者来说将是有用的。
提供以下功能:
- 生成 TagValue、JSON 和 YAML 格式的 SPDX SBOM
- 生成 JSON 格式的 CycloneDX SBOM
- 解析 TagValue、JSON、YAML、XML 和 RDF 格式的 SPDX SBOM
- 验证 JSON 和 XML 格式的 CycloneDX SBOM
- 验证 TagValue、JSON、YAML、XML 和 RDF 格式的 SPDX SBOM
- 解析 JSON 和 XML 格式的 CycloneDX SBOM
- 创建和操作 SBOM 文件对象
- 创建和操作 SBOM 包对象
- 创建和操作 SBOM 依赖关系对象
- 创建和操作漏洞对象
- 创建和操作软件服务对象
- 创建和操作加密对象
- 生成的 SBOM 可以输出到文件或控制台
- 解析嵌入在 Intoto 证明中的 SBOM
- 解析嵌入在 Protobom 对象中的 SPDX 或 CycloneDX SBOM
## 安装
使用以下命令进行安装:
`pip install lib4sbom`
或者,直接克隆仓库并使用以下命令安装依赖项:
`pip install -U -r requirements.txt`
该工具需要较新版本的 Python 3 (3.9+)。建议使用 Python 虚拟环境,特别
是如果您正在使用不同版本的 python。`virtualenv` 是一个用于设置虚拟 Python 环境的工具,
它允许您在单个环境中设置该工具的所有依赖项,或者设置不同的环境以便
使用不同版本的 Python 进行测试。
## API
### SBOMParser
SBOMParser 模块提供了解析 SPDX 或 CycloneDX 格式 SBOM 的方法,并
返回 SBOM 中的文件、包和关系信息。
实现的重点是无论 SBOM 格式如何,都提供一组通用的 SBOM 数据。
支持以下格式和版本的 SBOM
| SBOM Type | Version | Format |
| --------- |---------|----------|
| SPDX | 2.2 | TagValue |
| SPDX | 2.2 | JSON |
| SPDX | 2.2 | YAML |
| SPDX | 2.2 | RDF |
| SPDX | 2.2 | XML |
| SPDX | 2.3 | TagValue |
| SPDX | 2.3 | JSON |
| SPDX | 2.3 | YAML |
| SPDX | 2.3 | RDF |
| SPDX | 2.3 | XML |
| SPDX | 3.0 | JSON-LD |
| CycloneDX | 1.4 | JSON |
| CycloneDX | 1.4 | XML |
| CycloneDX | 1.5 | JSON |
| CycloneDX | 1.5 | XML |
| CycloneDX | 1.6 | JSON |
| CycloneDX | 1.6 | XML |
| CycloneDX | 1.7 | JSON |
| CycloneDX | 1.7 | XML |
**注意**,对 SPDX RDF 和 XML 格式的支持仅限于少数包属性。
**注意**,解析作为 [In-Toto](https://in-toto.io/) 语句嵌入的 SBOM 的支持仅适用于 SPDX 文档。
_class_ **SBOMParser**(_sbom_type='auto_')
这将创建一个简单的 SBOM 解析器对象。可以指定一个可选参数 _sbom_type_,
它表示 SBOM 的类型(spdx、cyclonedx 或 auto)。默认值为 auto,在这种
情况下,解析器将使用以下文件名约定自动识别 SBOM 类型。
| SBOM | Format | Filename extension |
| --------- |----------|--------------------|
| SPDX | TagValue | .spdx |
| SPDX | JSON | .spdx.json |
| SPDX | JSON-LD | .json |
| SPDX | YAML | .spdx.yaml |
| SPDX | YAML | .spdx.yml |
| SPDX | RDF | .spdx.rdf |
| SPDX | XML | .spdx.xml |
| CycloneDX | JSON | .json |
| CycloneDX | JSON | .cdx.json |
| CycloneDX | JSON | .bom.json |
| CycloneDX | XML | .xml |
| CycloneDX | XML | .cdx.xml |
| CycloneDX | XML | .bom..xml |
解析器将检查是否由正确的解析器处理了正确的 JSON 文件。
提交给 CycloneDX 解析器的 SPDX JSON 文件将导致没有数据被处理。
解析过程中的错误将导致引发 SBOMParserException 异常。
**方法**
parse_file(filename)
解析 SBOM 文件。如果文件不存在,则引发 FileNotFoundError 异常。
如果在解析文件过程中发生错误,则引发 SBOMParserException 异常。
parse_string(sbom_string)
从 sbom_string 解析 SBOM 内容。
如果在解析文件过程中发生错误,则引发 SBOMParserException 异常。
get_files()
从解析的 SBOM 中返回文件元素列表
get_packages()
从解析的 SBOM 中返回包元素列表
get_relationships()
从解析的 SBOM 中返回关系元素
get_vulnerabilities()
从解析的 SBOM 中返回漏洞元素
get_services()
从解析的 SBOM 中返回软件服务元素
get_type()
返回 SBOM 的类型(spdx 或 cyclonedx)
**示例**
以下示例中使用了一个测试 SBOM 文件 (test_sbom.spdx)。
```
SPDXVersion: SPDX-2.2
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: virtualenv
DocumentNamespace: http://spdx.org/spdxdocs/virtualenv-b7ac9cce-efe8-4fe7-a544-100e6a5664e6
LicenseListVersion: 3.18
Creator: Tool: sbom4python-0.4.0
Created: 2022-11-16T10:14:26Z
CreatorComment: This document has been automatically generated.
##### PackageName: virtualenv
SPDXID: SPDXRef-Package-1-virtualenv
PackageSupplier: Person: Bernat_Gabor
PackageVersion: 20.16.7
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageLicenseConcluded: MIT
PackageLicenseDeclared: MIT
PackageCopyrightText: NOASSERTION
ExternalRef: PACKAGE-MANAGER purl pkg:pypi/virtualenv@20.16.7
##### PackageName: distlib
SPDXID: SPDXRef-Package-2-distlib
PackageSupplier: Person: Vinay_Sajip
PackageVersion: 0.3.6
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION
PackageCopyrightText: NOASSERTION
ExternalRef: PACKAGE-MANAGER purl pkg:pypi/distlib@0.3.6
##### PackageName: filelock
SPDXID: SPDXRef-Package-3-filelock
PackageSupplier: Person: Benedikt_Schmitt
PackageVersion: 3.8.0
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageLicenseConcluded: Unlicense
PackageLicenseDeclared: Unlicense
PackageCopyrightText: NOASSERTION
ExternalRef: PACKAGE-MANAGER purl pkg:pypi/filelock@3.8.0
##### PackageName: platformdirs
SPDXID: SPDXRef-Package-4-platformdirs
PackageSupplier: NOASSERTION
PackageVersion: 2.5.4
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION
PackageCopyrightText: NOASSERTION
ExternalRef: PACKAGE-MANAGER purl pkg:pypi/platformdirs@2.5.4
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package-1-virtualenv
Relationship: SPDXRef-Package-1-virtualenv CONTAINS SPDXRef-Package-2-distlib
Relationship: SPDXRef-Package-1-virtualenv CONTAINS SPDXRef-Package-3-filelock
Relationship: SPDXRef-Package-1-virtualenv CONTAINS SPDXRef-Package-4-platformdirs
```
以下代码示例展示了 SBOMParser 模块的使用。
```
>>> from lib4sbom.parser import SBOMParser
>>> test_parser = SBOMParser()
>>> print (f"SBOM type {test_parser.get_type()}")
SBOM type auto
>>> test_parser.parse_file("test_sbom.spdx")
>>> print (f"SBOM type {test_parser.get_type()}")
SBOM type spdx
>>> sbom_files = test_parser.get_files()
>>> print (sbom_files)
[]
>>> sbom_packages = test_parser.get_packages()
>>> print (sbom_packages)
[{'name': 'virtualenv', 'type': 'library', 'id': 'SPDXRef-Package-1-virtualenv', 'supplier_type': 'Person', 'supplier': 'Bernat_Gabor', 'version': '20.16.7', 'downloadlocation': 'NOASSERTION', 'filesanalysis': 'false', 'licenseconcluded': 'MIT', 'licensedeclared': 'MIT', 'externalreference': [['PACKAGE-MANAGER', 'purl', 'pkg:pypi/virtualenv@20.16.7']]}, {'name': 'distlib', 'type': 'library', 'id': 'SPDXRef-Package-2-distlib', 'supplier_type': 'Person', 'supplier': 'Vinay_Sajip', 'version': '0.3.6', 'downloadlocation': 'NOASSERTION', 'filesanalysis': 'false', 'licenseconcluded': 'NOASSERTION', 'licensedeclared': 'NOASSERTION', 'externalreference': [['PACKAGE-MANAGER', 'purl', 'pkg:pypi/distlib@0.3.6']]}, {'name': 'filelock', 'type': 'library', 'id': 'SPDXRef-Package-3-filelock', 'supplier_type': 'Person', 'supplier': 'Benedikt_Schmitt', 'version': '3.8.0', 'downloadlocation': 'NOASSERTION', 'filesanalysis': 'false', 'licenseconcluded': 'Unlicense', 'licensedeclared': 'Unlicense', 'externalreference': [['PACKAGE-MANAGER', 'purl', 'pkg:pypi/filelock@3.8.0']]}, {'name': 'platformdirs', 'type': 'library', 'id': 'SPDXRef-Package-4-platformdirs', 'supplier_type': 'Organization', 'supplier': 'Unknown', 'version': '2.5.4', 'downloadlocation': 'NOASSERTION', 'filesanalysis': 'false', 'licenseconcluded': 'NOASSERTION', 'licensedeclared': 'NOASSERTION', 'externalreference': [['PACKAGE-MANAGER', 'purl', 'pkg:pypi/platformdirs@2.5.4']]}]
>>> print (len(sbom_packages))
4
>>> sbom_packages[0]
{'name': 'virtualenv', 'type': 'library', 'id': 'SPDXRef-Package-1-virtualenv', 'supplier_type': 'Person', 'supplier': 'Bernat_Gabor', 'version': '20.16.7', 'downloadlocation': 'NOASSERTION', 'filesanalysis': 'false', 'licenseconcluded': 'MIT', 'licensedeclared': 'MIT', 'externalreference': [['PACKAGE-MANAGER', 'purl', 'pkg:pypi/virtualenv@20.16.7']]}
>>> sbom_relationships = test_parser.get_relationships()
>>> print (sbom_relationships)
[{'source': 'TestDocument', 'type': 'DESCRIBES', 'target': 'virtualenv', 'source_id': 'SPDXRef-DOCUMENT', 'target_id': 'SPDXRef-Package-1-virtualenv'}, {'source': 'virtualenv', 'type': 'CONTAINS', 'target': 'distlib', 'source_id': 'SPDXRef-Package-1-virtualenv', 'target_id': 'SPDXRef-Package-2-distlib'}, {'source': 'virtualenv', 'type': 'CONTAINS', 'target': 'filelock', 'source_id': 'SPDXRef-Package-1-virtualenv', 'target_id': 'SPDXRef-Package-3-filelock'}, {'source': 'virtualenv', 'type': 'CONTAINS', 'target': 'platformdirs', 'source_id': 'SPDXRef-Package-1-virtualenv', 'target_id': 'SPDXRef-Package-4-platformdirs'}]
>>> sbom_relationships[2]
{'source': 'virtualenv', 'type': 'CONTAINS', 'target': 'filelock', 'source_id': 'SPDXRef-Package-1-virtualenv', 'target_id': 'SPDXRef-Package-3-filelock'}
>>>
```
_class_ **SBOMValidator**(_sbom_type='auto', version=None, debug=False_)
这将创建一个简单的 SBOM 验证器对象。
可以指定可选参数 _sbom_type_,它表示 SBOM 的类型(spdx、cyclonedx 或 auto)。默认值为 auto,在这种
情况下,解析器将使用以下文件名约定自动识别 SBOM 类型。
| SBOM | Format | Filename extension |
| --------- |----------|--------------------|
| SPDX | TagValue | .spdx |
| SPDX | JSON | .spdx.json |
| SPDX | JSON-LD | .json |
| SPDX | YAML | .spdx.yaml |
| SPDX | YAML | .spdx.yml |
| SPDX | RDF | .spdx.rdf |
| SPDX | XML | .spdx.xml |
| CycloneDX | JSON | .json |
| CycloneDX | JSON | .cdx.json |
| CycloneDX | JSON | .bom.json |
| CycloneDX | XML | .xml |
| CycloneDX | XML | .cdx.xml |
| CycloneDX | XML | .bom.xml |
可选参数 _version_ 可用于指定要验证的 SBOM 的单个版本,例如 "1.6"。
可选参数 _debug_ 可用于生成调试输出。
**注意** 要验证 3.0 版本格式的 SPDX SBOM,必须明确指定版本,
**方法**
validate_file(filename)
验证 SBOM 文件。如果文件不存在,则引发 FileNotFoundError 异常。
验证器将检查是否由正确的解析器处理了正确的 JSON 文件。
提交给 CycloneDX 解析器的 SPDX JSON 文件将导致没有数据被处理。
如果在验证文件过程中发生错误,则引发 SBOMValidatorException 异常。
返回值是一个表示 SBOM 类型 and 版本的字典。例如 {"SPDX" : 2.3}。
如果 SBOM 验证失败,返回值是 SBOM 类型和布尔值,例如 {"SPDX" : False}。
如果无法验证 SBOM,返回值是 SBOM 类型和 "Unknown",即 {"SPDX" : "Unknown"}。
验证规则:
- SPDX JSON 和 YAML 文件的验证依据是 SPDX JSON schema。
- SPDX TagValue、RDF 和 XML 文件的验证仅仅是检测是否存在有效的 SDPX 规范版本。
- CycloneDX JSON 文件的验证依据是 CycloneDX JSON schema。
- CycloneDX XML 文件的验证依据是 CycloneDX XML schema。
### SBOMGenerator
SBOMGenerator 模块提供了以 SPDX 或 CycloneDX 格式生成 SBOM 的方法。
实现的重点是无论 SBOM 类型和格式如何,都提供单一接口。
可以生成以下格式的 SBOM
| SBOM Type | Version | Format |
| --------- |---------| ----------|
| SPDX | 2.2 | Tag |
| SPDX | 2.2 | JSON |
| SPDX | 2.2 | YAML |
| SPDX | 2.3 | Tag |
| SPDX | 2.3 | JSON |
| SPDX | 2.3 | YAML |
| SPDX | 3.0 | JSON-LD |
| CycloneDX | 1.4 | JSON |
| CycloneDX | 1.5 | JSON |
| CycloneDX | 1.6 | JSON |
| CycloneDX | 1.7 | JSON |
CycloneDX 的默认版本是 1.7。但是,可以通过将环境变量 LIB4SBOM_CYCLONEDX_VERSION 设置为 "1.4"、"1.5" 或 "1.6" 来覆盖该版本。
SPDX 的默认版本是 2.3。但是,可以通过将环境变量 LIB4SBOM_SPDX_VERSION 设置为 "SPDX-2.2" 来覆盖该版本。
**注意** 要生成 3.0 格式的 SPDX SBOM,必须设置环境变量 LIB4SBOM_SPDX3。
可以通过设置环境变量 SBOM_ORGANIZATION 来设置创建 SBOM 的组织。这可以通过在 SBOM 文档中设置 Metadata_Supplier 属性的值来覆盖。
_class_ **SBOMGenerator**(_validate_license: True, sbom_type="spdx", format="tag", application="lib4sbom", version="0.1"_)
这将创建一个简单的 SBOM 生成器对象。可以指定以下可选参数:
_validate_license_ 指示是否根据 [SPDX license identifiers](https://spdx.org/licenses/) 集合验证许可证信息。此选项仅适用于 SPDX SBOM,
因为这对于 CycloneDX SBOM 是强制性的。
_sbom_type_ 指示要生成的 SBOM 的类型。有效选项为 spdx 或 cyclonedx。对 SPDX3 的支持目前处于实验阶段,但可以通过将 sbom_type 设置为 spdx3 来启用。
_format_ 指示要生成的 SBOM 的格式。有效选项为 Tag、JSON 或 YAML。如果指定了无效的格式,
则假设默认格式为 JSON。如果为 SBOM 类型指定了不支持的格式(例如 CycloneDX 的 Tag 或 YAML),则采用默认
格式(SPDX 为 Tag,CycloneDX 为 JSON)。
_application_ 和 _version_ 指定创建 SBOM 的工具的名称和版本。如果未指定,则应用程序名称为 'lib4sbom',版本为 '0.1'。
**方法**
_generate(project_name, sbom_data, filename = "", send_to_output = True)_
该方法生成 SBOM 文件。参数为
_project_name_ 指定项目的名称
_sbom_data_ 包含要用于生成的 SBOM 数据。它包含要包含在 SBOM 中的包、文件和关系的详细信息。
_filename_ 是要生成的文件的名称。默认情况下,输出将生成到控制台。
_send_to_output_ 指示是否将输出发送到文件名。
get_sbom()
以请求的格式返回生成的 SBOM
get_type()
返回生成的 SBOM 的类型(spdx 或 cyclonedx)
get_format()
返回生成的 SBOM 的格式(tag、json 或 yaml 之一)
**示例**
以下代码示例展示了 SBOMGenerator 模块的使用,
将 SBOM 从 Tag Value 格式转换为 YAML 格式。输出发送到控制台。
```
>>> from lib4sbom.parser import SBOMParser
>>> test_parser = SBOMParser()
>>> test_parser.parse_file("test_sbom.spdx")
>>> from lib4sbom.generator import SBOMGenerator
>>> test_generator = SBOMGenerator(format="yaml")
>>> test_generator.get_type()
'spdx'
>>> test_generator.get_format()
'yaml'
>>> test_generator.generate("TestApp",test_parser.get_sbom())
SPDXID: SPDXRef-DOCUMENT
creationInfo:
comment: This document has been automatically generated.
created: '2023-01-24T13:51:36Z'
creators:
- 'Tool: lib4sbom-0.1.0'
licenseListVersion: '3.18'
dataLicense: CC0-1.0
documentNamespace: http://spdx.org/spdxdocs/TestDocument-817c4e4c-eac4-49d9-bc41-65f0972edce8
name: TestDocument
...
- relatedSpdxElement: SPDXRef-Package-4-platformdirs
relationshipType: DESCRIBES
spdxElementId: SPDXRef-DOCUMENT
- relatedSpdxElement: SPDXRef-Package-2-distlib
relationshipType: CONTAINS
spdxElementId: SPDXRef-Package-1-virtualenv
- relatedSpdxElement: SPDXRef-Package-3-filelock
relationshipType: CONTAINS
spdxElementId: SPDXRef-Package-1-virtualenv
- relatedSpdxElement: SPDXRef-Package-4-platformdirs
relationshipType: CONTAINS
spdxElementId: SPDXRef-Package-1-virtualenv
spdxVersion: SPDX-2.3
>>> test_generator.get_sbom()
{'SPDXID': 'SPDXRef-DOCUMENT', 'spdxVersion': 'SPDX-2.3', 'creationInfo': {'comment': 'This document has been automatically generated.', 'creators': ['Tool: lib4sbom-0.1.0'], 'created': '2023-01-24T13:51:36Z', 'licenseListVersion': '3.18'}, 'name': 'TestDocument', 'dataLicense': 'CC0-1.0', 'documentNamespace': 'http://spdx.org/spdxdocs/TestDocument-817c4e4c-eac4-49d9-bc41-65f0972edce8', 'packages': [{'SPDXID': 'SPDXRef-Package-1-virtualenv', 'name': 'virtualenv', 'versionInfo': '20.16.7', 'supplier': 'Person: Bernat_Gabor', 'downloadLocation': 'NONE', 'filesAnalyzed': 'false', 'licenseConcluded': 'MIT', 'licenseDeclared': 'MIT', 'copyrightText': 'NOASSERTION', 'externalRefs': [{'referenceCategory': 'PACKAGE-MANAGER', 'referenceType': 'purl', 'referenceLocator': 'pkg:pypi/virtualenv@20.16.7'}]}, {'SPDXID': 'SPDXRef-Package-2-distlib', 'name': 'distlib', 'versionInfo': '0.3.6', 'supplier': 'Person: Vinay_Sajip', 'downloadLocation': 'NONE', 'filesAnalyzed': 'false', 'licenseConcluded': 'NOASSERTION', 'licenseDeclared': 'NOASSERTION', 'copyrightText': 'NOASSERTION', 'externalRefs': [{'referenceCategory': 'PACKAGE-MANAGER', 'referenceType': 'purl', 'referenceLocator': 'pkg:pypi/distlib@0.3.6'}]}, {'SPDXID': 'SPDXRef-Package-3-filelock', 'name': 'filelock', 'versionInfo': '3.8.0', 'supplier': 'Person: Benedikt_Schmitt', 'downloadLocation': 'NONE', 'filesAnalyzed': 'false', 'licenseConcluded': 'Unlicense', 'licenseDeclared': 'Unlicense', 'copyrightText': 'NOASSERTION', 'externalRefs': [{'referenceCategory': 'PACKAGE-MANAGER', 'referenceType': 'purl', 'referenceLocator': 'pkg:pypi/filelock@3.8.0'}]}, {'SPDXID': 'SPDXRef-Package-4-platformdirs', 'name': 'platformdirs', 'versionInfo': '2.5.4', 'supplier': 'Organization: Unknown', 'downloadLocation': 'NONE', 'filesAnalyzed': 'false', 'licenseConcluded': 'NOASSERTION', 'licenseDeclared': 'NOASSERTION', 'copyrightText': 'NOASSERTION', 'externalRefs': [{'referenceCategory': 'PACKAGE-MANAGER', 'referenceType': 'purl', 'referenceLocator': 'pkg:pypi/platformdirs@2.5.4'}]}], 'relationships': [{'spdxElementId': 'SPDXRef-DOCUMENT', 'relatedSpdxElement': 'SPDXRef-Package-1-virtualenv', 'relationshipType': 'DESCRIBES'}, {'spdxElementId': 'SPDXRef-DOCUMENT', 'relatedSpdxElement': 'SPDXRef-Package-2-distlib', 'relationshipType': 'DESCRIBES'}, {'spdxElementId': 'SPDXRef-DOCUMENT', 'relatedSpdxElement': 'SPDXRef-Package-3-filelock', 'relationshipType': 'DESCRIBES'}, {'spdxElementId': 'SPDXRef-DOCUMENT', 'relatedSpdxElement': 'SPDXRef-Package-4-platformdirs', 'relationshipType': 'DESCRIBES'}, {'spdxElementId': 'SPDXRef-Package-1-virtualenv', 'relatedSpdxElement': 'SPDXRef-Package-2-distlib', 'relationshipType': 'CONTAINS'}, {'spdxElementId': 'SPDXRef-Package-1-virtualenv', 'relatedSpdxElement': 'SPDXRef-Package-3-filelock', 'relationshipType': 'CONTAINS'}, {'spdxElementId': 'SPDXRef-Package-1-virtualenv', 'relatedSpdxElement': 'SPDXRef-Package-4-platformdirs', 'relationshipType': 'CONTAINS'}]}
>>>
```
### SBOMOutput
_class_ **SBOMOutput**(_filename="", output_format="tag"_)
这将创建一个简单的 SBOM 输出对象。可以指定以下可选参数:
_filename_ 指示要生成的 SBOM 的输出目标。如果提供了有效的文件路径并且可以创建文件,则输出将到文件,否则
将输出到控制台。
_output_format_ 指示要生成的 SBOM 的格式。有效选项为 Tag、JSON 或 YAML。如果指定了无效的格式,
则假设默认格式为 Tag。
**方法**
generate_output(dataset) 输出 SBOM 文件。参数为
_dataset_ 包含输出格式的 SBOM 数据。如果 SBOM 数据不是 output_format 参数指定的格式,则不会生成输出。
**注意 有效的数据集通常由 SBOMGenerator 类生成,并通过调用 get_sbom() 方法获得。**
get_type()
返回生成的 SBOM 的目标。文件或控制台
get_format()
返回生成的 SBOM 的格式。Tag、JSON 或 YAML 之一。
**示例**
以下代码示例展示了 SBOMOutput 模块的使用。
```
>>> from lib4sbom.parser import SBOMParser
>>> test_parser = SBOMParser()
>>> test_parser.parse_file("test_sbom.spdx")
>>> from lib4sbom.generator import SBOMGenerator
>>> test_generator = SBOMGenerator(format="json")
>>> test_generator.generate("TestApp",test_parser.get_sbom())
>>> from lib4sbom.output import SBOMOutput
>>> sbom_output = SBOMOutput(filename="testapp.json", output_format="json")
>>> sbom_output.generate_output(test_generator.get_sbom())
>>>
```
### SBOM
_class_ **SBOM**()
这将创建一个简单的 SBOM 对象。此对象包含要包含在 SBOM 中的所有项目,包括
组件和关系。留给操作 SBOM 对象的应用程序根据每个属性的存在情况应用适当的验证。
**方法**
**_Setter 方法_**
对于以下属性,提供了 **_set_attribute(value)_** 方法。请注意,属性名称始终为 _小写_。
例如 set_type()。除非另有说明,否则该方法仅接受一个值参数。在指出的地方,可以定义属性的多个实例。
| Attribute | Multiple | Note |
|-------------------|----------|------|
| Version | No | (1) |
| Type | No | (2) |
| Uuid | No | (3) |
| Bom_Version | No | |
**注意**
1. 这与类型属性指定的 SBOM 规范的版本有关。例如 CycloneDX 为 1.4,SPDX 为 SPDX-2.3。
2. 这是指 SBOM 的类型,即 SPDX 或 CycloneDX。
3. 这与 SBOM 的唯一标识符有关。
**_Getter 方法_**
get_sbom()
以字典形式返回 SBOM 对象。
**示例**
```
>>> from lib4sbom.sbom import SBOM
>>> sbom = SBOM()
>>> sbom.set_type(sbom_type='cyclonedx')
>>> sbom.set_version("1.4")
>>> sbom.set_uuid("urn:uuid:My_uuid_1234")
>>> sbom.set_bom_version("2")
>>> sbom.get_type()
'cyclonedx'
>>> from lib4sbom.data.document import SBOMDocument
>>> my_doc = SBOMDocument()
>>> my_doc.set_metadata_type("firmware")
>>> sbom.add_document(my_doc.get_document())
```
### SBOMDocument 对象
_class_ **SBOMDocument**()
这将创建一个简单的 SBOMDocument 对象。此对象包含可以与 SBOM 关联的属性值。
这包括名称、标识符、文件类型、校验和和许可证信息等属性。由于每个属性都是可选的,因此留给操作
SBOMFile 对象的应用程序根据每个属性的存在情况应用适当的验证。
**方法**
**_Setter 方法_**
对于以下属性,提供了 **_set_attribute(value)_** 方法。请注意,属性名称始终为 _小写_。
例如 set_filetype()。属性名称与 SPDX 规范中 File Object 的属性一致。除非
另有说明,否则该方法仅接受一个值参数。在指出的地方,可以定义属性的多个实例。
| Attribute | Multiple | Note |
|-------------------|----------|------|
| Name | No | |
| Id | No | |
| DataLicense | No | |
| Metadata_Type | No | (1) |
| Metadata_Supplier | No | |
| Metadata_Version | No | |
| Bom_Version | No | |
**注意**
1. 这与 SBOM 描述的组件类型有关。此属性仅用于 CycloneDX SBOM。
还有一个额外的 setter 方法 **set_value**(_attribute, value_),它允许设置任何属性。
`set_value("language", "Rust")`
**_Getter 方法_**
get_document()
以列表形式返回 SBOMDocument 对象。
get_name()
返回 SBOMDocument 对象的名称,如果 SBOMDocument 对象实例中不存在该属性,则返回默认值。
get_value(attribute)
返回属性的值。如果 SBOMDocument 对象实例中不存在该属性,则返回默认值。
**示例**
```
>>> from lib4sbom.data.document import SBOMDocument
>>> sbom_document = SBOMDocument()
>>> sbom_document.set_name("test_file")
>>> sbom_document.set_metadata_type("firmware")
>>> sbom_document.get_name()
'test_file'
>>> from lib4sbom.sbom import SBOM
>>> my_sbom = SBOM()
>>> my_sbom.add_document(sbom_document.get_document())
```
### SBOMFile 对象
_class_ **SBOMFile**()
这将创建一个简单的 SBOM 文件对象。此对象包含可以与 SBOM 中的文件工件关联的属性值。
这包括名称、标识符、文件类型、校验和和许可证信息等属性。由于每个属性都是可选的,因此留给操作
SBOMFile 对象的应用程序根据每个属性的存在情况应用适当的验证。
**方法**
**_Setter 方法_**
对于以下属性,提供了 **_set_attribute(value)_** 方法。请注意,属性名称始终为 _小写_。
例如 set_filetype()。属性名称与 SPDX 规范中 File Object 的属性一致。除非
另有说明,否则该方法仅接受一个值参数。在指出的地方,可以定义属性的多个实例。
| Attribute | Multiple | Note |
|-------------------|----------|------|
| Name | No | |
| Id | No | |
| FileType | Yes | |
| Checksum | Yes | (1) |
| LicenseConcluded | No | |
| LicenceInfoInFile | Yes | |
| LicenceComment | No | |
| CopyrightText | No | |
| Comment | No | |
| Notice | No | |
| Contributor | Yes | |
| Attribution | No | |
**注意**
1. set_checksum 方法接受两个参数:校验和算法(例如 SHA256)和实际校验和值(作为字符串)
还有一个额外的 setter 方法 **set_value**(_attribute, value_),它允许设置任何属性。
`set_value("language", "Rust")`
**_Getter 方法_**
get_file()
以字典形式返回 SBOMFile 对象。属性的值作为字符串返回,除非
允许属性的多个实例,在这种情况下,属性的值作为 List 返回。
get_name()
返回 SBOMFile 对象的名称,如果 SBOMFile 对象实例中不存在 'name' 属性,则返回 None。
get_value(attribute)
返回属性的值。如果 SBOMFile 对象实例中不存在该属性,则返回默认值。
_**Utility 方法**_
initialise() 重新初始化 SBOMFile 对象。与该对象关联的所有数据将被删除。
**示例**
```
>>> from lib4sbom.data.file import SBOMFile
>>> sbom_file = SBOMFile()
>>> sbom_files = {}
>>> sbom_file.initialise()
>>> sbom_file.set_name("test_file.c")
>>> sbom_file.set_licenseconcluded("MIT")
>>> file_hash = <<< some calculation >>>
>>> sbom_file.set_checksum("SHA1", file_hash)
>>> sbom_file.set_id("SPDXRef-File-0001")
>>> sbom_files[sbom_file.get_name()] = sbom_file.get_file()
>>> sbom_file.initialise()
>>> sbom_file.set_name("makefile")
>>> sbom_file.set_licenseconcluded("NOASSERTION")
>>> sbom_file.set_id("SPDXRef-File-0002")
>>> sbom_files[sbom_file.get_name()] = sbom_file.get_file()
>>> from lib4sbom.sbom import SBOM
>>> my_sbom = SBOM()
>>> my_sbom.add_files(sbom_files)
```
### SBOMPackage 对象
_class_ **SBOMPackage**()
这将创建一个简单的 SBOM 包对象。此对象包含可以与 SBOM 中的包或组件工件关联的属性值。
这包括名称、标识符、供应商、版本和许可证信息等属性。由于每个属性都是可选的,因此留给操作
SBOMPackage 对象的应用程序根据每个属性的存在情况应用适当的验证。
**_Setter 方法_**
对于以下属性,提供了 **_set_attribute(value)_** 方法。请注意,属性名称始终为 _小写_。
例如 set_version()。属性名称与 SPDX 规范中 Package Object 的属性一致。除非
另有说明,否则该方法仅接受一个值参数。在指出的地方,可以定义属性的多个实例。
| Attribute | Multiple | Note |
|-------------------|----------|------|
| Name | No | |
| Id | No | |
| Type | No | (1) |
| Checksum | Yes | (2) |
| LicenseConcluded | No | |
| LicenseDeclared | No | (3) |
| LicenceInfoInFile | Yes | |
| LicenceComments | No | |
| FilesAnalysis | No | |
| CopyrightText | No | |
| Comment | No | |
| Originator | No | |
| Supplier | No | |
| Version | No | |
| Homepage | No | |
| Property | Yes | (4) |
| DownloadLocation | No | |
| Description | No | |
| ExternalReference | Yes | (5) |
| Cpe | No | |
| Purl | No | (6) |
| Summary | No | |
| SourceInfo | No | |
| Filename | No | |
**注意**
1. set_type 方法用于指示包的用途(例如 Application、Library、Operating-System)。
2. set_checksum 方法接受两个参数:校验和算法(例如 SHA256)和实际校验和值(作为字符串)
3. set_licensedeclared 方法接受一个可选的第二个参数,即许可证名称。在这种情况下,第一个参数 license 被假定为许可证文本而不是许可证标识。
4. set_property 方法接受两个参数,即属性名称和值。
5. set_externalreference 方法接受三个参数,即类别(SECURITY 或 PACKAGE_MANAGER)、类型(cpe22Type、cpe23Type 或 purl)以及与类型对应的元素。
6. set_cpe 接受一个可选的第二个参数,即 CPE 类型(默认为 cpeType23)。
还有一个额外的 setter 方法 **set_value**(_attribute, value_),它允许设置任何属性。
`set_value("language", "Rust")`
**_Getter 方法_**
get_package()
以字典形式返回 SBOMPackage 对象。属性的值作为字符串返回,除非允许属性的多个实例,在这种情况下,属性的值作为 List 返回。
get_name()
返回 SBOMPackage 对象的名称,如果 SBOMPackage 对象实例中不存在 'name' 属性,则返回 None。
get_value(attribute)
返回属性的值。如果 SBOMPackage 对象实例中不存在该属性,则返回默认值。
get_purl()
以字符串形式返回包的 PURL 标识符,如果未定义 PURL 元素,则返回 None。
get_cpe()
以字符串形式返回包的 CPE 标识符,如果未定义 CPE 元素,则返回 None。
**_Utility 方法_**
initialise() 重新初始化 SBOMPackage 对象。与该对象关联的所有数据将被删除。
**示例**
```
>>> from lib4sbom.data.package import SBOMPackage
>>> sbom_packages = {}
>>> my_package = SBOMPackage()
>>> my_package.set_name("glibc")
>>> my_package.set_version("2.15")
>>> my_package.set_supplier("organisation","gnu")
>>> my_package.set_licensedeclared("GPL3")
>>> sbom_packages[(my_package.get_name(), my_package.get_value('version'))] = my_package.get_package()
>>> my_package.initialise()
>>> my_package.set_name("tomcat")
>>> my_package.set_version("9.0.46")
>>> my_package.set_supplier("organisation","apache")
>>> my_package.set_licensedeclared("Apache-2.0")
>>> sbom_packages[(my_package.get_name(), my_package.get_value('version'))] = my_package.get_package()
>>> from lib4sbom.sbom import SBOM
>>> my_sbom = SBOM()
>>> my_sbom.add_packages(sbom_packages)
```
### SBOMRelationship 对象
_class_ **SBOMRelationship**()
这将创建一个简单的 SBOMRelationship 对象,用于显示 SBOM 中两个项目之间的关系。
由于存在多种类型的关系,留给操作
SBOMRelationship 对象的应用程序应用适当的验证以确保关系的语义正确。
**_Setter 方法_**
set_relationship (source, type, target)
_source_ 和 _target_ 是定义关系的组件的唯一标识符。
_type_ 是定义的关系类型。
**_Getter 方法_**
get_relationship()
以字典形式返回 SBOMRelationship 对象。
**示例**
```
>>> from lib4sbom.data.relationship import SBOMRelationship
>>> sbom_relationships = []
>>> my_relationship = SBOMRelationship()
>>> my_relationship.set_relationship("Package-1","CONTAINS", "Package-2")
>>> sbom_relationships.append(my_relationship)
>>> from lib4sbom.sbom import SBOM
>>> my_sbom = SBOM()
>>> my_sbom.add_relationships(sbom_relationships)
```
### Vulnerability 对象
_class_ **Vulnerability**(validation = None)
这将创建一个简单的漏洞对象,用于定义漏洞的详细信息,通常用于
SBOM 中指定的组件。由于指定漏洞状态的方式有多种,留给操作
Vulnerability 对象的应用程序应用适当的验证以确保漏洞的语义正确。
可以指定以下可选参数:
_validation_ 指示是否根据 [OpenVEX](https://openvex.dev)、[CycloneDX](https://www.cyclonedx.org) 或 [CSAF](https://docs.oasis-open.org/csaf/csaf/v2.0/csaf-v2.0.html) 规范验证状态字段。
**注意** 漏洞对象仅包含在 CyclonedDX SBOM 中
**_Setter 方法_**
对于以下属性,提供了 **_set_attribute(value)_** 方法。请注意,属性名称始终为 _小写_。
例如 set_release()。每个方法接受一个值参数。不允许属性的多个实例。
| Attribute | Multiple | Note |
|-------------------|----------|------|
| Name | No | |
| Id | No | (1) |
| Release | No | |
| Status | No | (2) |
| Comment | No | (3) |
| Description | No | (4) |
**注意**
1. set_id 方法用于指示漏洞的身份,例如 CVE-2021-44228
2. set_status 用于指示漏洞的状态。状态值的验证可以根据
创建 Vulnerability 对象时指定的可选参数 _validation_ 选择性地执行。无效状态由 None 值指示。
3. set_comment 方法用于提供额外信息以支持状态值,例如简要理由
4. set_description 方法用于描述漏洞。
还有一个额外的 setter 方法 **set_value**(_attribute, value_),它允许设置任何属性。
`set_value("bom-ref", "rust@1.2.3")`
**_Getter 方法_**
get_vulnerability()
以字典形式返回漏洞对象。
**示例**
```
>>> from lib4sbom.data.vulnerability import Vulnerability
>>> vulnerabilities = []
>>> vulnerability = Vulnerability(validation="cyclonedx")
>>> vulnerability.set_id("CVE-2023-1235")
>>> vulnerability.set_name("rust")
>>> vulnerability.set_release("1.2.3")
>>> vulnerability.set_value("bom-ref", "rust@1.2.3")
>>> vulnerability.set_status("in_triage")
>>> vulnerabilities.append(vulnerability.get_vulnerability())
>>> from lib4sbom.sbom import SBOM
>>> my_sbom = SBOM()
>>> my_sbom.add_vulnerabilities(vulnerabilities)
```
### Services 对象
_class_ **SBOMService**(validation = None)
这将创建一个简单的软件服务对象,用于定义软件服务的详细信息。
由于指定服务的方式有多种,留给操作
服务对象的应用程序应用适当的验证以确保服务的语义正确。
**注意** 服务对象仅包含在 CyclonedDX SBOM 中
**_Setter 方法_**
对于以下属性,提供了 **_set_attribute(value)_** 方法。请注意,属性名称始终为 _小写_。
例如 set_release()。每个方法接受一个值参数。不允许属性的多个实例。
| Attribute | Multiple | Note |
|------------------|----------|------|
| Name | No | |
| Id | No | (1) |
| Version | No | |
| Provider | No | (2) |
| Endpoint | Yes | |
| Data | Yes | (3) |
| Property | Yes | (4) |
| License | Yes | |
| Exernalreference | Yes | (5) |
| Description | No | |
**注意**
1. set_id 方法用于指示服务的身份。如果未指定,将自动生成一个 id。
2. set_provider 用于指定服务提供商的详细信息。可以指定多个参数(name、url、contactname、email address 和 phone),至少必须指定一个。
3. set_data 方法用于提供额外信息以描述正在交换的数据。有两个强制性参数 flow type("Inbound"、"Outbound"、"Bi-directional" 或 "Unknown")和 classification,以及两个可选参数 name 和 description。
4. set_property 方法接受两个参数,即属性名称和值。
5. set_externalreference 方法接受三个参数:URL、被引用的信息类型和可选注释。
还有一个额外的 setter 方法 **set_value**(_attribute, value_),它允许设置任何属性。
`set_value("trustzone", "Data_DMZ")**_Getter 方法_**
get_service()
以字典形式返回服务对象。
**示例**
```
>>> from lib4sbom.data.service import SBOMService
>>> sbom_services = {}
>>> my_service=SBOMService()
>>> my_service.set_name("Microsoft 365")
>>> my_service.set_version("2022.04")
>>> my_service.set_provider(name="Microsoft Inc.", contact="Fred Flintstone", email="fred@micrsoft.com")
>>> my_service.set_description("Business productivity suite")
>>> my_service.set_value("authenticated",True)
>>> my_service.set_endpoint("www.microsoft.com")
>>> my_service.set_endpoint("www.microsoft.com/owa")
>>> my_service.set_data("Bi-directional","None",description="document")
>>> my_service.set_data("outbound","PII",name="User information")
>>> my_service.set_license("Apache-2.0")
>>> my_service.set_license("MIT")
>>> my_service.set_property("Data_Location","EU")
>>> my_service.set_externalreference("https://www.microsoft.com","Website", "Company website")
>>> sbom_services[(my_service.get_name(), my_service.get_value('version'))] = my_service.get_service()
>>> from lib4sbom.sbom import SBOM
>>> my_sbom = SBOM()
>>> my_sbom.add_services(sbom_services)
```
### Cryptpgraphy 对象
_class_ **SBOMCryptography**(validation = None)
这将创建一个简单的加密对象,用于定义加密项目(算法、证书、协议或相关材料)的详细信息。
由于指定加密对象的方式有多种,留给操作
该对象的应用程序应用适当的验证以确保语义正确。强烈
建议参考 [CycloneDX guide to CBOMs](https://cyclonedx.org/guides/OWASP_CycloneDX-Authoritative-Guide-to-CBOM-en.pdf)。
**注意** 加密对象仅包含在 CyclonedDX SBOM 中,并且是组件的属性。
**_Setter 方法_**
必须调用 **set_type** 方法来定义要定义的加密对象的类型。此方法接受要创建的对象类型('algorithm'、'certificate'、'protocol' 或 'related-crypto-material' 之一)。
为每种类型提供支持方法,如下所示:
- Algorithm
- set_algorithm
- set_keysize
- Certificate
- set_certificate
- set_format
- set_date
- set_state
- set_asset
- Protocol
- set_version
- Material
- set_property
- set_value
下面的示例提供了有关如何使用每种方法的指导。
**_Getter 方法_**
get_cryptography()
以字典形式返回加密对象。
**示例**
```
from lib4sbom.data.cryptography import SBOMCryptoography
from lib4sbom.data.package import SBOMPackage
my_package = SBOMPackage()
my_crypto = SBOMCryptography()
# 随组件包含 crypto
my_package.initialise()
my_package.set_name("RSA-PKCS1-1.5-SHA-256-2048")
my_package.set_type("cryptographic-asset")
my_crypto.initialise()
my_crypto.set_oid("1.3.4.5.6")
my_crypto.set_type("algorithm","signature")
my_crypto.set_keysize("2048")
my_crypto.set_algorithm("RSASSA-PKCS1")
my_crypto.set_value("elipticCurve","bn/bn158")
# 向组件添加 crypto 元素
my_package.set_value("crypto", my_crypto.get_cryptography())
sbom_packages[
(my_package.get_name(), my_package.get_value("version"))
] = my_package.get_package()
# 随组件包含 crypto
my_package.initialise()
my_package.set_name("Wikipedia-cert",)
my_package.set_type("cryptographic-asset")
my_crypto.initialise()
my_crypto.set_type("certificate")
my_crypto.set_certificate(subject = "C=US, ST=California, O=San Fransico, O=Wikipedia",
issuer='C=BE, O=GlbalSign, CN=Acme')
my_crypto.set_state("pre-activation")
my_crypto.set_date("create", "2026-02-13")
my_crypto.set_date("activate", "2026-02-14")
my_crypto.set_asset("publickey","abcd")
my_crypto.set_format("X.509")
my_package.set_value("crypto", my_crypto.get_cryptography())
sbom_packages[
(my_package.get_name(), my_package.get_value("version"))
] = my_package.get_package()
# 随组件包含 crypto
my_package.initialise()
my_package.set_name("Wikipedia",)
my_package.set_type("cryptographic-asset")
my_crypto.initialise()
my_crypto.set_type("protocol", "tls")
my_crypto.set_version("1.3")
my_crypto.set_asset("publickey","abcd")
my_package.set_value("crypto", my_crypto.get_cryptography())
sbom_packages[
(my_package.get_name(), my_package.get_value("version"))
] = my_package.get_package()
# 随组件包含 crypto
my_package.initialise()
my_package.set_name("WikipediaData",)
my_package.set_type("cryptographic-asset")
my_crypto.initialise()
my_crypto.set_type("related-crypto-material", "private-key")
my_crypto.set_state("active")
my_crypto.set_date("activate", "2026-02-16")
my_crypto.set_asset("privatekey","abcd")
my_package.set_value("crypto", my_crypto.get_cryptography())
sbom_packages[
(my_package.get_name(), my_package.get_value("version"))
] = my_package.get_package()
```
## 示例
_examples_ 子目录中包含许多示例脚本。
## 实现说明
在处理 SBOM 文件时做出了以下设计决策:
1. 假设 SBOM 是有效的并包含语法有效的数据。
2. 在 SPDX 格式中,工具假定包的名称位于包的版本和许可证之前。
3. 在 SPDX 格式中,当前实现目前不处理多行元素。
4. 在处理和验证许可证时,应用程序将使用一组同义词尝试将某些许可证标识符映射到正确的 [SPDX License Identifiers](https://spdx.org/licenses/)。但是,
提醒工具的使用者,他们应该断言工具提供的任何数据的质量,特别是在许可证标识符已被修改的情况下。
5. 当解析具有同名和同版本的组件的多个实例的 SBOM 时,仅保留组件的一个实例。如果需要保留
多个实例,请考虑确保组件名称是唯一的。
6. 在验证 SPDX 格式的 SBOM 时,由于没有发布官方 schema,验证仅限于检查某些强制字段的存在。
## 未来开发
1. 支持更高版本的 SPDX (3.0)。
2. 增强 SBOM 数据的验证以检查所有强制元素。
3. 跨模块实现 Python 类型。
4. 将打包基础设施从 setup.py 迁移。
5. 利用第三方 SPDX 和 CycloneDX 解析器和生成器
6. 增加对 SPDX XML 和 RDF 格式的进一步支持
7. 添加 CycloneDX XML 文档的生成器。
8. 实施测试套件。
## 许可证
根据 Apache 2.0 许可证授权。
该工具使用 [SPDX Licenses List](https://github.com/spdx/license-list-data) 的本地副本,该副本根据
[Creative Commons Attribution 3.0 (CC-BY-3.0)](http://creativecommons.org/licenses/by/3.0/) 发布。
该工具使用 CycloneDX schema 的本地副本,该副本根据 [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) 发布
该工具使用源自 [Blue Oak Council's License List](https://blueoakcouncil.org/list) 的信息,该信息根据
[Creative Commons Attribution 1.0 (CC-BY-1.0)](https://creativecommons.org/licenses/by/1.0/) 发布。
## 限制
该工具旨在支持软件开发。该工具的有用性取决于提供给
工具的 SBOM 数据。遗憾的是,该工具无法确定此类 SBOM 文件的有效性或完整性;因此提醒工具的使用者,
他们应该断言提供给工具的任何数据的质量。
解析无效的 SBOM 文件可能会导致不可预测的结果。
## 反馈与贡献
可以通过 GitHub Issues 报告错误和功能请求。
标签:CycloneDX, DevSecOps, GPT, OpenSSF, Python, SBOM, SBOM生成, SBOM解析, SPDX, WebSocket, 上游代理, 人工智能安全, 依赖分析, 包管理, 合规性, 后端开发, 文档安全, 无后门, 漏洞管理, 硬件无关, 组件解析, 跌倒检测, 软件物料清单