Skip to content

Commit a5ebbc1

Browse files
committed
Add purl information to SPDX reports
This commit adds a new function, `get_purl()` to spdx_common.py which uses the packageurl library to generate purl strings for given package objects. The namespace for certain purls is determined using the /etc/os-release file information collected via the `pkg_suppliers` field in base.yml. This commit then adds purl strings as external references[1] to both SPDX tag value and SPDX json reports. [1]https://spdx.github.io/spdx-spec/v2.3/package-information/#721-external-reference-field Resolves #1206 Signed-off-by: Rose Judge <[email protected]> Signed-off-by: Ivana Atanasova <[email protected]>
1 parent eec8761 commit a5ebbc1

File tree

3 files changed

+43
-2
lines changed

3 files changed

+43
-2
lines changed

tern/formats/spdx/spdx_common.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from license_expression import get_spdx_licensing
1717
from tern.utils import constants
1818
from tern.formats.spdx.spdxtagvalue import formats as spdx_formats
19+
from packageurl import PackageURL
1920

2021
# global logger
2122
logger = logging.getLogger(constants.logger_name)
@@ -210,3 +211,34 @@ def get_file_comment(filedata):
210211
comment = comment + \
211212
'{}: {}'.format(notice.level, notice.message) + '\n'
212213
return comment
214+
215+
216+
#######################
217+
# Common PURL Helpers #
218+
#######################
219+
220+
purl_types_with_namespaces = [
221+
'deb',
222+
'rpm',
223+
'apk',
224+
'alpm'
225+
]
226+
227+
228+
def get_purl(package_obj):
229+
'''Return a purl string for a given package'''
230+
purl_type = package_obj.pkg_format
231+
purl_namespace = ''
232+
if purl_type in purl_types_with_namespaces and package_obj.pkg_supplier:
233+
# https://github.com/package-url/purl-spec/pull/214
234+
if package_obj.pkg_supplier.split(' ')[0] == "VMware":
235+
purl_namespace = package_obj.pkg_supplier.split(' ')[1].lower()
236+
else:
237+
purl_namespace = package_obj.pkg_supplier.split(' ')[0].lower()
238+
# TODO- this might need adjusting for alpm. Currently can't test on M1
239+
purl = PackageURL(purl_type, purl_namespace, package_obj.name.lower(), package_obj.version,
240+
qualifiers={'arch': package_obj.arch if package_obj.arch else ''})
241+
try:
242+
return purl.to_string()
243+
except ValueError:
244+
return ''

tern/formats/spdx/spdxjson/package_helpers.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,14 @@ def get_package_dict(package, template):
7474
mapping['PackageLicenseDeclared']),
7575
'copyrightText': mapping['PackageCopyrightText'] if
7676
mapping['PackageCopyrightText'] else 'NONE',
77-
'comment': get_package_comment(package)
7877
}
79-
78+
# Only add package PURL if it exists
79+
if spdx_common.get_purl(package):
80+
package_dict['externalRefs'] = [{'referenceCategory': 'PACKAGE-MANAGER',
81+
'referenceLocator': spdx_common.get_purl(package),
82+
'referenceType': 'purl'}]
83+
# Put package comment after any potential externalRefs
84+
package_dict['comment'] = get_package_comment(package)
8085
return package_dict
8186

8287

tern/formats/spdx/spdxtagvalue/package_helpers.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ def get_package_block(package_obj, template):
115115
message=mapping['PackageCopyrightText']) + '\n'
116116
else:
117117
block += 'PackageCopyrightText: NONE\n'
118+
# Package URL
119+
if spdx_common.get_purl(package_obj):
120+
block += 'ExternalRef: PACKAGE-MANAGER purl {}\n'.format(
121+
spdx_common.get_purl(package_obj))
118122
# Package Comments
119123
block += get_package_comment(package_obj)
120124
return block

0 commit comments

Comments
 (0)