Wei Li | dec97b1 | 2023-04-07 16:45:17 -0700 | [diff] [blame^] | 1 | #!/usr/bin/env python3 |
| 2 | # |
| 3 | # Copyright (C) 2023 The Android Open Source Project |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | |
| 17 | """ |
| 18 | Define data classes that model SBOMs defined by SPDX. The data classes could be |
| 19 | written out to different formats (tagvalue, JSON, etc) of SPDX with corresponding |
| 20 | writer utilities. |
| 21 | |
| 22 | Rrefer to SPDX 2.3 spec: https://spdx.github.io/spdx-spec/v2.3/ and go/android-spdx for details of |
| 23 | fields in each data class. |
| 24 | """ |
| 25 | |
| 26 | from dataclasses import dataclass, field |
| 27 | from typing import List |
| 28 | |
| 29 | SPDXID_DOC = 'SPDXRef-DOCUMENT' |
| 30 | SPDXID_PRODUCT = 'SPDXRef-PRODUCT' |
| 31 | SPDXID_PLATFORM = 'SPDXRef-PLATFORM' |
| 32 | |
| 33 | PACKAGE_NAME_PRODUCT = 'PRODUCT' |
| 34 | PACKAGE_NAME_PLATFORM = 'PLATFORM' |
| 35 | |
| 36 | |
| 37 | class PackageExternalRefCategory: |
| 38 | SECURITY = 'SECURITY' |
| 39 | PACKAGE_MANAGER = 'PACKAGE-MANAGER' |
| 40 | PERSISTENT_ID = 'PERSISTENT-ID' |
| 41 | OTHER = 'OTHER' |
| 42 | |
| 43 | |
| 44 | class PackageExternalRefType: |
| 45 | cpe22Type = 'cpe22Type' |
| 46 | cpe23Type = 'cpe23Type' |
| 47 | |
| 48 | |
| 49 | @dataclass |
| 50 | class PackageExternalRef: |
| 51 | category: PackageExternalRefCategory |
| 52 | type: PackageExternalRefType |
| 53 | locator: str |
| 54 | |
| 55 | |
| 56 | @dataclass |
| 57 | class Package: |
| 58 | name: str |
| 59 | id: str |
| 60 | version: str = None |
| 61 | supplier: str = None |
| 62 | download_location: str = None |
| 63 | files_analyzed: bool = False |
| 64 | verification_code: str = None |
| 65 | file_ids: List[str] = field(default_factory=list) |
| 66 | external_refs: List[PackageExternalRef] = field(default_factory=list) |
| 67 | |
| 68 | |
| 69 | @dataclass |
| 70 | class File: |
| 71 | id: str |
| 72 | name: str |
| 73 | checksum: str |
| 74 | |
| 75 | |
| 76 | class RelationshipType: |
| 77 | DESCRIBES = 'DESCRIBES' |
| 78 | VARIANT_OF = 'VARIANT_OF' |
| 79 | GENERATED_FROM = 'GENERATED_FROM' |
| 80 | |
| 81 | |
| 82 | @dataclass |
| 83 | class Relationship: |
| 84 | id1: str |
| 85 | relationship: RelationshipType |
| 86 | id2: str |
| 87 | |
| 88 | |
| 89 | @dataclass |
| 90 | class DocumentExternalReference: |
| 91 | id: str |
| 92 | uri: str |
| 93 | checksum: str |
| 94 | |
| 95 | |
| 96 | @dataclass |
| 97 | class Document: |
| 98 | name: str |
| 99 | namespace: str |
| 100 | id: str = SPDXID_DOC |
| 101 | describes: str = SPDXID_PRODUCT |
| 102 | creators: List[str] = field(default_factory=list) |
| 103 | created: str = None |
| 104 | external_refs: List[DocumentExternalReference] = field(default_factory=list) |
| 105 | packages: List[Package] = field(default_factory=list) |
| 106 | files: List[File] = field(default_factory=list) |
| 107 | relationships: List[Relationship] = field(default_factory=list) |
| 108 | |
| 109 | def add_external_ref(self, external_ref): |
| 110 | if not any(external_ref.uri == ref.uri for ref in self.external_refs): |
| 111 | self.external_refs.append(external_ref) |
| 112 | |
| 113 | def add_package(self, package): |
| 114 | if not any(package.id == p.id for p in self.packages): |
| 115 | self.packages.append(package) |
| 116 | |
| 117 | def add_relationship(self, rel): |
| 118 | if not any(rel.id1 == r.id1 and rel.id2 == r.id2 and rel.relationship == r.relationship |
| 119 | for r in self.relationships): |
| 120 | self.relationships.append(rel) |