Skip to content

Commit a226cec

Browse files
add SPDX versioning
This adds new options to the -f parameter like "spdxjson22", "spdxjson23", "spdxrdf22" etc. Also extracts common code from the generators of all the different formats. Signed-off-by: Armin Tänzer <armin.taenzer@tngtech.com>
1 parent 0f1ff2c commit a226cec

File tree

29 files changed

+318
-283
lines changed

29 files changed

+318
-283
lines changed

setup.cfg

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,16 @@ tern.formats =
5252
yaml = tern.formats.yaml.generator:YAML
5353
html = tern.formats.html.generator:HTML
5454
cyclonedxjson = tern.formats.cyclonedx.cyclonedxjson.generator:CycloneDXJSON
55-
spdxjson_new = tern.formats.spdx_new.spdxjson.generator:SpdxJSON
56-
spdxyaml_new = tern.formats.spdx_new.spdxyaml.generator:SpdxYAML
57-
spdxxml_new = tern.formats.spdx_new.spdxxml.generator:SpdxXML
58-
spdxrdf_new = tern.formats.spdx_new.spdxrdf.generator:SpdxRDF
59-
spdxtagvalue_new = tern.formats.spdx_new.spdxtagvalue.generator:SpdxTagValue
55+
spdxjson22 = tern.formats.spdx_new.spdxjson22.generator:SpdxJSON22
56+
spdxyaml22 = tern.formats.spdx_new.spdxyaml22.generator:SpdxYAML22
57+
spdxxml22 = tern.formats.spdx_new.spdxxml22.generator:SpdxXML22
58+
spdxrdf22 = tern.formats.spdx_new.spdxrdf22.generator:SpdxRDF22
59+
spdxtagvalue22 = tern.formats.spdx_new.spdxtagvalue22.generator:SpdxTagValue22
60+
spdxjson23 = tern.formats.spdx_new.spdxjson23.generator:SpdxJSON23
61+
spdxyaml23 = tern.formats.spdx_new.spdxyaml23.generator:SpdxYAML23
62+
spdxxml23 = tern.formats.spdx_new.spdxxml23.generator:SpdxXML23
63+
spdxrdf23 = tern.formats.spdx_new.spdxrdf23.generator:SpdxRDF23
64+
spdxtagvalue23 = tern.formats.spdx_new.spdxtagvalue23.generator:SpdxTagValue23
6065
tern.extensions =
6166
cve_bin_tool = tern.extensions.cve_bin_tool.executor:CveBinTool
6267
scancode = tern.extensions.scancode.executor:Scancode

tern/formats/spdx_new/general_helpers.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@
77
General helpers for SPDX document generator
88
"""
99
import hashlib
10-
import io
1110
import re
1211
import uuid
1312
from datetime import datetime
14-
from typing import Union, Callable, IO, Tuple
13+
from typing import Union, Tuple
1514

1615
from license_expression import get_spdx_licensing, LicenseExpression, Licensing
17-
from spdx_tools.spdx.model import SpdxNone, Document
16+
from spdx_tools.spdx.model import SpdxNone
1817

1918
from tern.classes.file_data import FileData
2019
from tern.classes.image import Image
@@ -77,12 +76,6 @@ def get_package_license_declared(package_license_declared: str) -> Union[License
7776
return SpdxNone()
7877

7978

80-
def get_serialized_document_string(spdx_document: Document, writer_function: Callable[[Document, IO[str]], str]) -> str:
81-
with io.StringIO() as stream:
82-
writer_function(spdx_document, stream, validate=False)
83-
return stream.getvalue()
84-
85-
8679
###########################################################################################
8780
# central place for SPDXRef-generators to avoid circular imports as these are widely used #
8881
###########################################################################################

tern/formats/spdx_new/make_spdx_model.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,16 @@
44
# SPDX-License-Identifier: BSD-2-Clause
55

66
"""
7-
Common functions that are useful for all SPDX serialization formats
7+
Functions to create an SPDX model instance from a list of Images or an ImageLayer
88
"""
99

10-
import logging
1110
from typing import List
1211

1312
from spdx_tools.spdx.model import Document, CreationInfo, Actor, ActorType, Relationship, RelationshipType
1413

1514
from tern.classes.image_layer import ImageLayer
1615
from tern.classes.template import Template
17-
from tern.formats.spdx_new.constants import DOCUMENT_ID, DOCUMENT_NAME, SPDX_VERSION, DATA_LICENSE, DOCUMENT_COMMENT, \
16+
from tern.formats.spdx_new.constants import DOCUMENT_ID, DOCUMENT_NAME, DATA_LICENSE, DOCUMENT_COMMENT, \
1817
LICENSE_LIST_VERSION, CREATOR_NAME, DOCUMENT_NAME_SNAPSHOT, DOCUMENT_NAMESPACE_SNAPSHOT
1918
from tern.formats.spdx_new.file_helpers import get_layer_files_list
2019
from tern.formats.spdx_new.general_helpers import get_current_timestamp, get_uuid
@@ -25,23 +24,19 @@
2524
get_image_dict, get_document_namespace
2625
from tern.formats.spdx_new.layer_helpers import get_layer_dict, get_image_layer_relationships, get_layer_extracted_licenses
2726
from tern.formats.spdx_new.package_helpers import get_packages_list, get_layer_packages_list
28-
from tern.utils import constants
2927

3028
from tern.utils.general import get_git_rev_or_version
3129

32-
# global logger
33-
logger = logging.getLogger(constants.logger_name)
3430

35-
36-
def make_spdx_model(image_obj_list: List[Image]) -> Document:
31+
def make_spdx_model(image_obj_list: List[Image], spdx_version: str) -> Document:
3732
template = SPDX()
3833
# we still don't know how SPDX documents could represent multiple
3934
# images. Hence, we will assume only one image is analyzed and the
4035
# input is a list of length 1
4136
image_obj = image_obj_list[0]
4237

4338
creation_info = CreationInfo(
44-
spdx_version=SPDX_VERSION,
39+
spdx_version=spdx_version,
4540
spdx_id=DOCUMENT_ID,
4641
name=DOCUMENT_NAME.format(image_name=image_obj.name),
4742
document_namespace=get_document_namespace(image_obj),
@@ -73,13 +68,13 @@ def make_spdx_model(image_obj_list: List[Image]) -> Document:
7368
)
7469

7570

76-
def make_spdx_model_snapshot(layer_obj: ImageLayer, template: Template) -> Document:
71+
def make_spdx_model_snapshot(layer_obj: ImageLayer, template: Template, spdx_version: str) -> Document:
7772
"""This is the SPDX document containing just the packages found at
7873
container build time"""
7974
timestamp = get_current_timestamp()
8075

8176
creation_info = CreationInfo(
82-
spdx_version=SPDX_VERSION,
77+
spdx_version=spdx_version,
8378
spdx_id=DOCUMENT_ID,
8479
name=DOCUMENT_NAME_SNAPSHOT,
8580
document_namespace=DOCUMENT_NAMESPACE_SNAPSHOT.format(timestamp=timestamp, uuid=get_uuid()),
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# Copyright (c) 2021 VMware, Inc. All Rights Reserved.
4+
# SPDX-License-Identifier: BSD-2-Clause
5+
6+
"""
7+
Handle imports and logging for different SPDX formats
8+
"""
9+
import io
10+
import logging
11+
from typing import Callable, IO, List
12+
13+
from spdx_tools.spdx.model import Document
14+
15+
from tern.classes.image import Image
16+
from tern.classes.image_layer import ImageLayer
17+
from tern.formats.spdx.spdx import SPDX
18+
from tern.formats.spdx_new.make_spdx_model import make_spdx_model, make_spdx_model_snapshot
19+
from tern.utils import constants
20+
21+
logger = logging.getLogger(constants.logger_name)
22+
23+
24+
def get_spdx_from_image_list(image_obj_list: List[Image], spdx_format: str, spdx_version: str) -> str:
25+
"""Generate an SPDX document
26+
WARNING: This assumes that the list consists of one image or the base
27+
image and a stub image, in which case, the information in the stub
28+
image is not applicable in the SPDX case as it is an empty image
29+
object with no metadata as nothing got built.
30+
31+
For the sake of SPDX, an image is a 'Package' which 'CONTAINS' each
32+
layer which is also a 'Package' which 'CONTAINS' the real Packages"""
33+
logger.debug(f"Generating SPDX {spdx_format} document...")
34+
35+
spdx_document: Document = make_spdx_model(image_obj_list, spdx_version)
36+
37+
return convert_document_to_serialized_string(spdx_document, spdx_format)
38+
39+
40+
def get_spdx_from_layer(layer: ImageLayer, spdx_format: str, spdx_version: str) -> str:
41+
"""Generate an SPDX document containing package and file information
42+
at container build time"""
43+
logger.debug(f"Generating SPDX {spdx_format} snapshot document...")
44+
45+
template = SPDX()
46+
spdx_document: Document = make_spdx_model_snapshot(layer, template, spdx_version)
47+
48+
return convert_document_to_serialized_string(spdx_document, spdx_format)
49+
50+
51+
def convert_document_to_serialized_string(spdx_document: Document, spdx_format: str) -> str:
52+
if spdx_format == "JSON":
53+
from spdx_tools.spdx.writer.json.json_writer import write_document_to_stream
54+
return get_serialized_document_string(spdx_document, write_document_to_stream)
55+
if spdx_format == "YAML":
56+
from spdx_tools.spdx.writer.yaml.yaml_writer import write_document_to_stream
57+
return get_serialized_document_string(spdx_document, write_document_to_stream)
58+
if spdx_format == "XML":
59+
from spdx_tools.spdx.writer.xml.xml_writer import write_document_to_stream
60+
return get_serialized_document_string(spdx_document, write_document_to_stream)
61+
if spdx_format == "RDF-XML":
62+
return get_serialized_rdf_document_string(spdx_document)
63+
if spdx_format == "Tag-Value":
64+
from spdx_tools.spdx.writer.tagvalue.tagvalue_writer import write_document_to_stream
65+
return get_serialized_document_string(spdx_document, write_document_to_stream)
66+
67+
68+
def get_serialized_document_string(spdx_document: Document, writer_function: Callable[[Document, IO[str]], str]) -> str:
69+
with io.StringIO() as stream:
70+
writer_function(spdx_document, stream, validate=False)
71+
return stream.getvalue()
72+
73+
74+
def get_serialized_rdf_document_string(spdx_document: Document) -> str:
75+
from spdx_tools.spdx.writer.rdf.rdf_writer import write_document_to_stream
76+
with io.BytesIO() as stream:
77+
write_document_to_stream(spdx_document, stream, validate=False)
78+
return stream.getvalue().decode("UTF-8")

tern/formats/spdx_new/spdxjson/generator.py

Lines changed: 0 additions & 50 deletions
This file was deleted.
File renamed without changes.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# Copyright (c) 2021 VMware, Inc. All Rights Reserved.
4+
# SPDX-License-Identifier: BSD-2-Clause
5+
6+
"""
7+
SPDX-2.2 JSON document generator
8+
"""
9+
from typing import List
10+
11+
from tern.classes.image import Image
12+
from tern.classes.image_layer import ImageLayer
13+
from tern.formats import generator
14+
from tern.formats.spdx_new.spdx_formats_helper import get_spdx_from_image_list, get_spdx_from_layer
15+
16+
17+
class SpdxJSON22(generator.Generate):
18+
def generate(self, image_obj_list: List[Image], print_inclusive=False) -> str:
19+
return get_spdx_from_image_list(image_obj_list, "JSON", "SPDX-2.2")
20+
21+
def generate_layer(self, layer: ImageLayer) -> str:
22+
return get_spdx_from_layer(layer, "JSON", "SPDX-2.2")
File renamed without changes.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# Copyright (c) 2021 VMware, Inc. All Rights Reserved.
4+
# SPDX-License-Identifier: BSD-2-Clause
5+
6+
"""
7+
SPDX-2.3 JSON document generator
8+
"""
9+
from typing import List
10+
11+
from tern.classes.image import Image
12+
from tern.classes.image_layer import ImageLayer
13+
from tern.formats import generator
14+
from tern.formats.spdx_new.spdx_formats_helper import get_spdx_from_image_list, get_spdx_from_layer
15+
16+
17+
class SpdxJSON22(generator.Generate):
18+
def generate(self, image_obj_list: List[Image], print_inclusive=False) -> str:
19+
return get_spdx_from_image_list(image_obj_list, "JSON", "SPDX-2.3")
20+
21+
def generate_layer(self, layer: ImageLayer) -> str:
22+
return get_spdx_from_layer(layer, "JSON", "SPDX-2.3")

tern/formats/spdx_new/spdxrdf/generator.py

Lines changed: 0 additions & 57 deletions
This file was deleted.

0 commit comments

Comments
 (0)