diff --git a/tern/formats/spdx/spdxjson/generator.py b/tern/formats/spdx/spdxjson/generator.py index 6c28c5e1..a9903f99 100644 --- a/tern/formats/spdx/spdxjson/generator.py +++ b/tern/formats/spdx/spdxjson/generator.py @@ -9,6 +9,7 @@ import json import logging +import pickle from tern.formats.spdx.spdx import SPDX from tern.formats.spdx import spdx_common @@ -158,6 +159,7 @@ def generate(self, image_obj_list, print_inclusive=False): # images. Hence we will assume only one image is analyzed and the # input is a list of length 1 image_obj = image_obj_list[0] + template = SPDX() report = get_document_dict(image_obj, template) diff --git a/tern/formats/spdx_new/layer_helpers.py b/tern/formats/spdx_new/layer_helpers.py index 832688ab..24308b27 100644 --- a/tern/formats/spdx_new/layer_helpers.py +++ b/tern/formats/spdx_new/layer_helpers.py @@ -109,7 +109,7 @@ def get_layer_dict(layer_obj: ImageLayer) -> Tuple[SpdxPackage, List[Relationshi The analyzed files will go in a separate part of the document.""" comment = get_layer_package_comment(layer_obj) - verification_code = get_layer_verification_code(layer_obj) + verification_code = get_layer_verification_code(layer_obj) if layer_obj.files_analyzed else None layer_licenses = get_layer_licenses(layer_obj) license_info_from_files = [] @@ -128,7 +128,7 @@ def get_layer_dict(layer_obj: ImageLayer) -> Tuple[SpdxPackage, List[Relationshi file_name=layer_obj.tar_file, download_location=SpdxNone(), files_analyzed=bool(layer_obj.files_analyzed), - verification_code=verification_code if bool(layer_obj.files_analyzed) else None, + verification_code=verification_code, checksums=[get_layer_checksum(layer_obj)], license_concluded=SpdxNoAssertion(), license_declared=SpdxNoAssertion(), diff --git a/tern/formats/spdx_new/make_spdx_model.py b/tern/formats/spdx_new/make_spdx_model.py index 188abd10..9d839f78 100644 --- a/tern/formats/spdx_new/make_spdx_model.py +++ b/tern/formats/spdx_new/make_spdx_model.py @@ -17,7 +17,7 @@ from tern.formats.spdx_new.constants import DOCUMENT_ID, DOCUMENT_NAME, SPDX_VERSION, DATA_LICENSE, DOCUMENT_COMMENT, \ LICENSE_LIST_VERSION, CREATOR_NAME, DOCUMENT_NAME_SNAPSHOT, DOCUMENT_NAMESPACE_SNAPSHOT from tern.formats.spdx_new.file_helpers import get_layer_files_list -from tern.formats.spdx_new.general_helpers import get_current_timestamp, get_uuid, get_image_spdxref +from tern.formats.spdx_new.general_helpers import get_current_timestamp, get_uuid from tern.classes.image import Image from tern.formats.spdx.spdx import SPDX from tern.formats.spdx_new.file_helpers import get_files_list @@ -51,7 +51,6 @@ def make_spdx_model(image_obj_list: List[Image]) -> Document: data_license=DATA_LICENSE, document_comment=DOCUMENT_COMMENT, ) - describes_relationship = Relationship(DOCUMENT_ID, RelationshipType.DESCRIBES, get_image_spdxref(image_obj)) packages = [get_image_dict(image_obj, template)] image_layer_relationships = get_image_layer_relationships(image_obj) @@ -69,7 +68,7 @@ def make_spdx_model(image_obj_list: List[Image]) -> Document: creation_info=creation_info, packages=packages, files=files, - relationships=[describes_relationship] + image_layer_relationships + layer_file_relationships, + relationships=image_layer_relationships + layer_file_relationships, extracted_licensing_info=extracted_licensing_info ) diff --git a/tests/golang_test_image.pkl b/tests/golang_test_image.pkl new file mode 100644 index 00000000..5c33bf2d Binary files /dev/null and b/tests/golang_test_image.pkl differ diff --git a/tests/large_tern_scan_image.pkl b/tests/large_tern_scan_image.pkl new file mode 100644 index 00000000..7465d135 Binary files /dev/null and b/tests/large_tern_scan_image.pkl differ diff --git a/tests/test_spdx_generation.py b/tests/test_spdx_generation.py new file mode 100644 index 00000000..5153192b --- /dev/null +++ b/tests/test_spdx_generation.py @@ -0,0 +1,86 @@ +import json +import os +import pickle +import unittest + +from tern.classes.docker_image import DockerImage +from tern.formats.spdx.spdxjson.generator import SpdxJSON + + +class TestSPDXGeneration(unittest.TestCase): + test_package = { + "name": "alpine-keys", + "SPDXID": "SPDXRef-alpine-keys-2.1-r2", + "versionInfo": "2.1-r2", + "supplier": "Organization: Alpine Linux", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": False, + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "MIT", + "copyrightText": "NONE", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:apk/alpine/alpine-keys@2.1-r2?arch=x86_64", + "referenceType": "purl" + } + ], + "comment": "alpine-keys:\n\twarning: No metadata for key: copyright\n\twarning: No metadata for key: download_url\n\twarning: No metadata for key: checksum\n\twarning: No metadata for key: pkg_licenses\n\twarning: No metadata for key: pkg_format\n\twarning: No metadata for key: src_name\n\twarning: No metadata for key: src_version\n" + } + + test_describes_relationship = { + "spdxElementId": "SPDXRef-DOCUMENT", + "relatedSpdxElement": "SPDXRef-golang-1.12-alpine", + "relationshipType": "DESCRIBES" + } + + test_contains_relationship = { + "spdxElementId": "SPDXRef-5216338b40", + "relatedSpdxElement": "SPDXRef-alpine-keys-2.1-r2", + "relationshipType": "CONTAINS" + } + + test_has_prerequisite_relationship = { + "spdxElementId": "SPDXRef-3957f7032f", + "relatedSpdxElement": "SPDXRef-7306dca01e", + "relationshipType": "HAS_PREREQUISITE" + } + + test_extracted_licensing_info = { + "extractedText": "MPL-2.0 GPL-2.0-or-later", + "licenseId": "LicenseRef-f30c02b" + } + + def test_spdx_generation_from_pickled_image(self): + json_file_path = "spdx_test.json" + test_image_file_path = "large_tern_scan_image.pkl" # generated during "tern report -i golang:1.12-alpine" + with open(test_image_file_path, "rb") as f: + image = pickle.load(f) + image_list = [image] + + json_as_string = SpdxJSON().generate(image_list) + with open(json_file_path, "w") as f: + f.write(json_as_string) + + with open(json_file_path, "r") as f: + json_dict = json.load(f) + assert json_dict["SPDXID"] == "SPDXRef-DOCUMENT" + assert json_dict["spdxVersion"] == "SPDX-2.2" + assert len(json_dict["packages"]) == 21 + assert self.test_package in json_dict["packages"] + assert len(json_dict["relationships"]) == 25 + assert self.test_describes_relationship in json_dict["relationships"] + assert self.test_contains_relationship in json_dict["relationships"] + assert self.test_has_prerequisite_relationship in json_dict["relationships"] + assert len(json_dict["hasExtractedLicensingInfos"]) == 4 + assert self.test_extracted_licensing_info in json_dict["hasExtractedLicensingInfos"] + + os.remove(json_file_path) + + + def test_spdx_generation_from_docker_image(self): + docker_image = DockerImage('vmware/tern@sha256:20b32a9a20752aa1ad7582c6' + '67704fda9f004cc4bfd8601fac7f2656c7567bb4') + + json_as_string = SpdxJSON().generate([docker_image]) + i = 0 \ No newline at end of file