15199d831SAndrew Geissler# 292b42cb3SPatrick Williams# Copyright OpenEmbedded Contributors 392b42cb3SPatrick Williams# 45199d831SAndrew Geissler# SPDX-License-Identifier: GPL-2.0-only 55199d831SAndrew Geissler# 65199d831SAndrew Geissler 793c203f3SPatrick Williams# 893c203f3SPatrick Williams# This library is intended to capture the JSON SPDX specification in a type 993c203f3SPatrick Williams# safe manner. It is not intended to encode any particular OE specific 1093c203f3SPatrick Williams# behaviors, see the sbom.py for that. 1193c203f3SPatrick Williams# 1293c203f3SPatrick Williams# The documented SPDX spec document doesn't cover the JSON syntax for 1393c203f3SPatrick Williams# particular configuration, which can make it hard to determine what the JSON 1493c203f3SPatrick Williams# syntax should be. I've found it is actually much simpler to read the official 1593c203f3SPatrick Williams# SPDX JSON schema which can be found here: https://github.com/spdx/spdx-spec 1693c203f3SPatrick Williams# in schemas/spdx-schema.json 1793c203f3SPatrick Williams# 1893c203f3SPatrick Williams 195199d831SAndrew Geisslerimport hashlib 205199d831SAndrew Geisslerimport itertools 215199d831SAndrew Geisslerimport json 225199d831SAndrew Geissler 235199d831SAndrew GeisslerSPDX_VERSION = "2.2" 245199d831SAndrew Geissler 255199d831SAndrew Geissler 2693c203f3SPatrick Williams# 2793c203f3SPatrick Williams# The following are the support classes that are used to implement SPDX object 2893c203f3SPatrick Williams# 2993c203f3SPatrick Williams 305199d831SAndrew Geisslerclass _Property(object): 3193c203f3SPatrick Williams """ 3293c203f3SPatrick Williams A generic SPDX object property. The different types will derive from this 3393c203f3SPatrick Williams class 3493c203f3SPatrick Williams """ 3593c203f3SPatrick Williams 365199d831SAndrew Geissler def __init__(self, *, default=None): 375199d831SAndrew Geissler self.default = default 385199d831SAndrew Geissler 395199d831SAndrew Geissler def setdefault(self, dest, name): 405199d831SAndrew Geissler if self.default is not None: 415199d831SAndrew Geissler dest.setdefault(name, self.default) 425199d831SAndrew Geissler 435199d831SAndrew Geissler 445199d831SAndrew Geisslerclass _String(_Property): 4593c203f3SPatrick Williams """ 4693c203f3SPatrick Williams A scalar string property for an SPDX object 4793c203f3SPatrick Williams """ 4893c203f3SPatrick Williams 495199d831SAndrew Geissler def __init__(self, **kwargs): 505199d831SAndrew Geissler super().__init__(**kwargs) 515199d831SAndrew Geissler 525199d831SAndrew Geissler def set_property(self, attrs, name): 535199d831SAndrew Geissler def get_helper(obj): 545199d831SAndrew Geissler return obj._spdx[name] 555199d831SAndrew Geissler 565199d831SAndrew Geissler def set_helper(obj, value): 575199d831SAndrew Geissler obj._spdx[name] = value 585199d831SAndrew Geissler 595199d831SAndrew Geissler def del_helper(obj): 605199d831SAndrew Geissler del obj._spdx[name] 615199d831SAndrew Geissler 625199d831SAndrew Geissler attrs[name] = property(get_helper, set_helper, del_helper) 635199d831SAndrew Geissler 645199d831SAndrew Geissler def init(self, source): 655199d831SAndrew Geissler return source 665199d831SAndrew Geissler 675199d831SAndrew Geissler 685199d831SAndrew Geisslerclass _Object(_Property): 6993c203f3SPatrick Williams """ 7093c203f3SPatrick Williams A scalar SPDX object property of a SPDX object 7193c203f3SPatrick Williams """ 7293c203f3SPatrick Williams 735199d831SAndrew Geissler def __init__(self, cls, **kwargs): 745199d831SAndrew Geissler super().__init__(**kwargs) 755199d831SAndrew Geissler self.cls = cls 765199d831SAndrew Geissler 775199d831SAndrew Geissler def set_property(self, attrs, name): 785199d831SAndrew Geissler def get_helper(obj): 795199d831SAndrew Geissler if not name in obj._spdx: 805199d831SAndrew Geissler obj._spdx[name] = self.cls() 815199d831SAndrew Geissler return obj._spdx[name] 825199d831SAndrew Geissler 835199d831SAndrew Geissler def set_helper(obj, value): 845199d831SAndrew Geissler obj._spdx[name] = value 855199d831SAndrew Geissler 865199d831SAndrew Geissler def del_helper(obj): 875199d831SAndrew Geissler del obj._spdx[name] 885199d831SAndrew Geissler 895199d831SAndrew Geissler attrs[name] = property(get_helper, set_helper) 905199d831SAndrew Geissler 915199d831SAndrew Geissler def init(self, source): 925199d831SAndrew Geissler return self.cls(**source) 935199d831SAndrew Geissler 945199d831SAndrew Geissler 955199d831SAndrew Geisslerclass _ListProperty(_Property): 9693c203f3SPatrick Williams """ 9793c203f3SPatrick Williams A list of SPDX properties 9893c203f3SPatrick Williams """ 9993c203f3SPatrick Williams 1005199d831SAndrew Geissler def __init__(self, prop, **kwargs): 1015199d831SAndrew Geissler super().__init__(**kwargs) 1025199d831SAndrew Geissler self.prop = prop 1035199d831SAndrew Geissler 1045199d831SAndrew Geissler def set_property(self, attrs, name): 1055199d831SAndrew Geissler def get_helper(obj): 1065199d831SAndrew Geissler if not name in obj._spdx: 1075199d831SAndrew Geissler obj._spdx[name] = [] 1085199d831SAndrew Geissler return obj._spdx[name] 1095199d831SAndrew Geissler 1107e0e3c0cSAndrew Geissler def set_helper(obj, value): 1117e0e3c0cSAndrew Geissler obj._spdx[name] = list(value) 1127e0e3c0cSAndrew Geissler 1135199d831SAndrew Geissler def del_helper(obj): 1145199d831SAndrew Geissler del obj._spdx[name] 1155199d831SAndrew Geissler 1167e0e3c0cSAndrew Geissler attrs[name] = property(get_helper, set_helper, del_helper) 1175199d831SAndrew Geissler 1185199d831SAndrew Geissler def init(self, source): 1195199d831SAndrew Geissler return [self.prop.init(o) for o in source] 1205199d831SAndrew Geissler 1215199d831SAndrew Geissler 1225199d831SAndrew Geisslerclass _StringList(_ListProperty): 12393c203f3SPatrick Williams """ 12493c203f3SPatrick Williams A list of strings as a property for an SPDX object 12593c203f3SPatrick Williams """ 12693c203f3SPatrick Williams 1275199d831SAndrew Geissler def __init__(self, **kwargs): 1285199d831SAndrew Geissler super().__init__(_String(), **kwargs) 1295199d831SAndrew Geissler 1305199d831SAndrew Geissler 1315199d831SAndrew Geisslerclass _ObjectList(_ListProperty): 13293c203f3SPatrick Williams """ 13393c203f3SPatrick Williams A list of SPDX objects as a property for an SPDX object 13493c203f3SPatrick Williams """ 13593c203f3SPatrick Williams 1365199d831SAndrew Geissler def __init__(self, cls, **kwargs): 1375199d831SAndrew Geissler super().__init__(_Object(cls), **kwargs) 1385199d831SAndrew Geissler 1395199d831SAndrew Geissler 1405199d831SAndrew Geisslerclass MetaSPDXObject(type): 14193c203f3SPatrick Williams """ 14293c203f3SPatrick Williams A metaclass that allows properties (anything derived from a _Property 14393c203f3SPatrick Williams class) to be defined for a SPDX object 14493c203f3SPatrick Williams """ 1455199d831SAndrew Geissler def __new__(mcls, name, bases, attrs): 1465199d831SAndrew Geissler attrs["_properties"] = {} 1475199d831SAndrew Geissler 1485199d831SAndrew Geissler for key in attrs.keys(): 1495199d831SAndrew Geissler if isinstance(attrs[key], _Property): 1505199d831SAndrew Geissler prop = attrs[key] 1515199d831SAndrew Geissler attrs["_properties"][key] = prop 1525199d831SAndrew Geissler prop.set_property(attrs, key) 1535199d831SAndrew Geissler 1545199d831SAndrew Geissler return super().__new__(mcls, name, bases, attrs) 1555199d831SAndrew Geissler 1565199d831SAndrew Geissler 1575199d831SAndrew Geisslerclass SPDXObject(metaclass=MetaSPDXObject): 15893c203f3SPatrick Williams """ 15993c203f3SPatrick Williams The base SPDX object; all SPDX spec classes must derive from this class 16093c203f3SPatrick Williams """ 1615199d831SAndrew Geissler def __init__(self, **d): 1625199d831SAndrew Geissler self._spdx = {} 1635199d831SAndrew Geissler 1645199d831SAndrew Geissler for name, prop in self._properties.items(): 1655199d831SAndrew Geissler prop.setdefault(self._spdx, name) 1665199d831SAndrew Geissler if name in d: 1675199d831SAndrew Geissler self._spdx[name] = prop.init(d[name]) 1685199d831SAndrew Geissler 1695199d831SAndrew Geissler def serializer(self): 1705199d831SAndrew Geissler return self._spdx 1715199d831SAndrew Geissler 1725199d831SAndrew Geissler def __setattr__(self, name, value): 1735199d831SAndrew Geissler if name in self._properties or name == "_spdx": 1745199d831SAndrew Geissler super().__setattr__(name, value) 1755199d831SAndrew Geissler return 1765199d831SAndrew Geissler raise KeyError("%r is not a valid SPDX property" % name) 1775199d831SAndrew Geissler 17893c203f3SPatrick Williams# 17993c203f3SPatrick Williams# These are the SPDX objects implemented from the spec. The *only* properties 18093c203f3SPatrick Williams# that can be added to these objects are ones directly specified in the SPDX 18193c203f3SPatrick Williams# spec, however you may add helper functions to make operations easier. 18293c203f3SPatrick Williams# 18393c203f3SPatrick Williams# Defaults should *only* be specified if the SPDX spec says there is a certain 18493c203f3SPatrick Williams# required value for a field (e.g. dataLicense), or if the field is mandatory 18593c203f3SPatrick Williams# and has some sane "this field is unknown" (e.g. "NOASSERTION") 18693c203f3SPatrick Williams# 18793c203f3SPatrick Williams 18893c203f3SPatrick Williamsclass SPDXAnnotation(SPDXObject): 18993c203f3SPatrick Williams annotationDate = _String() 19093c203f3SPatrick Williams annotationType = _String() 19193c203f3SPatrick Williams annotator = _String() 19293c203f3SPatrick Williams comment = _String() 1935199d831SAndrew Geissler 1945199d831SAndrew Geisslerclass SPDXChecksum(SPDXObject): 1955199d831SAndrew Geissler algorithm = _String() 1965199d831SAndrew Geissler checksumValue = _String() 1975199d831SAndrew Geissler 1985199d831SAndrew Geissler 1995199d831SAndrew Geisslerclass SPDXRelationship(SPDXObject): 2005199d831SAndrew Geissler spdxElementId = _String() 2015199d831SAndrew Geissler relatedSpdxElement = _String() 2025199d831SAndrew Geissler relationshipType = _String() 2035199d831SAndrew Geissler comment = _String() 204eff27476SAndrew Geissler annotations = _ObjectList(SPDXAnnotation) 2055199d831SAndrew Geissler 2065199d831SAndrew Geissler 2075199d831SAndrew Geisslerclass SPDXExternalReference(SPDXObject): 2085199d831SAndrew Geissler referenceCategory = _String() 2095199d831SAndrew Geissler referenceType = _String() 2105199d831SAndrew Geissler referenceLocator = _String() 2115199d831SAndrew Geissler 2125199d831SAndrew Geissler 2135199d831SAndrew Geisslerclass SPDXPackageVerificationCode(SPDXObject): 2145199d831SAndrew Geissler packageVerificationCodeValue = _String() 2155199d831SAndrew Geissler packageVerificationCodeExcludedFiles = _StringList() 2165199d831SAndrew Geissler 2175199d831SAndrew Geissler 2185199d831SAndrew Geisslerclass SPDXPackage(SPDXObject): 219*6aa7eec5SAndrew Geissler ALLOWED_CHECKSUMS = [ 220*6aa7eec5SAndrew Geissler "SHA1", 221*6aa7eec5SAndrew Geissler "SHA224", 222*6aa7eec5SAndrew Geissler "SHA256", 223*6aa7eec5SAndrew Geissler "SHA384", 224*6aa7eec5SAndrew Geissler "SHA512", 225*6aa7eec5SAndrew Geissler "MD2", 226*6aa7eec5SAndrew Geissler "MD4", 227*6aa7eec5SAndrew Geissler "MD5", 228*6aa7eec5SAndrew Geissler "MD6", 229*6aa7eec5SAndrew Geissler ] 230*6aa7eec5SAndrew Geissler 2315199d831SAndrew Geissler name = _String() 2325199d831SAndrew Geissler SPDXID = _String() 2335199d831SAndrew Geissler versionInfo = _String() 2345199d831SAndrew Geissler downloadLocation = _String(default="NOASSERTION") 235db4c27eeSPatrick Williams supplier = _String(default="NOASSERTION") 2365199d831SAndrew Geissler homepage = _String() 2375199d831SAndrew Geissler licenseConcluded = _String(default="NOASSERTION") 2385199d831SAndrew Geissler licenseDeclared = _String(default="NOASSERTION") 2395199d831SAndrew Geissler summary = _String() 2405199d831SAndrew Geissler description = _String() 2415199d831SAndrew Geissler sourceInfo = _String() 2425199d831SAndrew Geissler copyrightText = _String(default="NOASSERTION") 2435199d831SAndrew Geissler licenseInfoFromFiles = _StringList(default=["NOASSERTION"]) 2445199d831SAndrew Geissler externalRefs = _ObjectList(SPDXExternalReference) 2455199d831SAndrew Geissler packageVerificationCode = _Object(SPDXPackageVerificationCode) 2465199d831SAndrew Geissler hasFiles = _StringList() 2475199d831SAndrew Geissler packageFileName = _String() 24893c203f3SPatrick Williams annotations = _ObjectList(SPDXAnnotation) 249*6aa7eec5SAndrew Geissler checksums = _ObjectList(SPDXChecksum) 2505199d831SAndrew Geissler 2515199d831SAndrew Geissler 2525199d831SAndrew Geisslerclass SPDXFile(SPDXObject): 2535199d831SAndrew Geissler SPDXID = _String() 2545199d831SAndrew Geissler fileName = _String() 2555199d831SAndrew Geissler licenseConcluded = _String(default="NOASSERTION") 2565199d831SAndrew Geissler copyrightText = _String(default="NOASSERTION") 2575199d831SAndrew Geissler licenseInfoInFiles = _StringList(default=["NOASSERTION"]) 2585199d831SAndrew Geissler checksums = _ObjectList(SPDXChecksum) 2595199d831SAndrew Geissler fileTypes = _StringList() 2605199d831SAndrew Geissler 2615199d831SAndrew Geissler 2625199d831SAndrew Geisslerclass SPDXCreationInfo(SPDXObject): 2635199d831SAndrew Geissler created = _String() 2645199d831SAndrew Geissler licenseListVersion = _String() 2655199d831SAndrew Geissler comment = _String() 2665199d831SAndrew Geissler creators = _StringList() 2675199d831SAndrew Geissler 2685199d831SAndrew Geissler 2695199d831SAndrew Geisslerclass SPDXExternalDocumentRef(SPDXObject): 2705199d831SAndrew Geissler externalDocumentId = _String() 2715199d831SAndrew Geissler spdxDocument = _String() 2725199d831SAndrew Geissler checksum = _Object(SPDXChecksum) 2735199d831SAndrew Geissler 2745199d831SAndrew Geissler 2755199d831SAndrew Geisslerclass SPDXExtractedLicensingInfo(SPDXObject): 2765199d831SAndrew Geissler name = _String() 2775199d831SAndrew Geissler comment = _String() 2785199d831SAndrew Geissler licenseId = _String() 2795199d831SAndrew Geissler extractedText = _String() 2805199d831SAndrew Geissler 2815199d831SAndrew Geissler 2825199d831SAndrew Geisslerclass SPDXDocument(SPDXObject): 2835199d831SAndrew Geissler spdxVersion = _String(default="SPDX-" + SPDX_VERSION) 2845199d831SAndrew Geissler dataLicense = _String(default="CC0-1.0") 2855199d831SAndrew Geissler SPDXID = _String(default="SPDXRef-DOCUMENT") 2865199d831SAndrew Geissler name = _String() 2875199d831SAndrew Geissler documentNamespace = _String() 2885199d831SAndrew Geissler creationInfo = _Object(SPDXCreationInfo) 2895199d831SAndrew Geissler packages = _ObjectList(SPDXPackage) 2905199d831SAndrew Geissler files = _ObjectList(SPDXFile) 2915199d831SAndrew Geissler relationships = _ObjectList(SPDXRelationship) 2925199d831SAndrew Geissler externalDocumentRefs = _ObjectList(SPDXExternalDocumentRef) 2935199d831SAndrew Geissler hasExtractedLicensingInfos = _ObjectList(SPDXExtractedLicensingInfo) 2945199d831SAndrew Geissler 2955199d831SAndrew Geissler def __init__(self, **d): 2965199d831SAndrew Geissler super().__init__(**d) 2975199d831SAndrew Geissler 2985199d831SAndrew Geissler def to_json(self, f, *, sort_keys=False, indent=None, separators=None): 2995199d831SAndrew Geissler class Encoder(json.JSONEncoder): 3005199d831SAndrew Geissler def default(self, o): 3015199d831SAndrew Geissler if isinstance(o, SPDXObject): 3025199d831SAndrew Geissler return o.serializer() 3035199d831SAndrew Geissler 3045199d831SAndrew Geissler return super().default(o) 3055199d831SAndrew Geissler 3065199d831SAndrew Geissler sha1 = hashlib.sha1() 3075199d831SAndrew Geissler for chunk in Encoder( 3085199d831SAndrew Geissler sort_keys=sort_keys, 3095199d831SAndrew Geissler indent=indent, 3105199d831SAndrew Geissler separators=separators, 3115199d831SAndrew Geissler ).iterencode(self): 3125199d831SAndrew Geissler chunk = chunk.encode("utf-8") 3135199d831SAndrew Geissler f.write(chunk) 3145199d831SAndrew Geissler sha1.update(chunk) 3155199d831SAndrew Geissler 3165199d831SAndrew Geissler return sha1.hexdigest() 3175199d831SAndrew Geissler 3185199d831SAndrew Geissler @classmethod 3195199d831SAndrew Geissler def from_json(cls, f): 3205199d831SAndrew Geissler return cls(**json.load(f)) 3215199d831SAndrew Geissler 322eff27476SAndrew Geissler def add_relationship(self, _from, relationship, _to, *, comment=None, annotation=None): 3235199d831SAndrew Geissler if isinstance(_from, SPDXObject): 3245199d831SAndrew Geissler from_spdxid = _from.SPDXID 3255199d831SAndrew Geissler else: 3265199d831SAndrew Geissler from_spdxid = _from 3275199d831SAndrew Geissler 3285199d831SAndrew Geissler if isinstance(_to, SPDXObject): 3295199d831SAndrew Geissler to_spdxid = _to.SPDXID 3305199d831SAndrew Geissler else: 3315199d831SAndrew Geissler to_spdxid = _to 3325199d831SAndrew Geissler 3335199d831SAndrew Geissler r = SPDXRelationship( 3345199d831SAndrew Geissler spdxElementId=from_spdxid, 3355199d831SAndrew Geissler relatedSpdxElement=to_spdxid, 3365199d831SAndrew Geissler relationshipType=relationship, 3375199d831SAndrew Geissler ) 3385199d831SAndrew Geissler 3395199d831SAndrew Geissler if comment is not None: 3405199d831SAndrew Geissler r.comment = comment 3415199d831SAndrew Geissler 342eff27476SAndrew Geissler if annotation is not None: 343eff27476SAndrew Geissler r.annotations.append(annotation) 344eff27476SAndrew Geissler 3455199d831SAndrew Geissler self.relationships.append(r) 3465199d831SAndrew Geissler 3475199d831SAndrew Geissler def find_by_spdxid(self, spdxid): 3485199d831SAndrew Geissler for o in itertools.chain(self.packages, self.files): 3495199d831SAndrew Geissler if o.SPDXID == spdxid: 3505199d831SAndrew Geissler return o 3515199d831SAndrew Geissler return None 3525199d831SAndrew Geissler 3535199d831SAndrew Geissler def find_external_document_ref(self, namespace): 3545199d831SAndrew Geissler for r in self.externalDocumentRefs: 3555199d831SAndrew Geissler if r.spdxDocument == namespace: 3565199d831SAndrew Geissler return r 3575199d831SAndrew Geissler return None 358