xref: /openbmc/openbmc/poky/meta/lib/oe/spdx.py (revision 6aa7eec5002756f5398774a35fb9d985e15a4573)
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