Create separate python libraries for the following logic and refactor SBOM generation script accordingly.

1) writer classes of generating SBOM in different SPDX formats
2) data classes to model the SBOM structure in SPDX

Bug: 272358880
Test: CIs
Test: build/soong/tests/sbom_test.sh
Test: atest --host sbom_writers_test

Change-Id: I1175cf0d99864bc4304559a59484ef0ba401cd64
This commit is contained in:
Wei Li
2023-04-07 16:45:17 -07:00
parent a0ffed1fa1
commit dec97b1462
9 changed files with 1020 additions and 285 deletions

120
tools/sbom/sbom_data.py Normal file
View File

@@ -0,0 +1,120 @@
#!/usr/bin/env python3
#
# Copyright (C) 2023 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Define data classes that model SBOMs defined by SPDX. The data classes could be
written out to different formats (tagvalue, JSON, etc) of SPDX with corresponding
writer utilities.
Rrefer to SPDX 2.3 spec: https://spdx.github.io/spdx-spec/v2.3/ and go/android-spdx for details of
fields in each data class.
"""
from dataclasses import dataclass, field
from typing import List
SPDXID_DOC = 'SPDXRef-DOCUMENT'
SPDXID_PRODUCT = 'SPDXRef-PRODUCT'
SPDXID_PLATFORM = 'SPDXRef-PLATFORM'
PACKAGE_NAME_PRODUCT = 'PRODUCT'
PACKAGE_NAME_PLATFORM = 'PLATFORM'
class PackageExternalRefCategory:
SECURITY = 'SECURITY'
PACKAGE_MANAGER = 'PACKAGE-MANAGER'
PERSISTENT_ID = 'PERSISTENT-ID'
OTHER = 'OTHER'
class PackageExternalRefType:
cpe22Type = 'cpe22Type'
cpe23Type = 'cpe23Type'
@dataclass
class PackageExternalRef:
category: PackageExternalRefCategory
type: PackageExternalRefType
locator: str
@dataclass
class Package:
name: str
id: str
version: str = None
supplier: str = None
download_location: str = None
files_analyzed: bool = False
verification_code: str = None
file_ids: List[str] = field(default_factory=list)
external_refs: List[PackageExternalRef] = field(default_factory=list)
@dataclass
class File:
id: str
name: str
checksum: str
class RelationshipType:
DESCRIBES = 'DESCRIBES'
VARIANT_OF = 'VARIANT_OF'
GENERATED_FROM = 'GENERATED_FROM'
@dataclass
class Relationship:
id1: str
relationship: RelationshipType
id2: str
@dataclass
class DocumentExternalReference:
id: str
uri: str
checksum: str
@dataclass
class Document:
name: str
namespace: str
id: str = SPDXID_DOC
describes: str = SPDXID_PRODUCT
creators: List[str] = field(default_factory=list)
created: str = None
external_refs: List[DocumentExternalReference] = field(default_factory=list)
packages: List[Package] = field(default_factory=list)
files: List[File] = field(default_factory=list)
relationships: List[Relationship] = field(default_factory=list)
def add_external_ref(self, external_ref):
if not any(external_ref.uri == ref.uri for ref in self.external_refs):
self.external_refs.append(external_ref)
def add_package(self, package):
if not any(package.id == p.id for p in self.packages):
self.packages.append(package)
def add_relationship(self, rel):
if not any(rel.id1 == r.id1 and rel.id2 == r.id2 and rel.relationship == r.relationship
for r in self.relationships):
self.relationships.append(rel)