1# Copyright (c) 2021 The Linux Foundation
2#
3# SPDX-License-Identifier: Apache-2.0
4
5from dataclasses import dataclass, field
6from enum import Enum
7from typing import Optional
8
9
10# DocumentConfig contains settings used to configure how the SPDX Document
11# should be built.
12@dataclass(eq=True)
13class DocumentConfig:
14    # name of document
15    name: str = ""
16
17    # namespace for this document
18    namespace: str = ""
19
20    # standardized DocumentRef- (including that prefix) that the other
21    # docs will use to refer to this one
22    docRefID: str = ""
23
24
25# Document contains the data assembled by the SBOM builder, to be used to
26# create the actual SPDX Document.
27class Document:
28    # initialize with a DocumentConfig
29    def __init__(self, cfg):
30        super().__init__()
31
32        # configuration - DocumentConfig
33        self.cfg = cfg
34
35        # dict of SPDX ID => Package
36        self.pkgs = {}
37
38        # relationships "owned" by this Document, _not_ those "owned" by its
39        # Packages or Files; will likely be just DESCRIBES
40        self.relationships = []
41
42        # dict of filename (ignoring its directory) => number of times it has
43        # been seen while adding files to this Document; used to calculate
44        # useful SPDX IDs
45        self.timesSeen = {}
46
47        # dict of absolute path on disk => File
48        self.fileLinks = {}
49
50        # set of other Documents that our elements' Relationships refer to
51        self.externalDocuments = set()
52
53        # set of LicenseRef- custom licenses to be declared
54        # may or may not include "LicenseRef-" license prefix
55        self.customLicenseIDs = set()
56
57        # this Document's SHA1 hash, filled in _after_ the Document has been
58        # written to disk, so that others can refer to it
59        self.myDocSHA1 = ""
60
61
62# PackageConfig contains settings used to configure how an SPDX Package should
63# be built.
64@dataclass(eq=True)
65class PackageConfig:
66    # package name
67    name: str = ""
68
69    # SPDX ID, including "SPDXRef-"
70    spdxID: str = ""
71
72    # primary package purpose (ex. "LIBRARY", "APPLICATION", etc.)
73    primaryPurpose: str = ""
74
75    # package URL
76    url: str = ""
77
78    # package version
79    version: str = ""
80
81    # package revision
82    revision: str = ""
83
84    # package supplier or vendor
85    supplier: str = ""
86
87    # package external references
88    externalReferences: list = field(default_factory=list)
89
90    # the Package's declared license
91    declaredLicense: str = "NOASSERTION"
92
93    # the Package's copyright text
94    copyrightText: str = "NOASSERTION"
95
96    # absolute path of the "root" directory on disk, to be used as the
97    # base directory from which this Package's Files will calculate their
98    # relative paths
99    # may want to note this in a Package comment field
100    relativeBaseDir: str = ""
101
102
103# Package contains the data assembled by the SBOM builder, to be used to
104# create the actual SPDX Package.
105class Package:
106    # initialize with:
107    # 1) PackageConfig
108    # 2) the Document that owns this Package
109    def __init__(self, cfg, doc):
110        super().__init__()
111
112        # configuration - PackageConfig
113        self.cfg = cfg
114
115        # Document that owns this Package
116        self.doc = doc
117
118        # verification code, calculated per section 7.9 of SPDX spec v2.3
119        self.verificationCode = ""
120
121        # concluded license for this Package, if
122        # cfg.shouldConcludePackageLicense == True; NOASSERTION otherwise
123        self.concludedLicense = "NOASSERTION"
124
125        # list of licenses found in this Package's Files
126        self.licenseInfoFromFiles = []
127
128        # Files in this Package
129        # dict of SPDX ID => File
130        self.files = {}
131
132        # Relationships "owned" by this Package (e.g., this Package is left
133        # side)
134        self.rlns = []
135
136        # If this Package was a target, which File was its main build product?
137        self.targetBuildFile = None
138
139
140# RelationshipDataElementType defines whether a RelationshipData element
141# (e.g., the "owner" or the "other" element) is a File, a target Package,
142# a Package's ID (as other only, and only where owner type is DOCUMENT),
143# or the SPDX document itself (as owner only).
144class RelationshipDataElementType(Enum):
145    UNKNOWN = 0
146    FILENAME = 1
147    TARGETNAME = 2
148    PACKAGEID = 3
149    DOCUMENT = 4
150
151
152# RelationshipData contains the pre-analysis data about a relationship between
153# Files and/or Packages/targets. It is eventually parsed into a corresponding
154# Relationship after we have organized the SPDX Package and File data.
155@dataclass(eq=True)
156class RelationshipData:
157    # for the "owner" element (e.g., the left side of the Relationship),
158    # is it a filename or a target name (e.g., a Package in the build doc)
159    ownerType: RelationshipDataElementType = RelationshipDataElementType.UNKNOWN
160
161    # owner file absolute path (if ownerType is FILENAME)
162    ownerFileAbspath: str = ""
163
164    # owner target name (if ownerType is TARGETNAME)
165    ownerTargetName: str = ""
166
167    # owner SPDX Document (if ownerType is DOCUMENT)
168    ownerDocument: Optional['Document'] = None
169
170    # for the "other" element (e.g., the right side of the Relationship),
171    # is it a filename or a target name (e.g., a Package in the build doc)
172    otherType: RelationshipDataElementType = RelationshipDataElementType.UNKNOWN
173
174    # other file absolute path (if otherType is FILENAME)
175    otherFileAbspath: str = ""
176
177    # other target name (if otherType is TARGETNAME)
178    otherTargetName: str = ""
179
180    # other package ID (if ownerType is DOCUMENT and otherType is PACKAGEID)
181    otherPackageID: str = ""
182
183    # text string with Relationship type
184    # from table 68 in section 11.1 of SPDX spec v2.3
185    rlnType: str = ""
186
187
188# Relationship contains the post-analysis, processed data about a relationship
189# in a form suitable for creating the actual SPDX Relationship in a particular
190# Document's context.
191@dataclass(eq=True)
192class Relationship:
193    # SPDX ID for left side of relationship
194    # including "SPDXRef-" as well as "DocumentRef-" if needed
195    refA: str = ""
196
197    # SPDX ID for right side of relationship
198    # including "SPDXRef-" as well as "DocumentRef-" if needed
199    refB: str = ""
200
201    # text string with Relationship type
202    # from table 68 in section 11.1 of SPDX spec v2.3
203    rlnType: str = ""
204
205
206# File contains the data needed to create a File element in the context of a
207# particular SPDX Document and Package.
208class File:
209    # initialize with:
210    # 1) Document containing this File
211    # 2) Package containing this File
212    def __init__(self, doc, pkg):
213        super().__init__()
214
215        # absolute path to this file on disk
216        self.abspath = ""
217
218        # relative path for this file, measured from the owning Package's
219        # cfg.relativeBaseDir
220        self.relpath = ""
221
222        # SPDX ID for this file, including "SPDXRef-"
223        self.spdxID = ""
224
225        # SHA1 hash
226        self.sha1 = ""
227
228        # SHA256 hash, if pkg.cfg.doSHA256 == True; empty string otherwise
229        self.sha256 = ""
230
231        # MD5 hash, if pkg.cfg.doMD5 == True; empty string otherwise
232        self.md5 = ""
233
234        # concluded license, if pkg.cfg.shouldConcludeFileLicenses == True;
235        # "NOASSERTION" otherwise
236        self.concludedLicense = "NOASSERTION"
237
238        # license info in file
239        self.licenseInfoInFile = []
240
241        # copyright text
242        self.copyrightText = "NOASSERTION"
243
244        # Relationships "owned" by this File (e.g., this File is left side)
245        self.rlns = []
246
247        # Package that owns this File
248        self.pkg = pkg
249
250        # Document that owns this File
251        self.doc = doc
252