mirror of
https://github.com/opnsense/core.git
synced 2026-10-11 22:40:01 +00:00
contrib: add a tool to generate an SPDX formatted software bill of materials (SBOM) using information available in our package database.
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
# SPDX Generator
|
||||
|
||||
A small tool for generating **SPDX 3** files for software packages.
|
||||
|
||||
## Usage
|
||||
|
||||
Provide the package name:
|
||||
|
||||
```bash
|
||||
spdx-generator.py <package-name>
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
spdx-generator.py opnsense
|
||||
```
|
||||
|
||||
The tool generates an SPDX formatted software bill of materials for the package offered as parameter.
|
||||
|
||||
|
||||
# TODO
|
||||
|
||||
* normalize licenseExpression to spdx standards
|
||||
* implement creationInfo tag properly
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
Copyright (c) 2026 Ad Schellevis <[email protected]>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
@@ -0,0 +1,101 @@
|
||||
"""
|
||||
Copyright (c) 2026 Ad Schellevis <[email protected]>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
--------------------------------------------------------------------------------------------------------------------
|
||||
Interface to pkg
|
||||
"""
|
||||
import subprocess
|
||||
import sqlite3
|
||||
|
||||
|
||||
class SimpleContainer:
|
||||
def __init__(self, payload:dict):
|
||||
self._payload = payload
|
||||
self._deps = []
|
||||
|
||||
def add_dependency(self, container:"SimpleContainer"):
|
||||
self._deps.append(container)
|
||||
|
||||
def __iter__(self):
|
||||
""" recursively collect all packages and their dependencies
|
||||
"""
|
||||
if self._deps:
|
||||
yield self, self._deps
|
||||
for dep in self._deps:
|
||||
yield from dep
|
||||
else:
|
||||
yield self, None
|
||||
|
||||
def __getattr__(self, prop):
|
||||
""" convenient wrapper access _payload properties
|
||||
"""
|
||||
return self._payload[prop]
|
||||
|
||||
def get_data(self):
|
||||
return self._payload
|
||||
|
||||
|
||||
class Pkg:
|
||||
def __init__(self):
|
||||
self._pkg_map = {}
|
||||
self._index_pkg()
|
||||
|
||||
@staticmethod
|
||||
def _fetch_details():
|
||||
result = {}
|
||||
dbdir = subprocess.run(['pkg', 'config', 'PKG_DBDIR'], text=True, capture_output=True).stdout.strip()
|
||||
dburi = "file:%s/local.sqlite?mode=ro" % dbdir
|
||||
con = sqlite3.connect(dburi, uri=True)
|
||||
con.row_factory = sqlite3.Row
|
||||
for row in con.execute('select name, desc, comment from packages'):
|
||||
result[row['name']] = row
|
||||
return result
|
||||
|
||||
def _index_pkg(self):
|
||||
pkg_details = self._fetch_details()
|
||||
args = ['/usr/sbin/pkg', 'rquery', '%n|%v|%o|%L|%m|%l|%w']
|
||||
for line in subprocess.run(args, text=True, capture_output=True).stdout.split("\n"):
|
||||
parts = line.split('|')
|
||||
if len(parts) > 1:
|
||||
pkgname = parts[0]
|
||||
self._pkg_map[pkgname] = SimpleContainer({
|
||||
'id': '%s:%s' % (pkgname, parts[1]),
|
||||
'name': pkgname,
|
||||
'packageVersion': parts[1],
|
||||
'origin': parts[2],
|
||||
'licenseExpression': (' %s ' % parts[5]).join([x.strip() for x in parts[3].split(',')]),
|
||||
'maintainer': parts[4],
|
||||
'homepage': parts[5],
|
||||
'comment': pkg_details[pkgname]['comment'] if pkgname in pkg_details else '',
|
||||
'description': pkg_details[pkgname]['desc'] if pkgname in pkg_details else '',
|
||||
'primaryPurpose': 'library' if pkgname.startswith('lib') else 'application'
|
||||
})
|
||||
args = ['/usr/sbin/pkg', 'rquery', '%n|%dn']
|
||||
for line in subprocess.run(args, text=True, capture_output=True).stdout.split("\n"):
|
||||
parts = line.split('|')
|
||||
if len(parts) == 2:
|
||||
self._pkg_map[parts[0]].add_dependency(self._pkg_map[parts[1]])
|
||||
|
||||
def query(self, pkg):
|
||||
return self._pkg_map[pkg] if pkg in self._pkg_map else None
|
||||
@@ -0,0 +1,93 @@
|
||||
"""
|
||||
Copyright (c) 2026 Ad Schellevis <[email protected]>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
--------------------------------------------------------------------------------------------------------------------
|
||||
Hightly simplified wrapper around the SPDX format used to generate software bill of materials (SBOM)
|
||||
More information about the format can be found at https://spdx.github.io/spdx-spec/v3.0.1/
|
||||
"""
|
||||
from .spdx_agent import SpdxAgent
|
||||
from .spdx_creation_info import SpdxCreationInfo
|
||||
from .spdx_element import SpdxElement
|
||||
from .spdx_license import SpdxLicenseExpression
|
||||
from .spdx_package import SpdxPackage
|
||||
from .spdx_relationship import SpdxRelationship
|
||||
|
||||
|
||||
class SpdxSoftwareSbom:
|
||||
@staticmethod
|
||||
def spdx_type():
|
||||
return 'software_Sbom'
|
||||
|
||||
def get_data(self):
|
||||
# XXX: temp static block
|
||||
return {
|
||||
'spdxId': 'urn:opnsense:sbom',
|
||||
'type': 'software_Sbom',
|
||||
'creationInfo': '_:b0',
|
||||
'profileConformance': [
|
||||
'core',
|
||||
'software'
|
||||
],
|
||||
'rootElement': self._rootElement.spdxId
|
||||
}
|
||||
|
||||
def __init__(self, root:SpdxElement):
|
||||
self._rootElement = root
|
||||
|
||||
|
||||
class SpdxDocument:
|
||||
def __init__(self, root:SpdxElement):
|
||||
self._contents = {
|
||||
'@id': SpdxCreationInfo(), # XXX: replace
|
||||
'urn:opnsense:sbom': root # XXX: static
|
||||
}
|
||||
# XXX static data from SpdxCreationInfo
|
||||
self.add_object(SpdxAgent(spdxId='urn:opnsense:organization:opnsense', name='OPNsense'))
|
||||
|
||||
def add_object(self, obj:SpdxElement):
|
||||
self._contents[obj.spdxId] = obj
|
||||
return obj
|
||||
|
||||
def get_data(self):
|
||||
# XXX: temp static block
|
||||
return {
|
||||
'spdxId': 'urn:opnsense:document',
|
||||
'type': 'SpdxDocument',
|
||||
'creationInfo': '_:b0',
|
||||
'profileConformance': [
|
||||
'core',
|
||||
'software'
|
||||
],
|
||||
'rootElement': 'urn:opnsense:sbom'
|
||||
}
|
||||
|
||||
def dump(self):
|
||||
items = [self.get_data()]
|
||||
for item in self._contents.values():
|
||||
items.append(item.get_data())
|
||||
|
||||
return {
|
||||
'@context': 'https://spdx.org/rdf/3.0.1/spdx-context.jsonld',
|
||||
'@graph': items
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
"""
|
||||
Copyright (c) 2026 Ad Schellevis <[email protected]>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
from .spdx_element import SpdxElement
|
||||
|
||||
|
||||
class SpdxAgent(SpdxElement):
|
||||
@staticmethod
|
||||
def spdx_type():
|
||||
return 'Agent'
|
||||
@@ -0,0 +1,46 @@
|
||||
"""
|
||||
Copyright (c) 2026 Ad Schellevis <[email protected]>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
--------------------------------------------------------------------------------------------------------------------
|
||||
Hightly simplified wrapper around the SPDX format used to generate software bill of materials (SBOM)
|
||||
"""
|
||||
import datetime
|
||||
|
||||
|
||||
class SpdxCreationInfo:
|
||||
@property
|
||||
def id(self):
|
||||
return '_:b0'
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
'@id': self.id,
|
||||
'type': 'CreationInfo',
|
||||
'created': datetime.datetime.now(datetime.timezone.utc).isoformat(),
|
||||
'createdBy': [
|
||||
'urn:opnsense:organization:opnsense'
|
||||
],
|
||||
'specVersion': '3.0.1'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
"""
|
||||
Copyright (c) 2026 Ad Schellevis <[email protected]>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
import uuid
|
||||
|
||||
|
||||
class SpdxElement:
|
||||
_all_elements = {} # elements should be unique per spdxId
|
||||
|
||||
@staticmethod
|
||||
def spdx_type():
|
||||
return 'Element'
|
||||
|
||||
@classmethod
|
||||
def spdx_id_constructor(cls, id):
|
||||
return 'urn:opnsense:%s:%s' % (cls.__name__.lstrip('Spdx').lower(), id)
|
||||
|
||||
@classmethod
|
||||
def _get_properties(cls):
|
||||
return {
|
||||
'spdxId': '',
|
||||
'comment': '',
|
||||
'name': '',
|
||||
'description': '',
|
||||
'creationInfo': '_:b0' # XXX: should replace
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
payload = {}
|
||||
for k,v in self._get_properties().items():
|
||||
val = kwargs[k] if k in kwargs else v
|
||||
payload[k] = v(val) if val and callable(v) else val
|
||||
|
||||
if 'spdxId' not in kwargs:
|
||||
# generate spdxId when not offered
|
||||
payload['spdxId'] = self.spdx_id_constructor(kwargs.get('id', str(uuid.uuid4())))
|
||||
|
||||
payload['type'] = self.spdx_type()
|
||||
self._spdxId = payload['spdxId']
|
||||
self._all_elements[self._spdxId] = payload
|
||||
|
||||
def __getattr__(self, prop):
|
||||
""" convenient wrapper to make Spdx attributes directly accessible
|
||||
"""
|
||||
return self._all_elements[self._spdxId][prop]
|
||||
|
||||
def get_data(self):
|
||||
return self._all_elements[self._spdxId]
|
||||
@@ -0,0 +1,44 @@
|
||||
"""
|
||||
Copyright (c) 2026 Ad Schellevis <[email protected]>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
from .spdx_element import SpdxElement
|
||||
|
||||
|
||||
class SpdxAnyLicenseInfo(SpdxElement):
|
||||
pass
|
||||
|
||||
class SpdxLicenseExpression(SpdxAnyLicenseInfo):
|
||||
@staticmethod
|
||||
def spdx_type():
|
||||
return 'simplelicensing_LicenseExpression'
|
||||
|
||||
@classmethod
|
||||
def _get_properties(cls):
|
||||
result = super()._get_properties()
|
||||
result.update({
|
||||
'licenseExpression': ''
|
||||
})
|
||||
return result
|
||||
@@ -0,0 +1,43 @@
|
||||
"""
|
||||
Copyright (c) 2026 Ad Schellevis <[email protected]>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
from .spdx_element import SpdxElement
|
||||
|
||||
|
||||
class SpdxPackage(SpdxElement):
|
||||
@staticmethod
|
||||
def spdx_type():
|
||||
return 'software_Package'
|
||||
|
||||
@classmethod
|
||||
def _get_properties(cls):
|
||||
result = super()._get_properties()
|
||||
result.update({
|
||||
'homePage': '',
|
||||
'packageVersion': '',
|
||||
'primaryPurpose': ''
|
||||
})
|
||||
return result
|
||||
@@ -0,0 +1,43 @@
|
||||
"""
|
||||
Copyright (c) 2026 Ad Schellevis <[email protected]>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
from .spdx_element import SpdxElement
|
||||
|
||||
|
||||
class SpdxRelationship(SpdxElement):
|
||||
@staticmethod
|
||||
def spdx_type():
|
||||
return 'Relationship'
|
||||
|
||||
@classmethod
|
||||
def _get_properties(cls):
|
||||
result = super()._get_properties()
|
||||
result.update({
|
||||
'relationshipType': '',
|
||||
'from': '',
|
||||
'to': ''
|
||||
})
|
||||
return result
|
||||
Executable
+71
@@ -0,0 +1,71 @@
|
||||
#!/usr/local/bin/python3
|
||||
|
||||
"""
|
||||
Copyright (c) 2026 Ad Schellevis <[email protected]>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
from lib.pkg import Pkg
|
||||
from lib.spdx import *
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('pkg', help='package name to inspect')
|
||||
inputargs = parser.parse_args()
|
||||
pkg = Pkg().query(inputargs.pkg)
|
||||
doc = SpdxDocument(
|
||||
SpdxSoftwareSbom(SpdxPackage(**pkg.get_data()))
|
||||
)
|
||||
|
||||
for frm, to in pkg:
|
||||
pkg = doc.add_object(SpdxPackage(**frm.get_data()))
|
||||
lic = doc.add_object(
|
||||
SpdxLicenseExpression(**{
|
||||
'id': frm.licenseExpression.replace(' ', '_').lower(),
|
||||
'licenseExpression': frm.licenseExpression
|
||||
})
|
||||
)
|
||||
doc.add_object(
|
||||
SpdxRelationship(**{
|
||||
'relationshipType': 'hasConcludedLicense',
|
||||
'id': '%s_lic' % frm.id,
|
||||
'from': pkg.spdxId,
|
||||
'to': [lic.spdxId]
|
||||
})
|
||||
)
|
||||
|
||||
if to:
|
||||
doc.add_object(
|
||||
SpdxRelationship(**{
|
||||
'relationshipType': 'dependsOn',
|
||||
'id': '%s-%s' % (frm.id, '_'.join([x.id for x in to])),
|
||||
'from': pkg.spdxId,
|
||||
'to': [SpdxPackage.spdx_id_constructor(x.id) for x in to]
|
||||
})
|
||||
)
|
||||
|
||||
print(json.dumps(doc.dump(), indent=4))
|
||||
Reference in New Issue
Block a user