fosslight-util 2.1.2__py3-none-any.whl → 2.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,11 +8,12 @@ from fosslight_util.write_excel import write_result_to_excel, write_result_to_cs
8
8
  from fosslight_util.write_opossum import write_opossum
9
9
  from fosslight_util.write_yaml import write_yaml
10
10
  from fosslight_util.write_spdx import write_spdx
11
+ from fosslight_util.write_cyclonedx import write_cyclonedx
11
12
  from typing import Tuple
12
13
 
13
14
  SUPPORT_FORMAT = {'excel': '.xlsx', 'csv': '.csv', 'opossum': '.json', 'yaml': '.yaml',
14
15
  'spdx-yaml': '.yaml', 'spdx-json': '.json', 'spdx-xml': '.xml',
15
- 'spdx-tag': '.tag'}
16
+ 'spdx-tag': '.tag', 'cyclonedx-json': '.json', 'cyclonedx-xml': '.xml'}
16
17
 
17
18
 
18
19
  def check_output_format(output='', format='', customized_format={}):
@@ -188,6 +189,8 @@ def write_output_file(output_file_without_ext: str, file_extension: str, scan_it
188
189
  msg = f'{platform.system()} not support spdx format.'
189
190
  else:
190
191
  success, msg, _ = write_spdx(output_file_without_ext, file_extension, scan_item, spdx_version)
192
+ elif format.startswith('cyclonedx'):
193
+ success, msg, _ = write_cyclonedx(output_file_without_ext, file_extension, scan_item)
191
194
  else:
192
195
  if file_extension == '.xlsx':
193
196
  success, msg = write_result_to_excel(result_file, scan_item, extended_header, hide_header)
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ # Copyright (c) 2024 LG Electronics Inc.
4
+ # Copyright (c) OWASP Foundation.
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ import os
8
+ import sys
9
+ import logging
10
+ import re
11
+ import json
12
+ from pathlib import Path
13
+ from datetime import datetime
14
+ from fosslight_util.spdx_licenses import get_spdx_licenses_json, get_license_from_nick
15
+ from fosslight_util.constant import (LOGGER_NAME, FOSSLIGHT_DEPENDENCY, FOSSLIGHT_SCANNER,
16
+ FOSSLIGHT_BINARY, FOSSLIGHT_SOURCE)
17
+ from fosslight_util.oss_item import CHECKSUM_NULL, get_checksum_sha1
18
+ from packageurl import PackageURL
19
+ import traceback
20
+ from cyclonedx.builder.this import this_component as cdx_lib_component
21
+ from cyclonedx.exception import MissingOptionalDependencyException
22
+ from cyclonedx.factory.license import LicenseFactory
23
+ from cyclonedx.model import XsUri, ExternalReferenceType
24
+ from cyclonedx.model.bom import Bom
25
+ from cyclonedx.model.component import Component, ComponentType, HashAlgorithm, HashType, ExternalReference
26
+ from cyclonedx.model.contact import OrganizationalEntity
27
+ from cyclonedx.output import make_outputter, BaseOutput
28
+ from cyclonedx.output.json import JsonV1Dot6
29
+ from cyclonedx.schema import OutputFormat, SchemaVersion
30
+ from cyclonedx.validation import make_schemabased_validator
31
+ from cyclonedx.validation.json import JsonStrictValidator
32
+ from cyclonedx.output.json import Json as JsonOutputter
33
+ from cyclonedx.output.xml import Xml as XmlOutputter
34
+ from cyclonedx.validation.xml import XmlValidator
35
+
36
+ logger = logging.getLogger(LOGGER_NAME)
37
+
38
+
39
+ def write_cyclonedx(output_file_without_ext, output_extension, scan_item):
40
+ success = True
41
+ error_msg = ''
42
+
43
+ bom = Bom()
44
+ if scan_item:
45
+ try:
46
+ cover_name = scan_item.cover.get_print_json()["Tool information"].split('(').pop(0).strip()
47
+ match = re.search(r"(.+) v([0-9.]+)", cover_name)
48
+ if match:
49
+ scanner_name = match.group(1)
50
+ else:
51
+ scanner_name = FOSSLIGHT_SCANNER
52
+ except Exception:
53
+ cover_name = FOSSLIGHT_SCANNER
54
+ scanner_name = FOSSLIGHT_SCANNER
55
+
56
+ lc_factory = LicenseFactory()
57
+ bom.metadata.tools.components.add(cdx_lib_component())
58
+ bom.metadata.tools.components.add(Component(name=scanner_name.upper(),
59
+ type=ComponentType.APPLICATION))
60
+ comp_id = 0
61
+ bom.metadata.component = root_component = Component(name='Root Component',
62
+ type=ComponentType.APPLICATION,
63
+ bom_ref=str(comp_id))
64
+ relation_tree = {}
65
+ bom_ref_packages = []
66
+
67
+ output_dir = os.path.dirname(output_file_without_ext)
68
+ Path(output_dir).mkdir(parents=True, exist_ok=True)
69
+ try:
70
+ root_package = False
71
+ for scanner_name, file_items in scan_item.file_items.items():
72
+ for file_item in file_items:
73
+ if file_item.exclude:
74
+ continue
75
+ if scanner_name == FOSSLIGHT_SOURCE:
76
+ comp_type = ComponentType.FILE
77
+ else:
78
+ comp_type = ComponentType.LIBRARY
79
+
80
+ for oss_item in file_item.oss_items:
81
+ if oss_item.name == '':
82
+ if scanner_name == FOSSLIGHT_DEPENDENCY:
83
+ continue
84
+ else:
85
+ comp_name = file_item.source_name_or_path
86
+ else:
87
+ comp_name = oss_item.name
88
+
89
+ comp_id += 1
90
+ comp = Component(type=comp_type,
91
+ name=comp_name,
92
+ bom_ref=str(comp_id))
93
+
94
+ if oss_item.version != '':
95
+ comp.version = oss_item.version
96
+ if oss_item.copyright != '':
97
+ comp.copyright = oss_item.copyright
98
+ if scanner_name == FOSSLIGHT_DEPENDENCY and file_item.purl:
99
+ comp.purl = PackageURL.from_string(file_item.purl)
100
+ if scanner_name != FOSSLIGHT_DEPENDENCY:
101
+ comp.hashes = [HashType(alg=HashAlgorithm.SHA_1, content=file_item.checksum)]
102
+
103
+ if oss_item.download_location != '':
104
+ comp.external_references = [ExternalReference(url=XsUri(oss_item.download_location),
105
+ type=ExternalReferenceType.WEBSITE)]
106
+
107
+ oss_licenses = []
108
+ for ol in oss_item.license:
109
+ try:
110
+ oss_licenses.append(lc_factory.make_from_string(ol))
111
+ except Exception:
112
+ logger.info(f'No spdx license name: {oi}')
113
+ if oss_licenses:
114
+ comp.licenses = oss_licenses
115
+
116
+ root_package = False
117
+ if scanner_name == FOSSLIGHT_DEPENDENCY:
118
+ if oss_item.comment:
119
+ oss_comment = oss_item.comment.split('/')
120
+ for oc in oss_comment:
121
+ if oc in ['direct', 'transitive', 'root package']:
122
+ if oc == 'direct':
123
+ bom.register_dependency(root_component, [comp])
124
+ elif oc == 'root package':
125
+ root_package = True
126
+ root_component.name = comp_name
127
+ root_component.type = comp_type
128
+ comp_id -= 1
129
+ else:
130
+ bom.register_dependency(root_component, [comp])
131
+ if len(file_item.depends_on) > 0:
132
+ purl = file_item.purl
133
+ relation_tree[purl] = []
134
+ relation_tree[purl].extend(file_item.depends_on)
135
+
136
+ if not root_package:
137
+ bom.components.add(comp)
138
+
139
+ if len(bom.components) > 0:
140
+ for comp_purl in relation_tree:
141
+ comp = bom.get_component_by_purl(PackageURL.from_string(comp_purl))
142
+ if comp:
143
+ dep_comp_list = []
144
+ for dep_comp_purl in relation_tree[comp_purl]:
145
+ dep_comp = bom.get_component_by_purl(PackageURL.from_string(dep_comp_purl))
146
+ if dep_comp:
147
+ dep_comp_list.append(dep_comp)
148
+ bom.register_dependency(comp, dep_comp_list)
149
+
150
+ except Exception as e:
151
+ success = False
152
+ error_msg = f'Failed to create CycloneDX document object:{e}, {traceback.format_exc()}'
153
+ else:
154
+ success = False
155
+ error_msg = 'No item to write in output file.'
156
+
157
+ result_file = ''
158
+ if success:
159
+ result_file = output_file_without_ext + output_extension
160
+ try:
161
+ if output_extension == '.json':
162
+ write_cyclonedx_json(bom, result_file)
163
+ elif output_extension == '.xml':
164
+ write_cyclonedx_xml(bom, result_file)
165
+ else:
166
+ success = False
167
+ error_msg = f'Not supported output_extension({output_extension})'
168
+ except Exception as e:
169
+ success = False
170
+ error_msg = f'Failed to write CycloneDX document: {e}'
171
+ if os.path.exists(result_file):
172
+ os.remove(result_file)
173
+
174
+ return success, error_msg, result_file
175
+
176
+
177
+ def write_cyclonedx_json(bom, result_file):
178
+ success = True
179
+ try:
180
+ my_json_outputter: 'JsonOutputter' = JsonV1Dot6(bom)
181
+ my_json_outputter.output_to_file(result_file)
182
+ serialized_json = my_json_outputter.output_as_string(indent=2)
183
+ my_json_validator = JsonStrictValidator(SchemaVersion.V1_6)
184
+ try:
185
+ validation_errors = my_json_validator.validate_str(serialized_json)
186
+ if validation_errors:
187
+ logger.warning(f'JSON invalid, ValidationError: {repr(validation_errors)}')
188
+ except MissingOptionalDependencyException as error:
189
+ logger.debug(f'JSON-validation was skipped due to {error}')
190
+ except Exception as e:
191
+ success = False
192
+ return success
193
+
194
+
195
+
196
+ def write_cyclonedx_xml(bom, result_file):
197
+ success = True
198
+ try:
199
+ my_xml_outputter: BaseOutput = make_outputter(bom=bom,
200
+ output_format=OutputFormat.XML,
201
+ schema_version=SchemaVersion.V1_6)
202
+ my_xml_outputter.output_to_file(filename=result_file)
203
+ serialized_xml = my_xml_outputter.output_as_string(indent=2)
204
+ my_xml_validator = XmlValidator(SchemaVersion.V1_6)
205
+ try:
206
+ validation_errors = my_xml_validator.validate_str(serialized_xml)
207
+ if validation_errors:
208
+ logger.warning(f'XML invalid, ValidationError: {repr(validation_errors)}')
209
+ except MissingOptionalDependencyException as error:
210
+ logger.debug(f'XML-validation was skipped due to {error}')
211
+ except Exception as e:
212
+ success = False
213
+ return success
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fosslight-util
3
- Version: 2.1.2
3
+ Version: 2.1.3
4
4
  Summary: FOSSLight Util
5
5
  Home-page: https://github.com/fosslight/fosslight_util
6
6
  Author: LG Electronics
@@ -30,6 +30,7 @@ Requires-Dist: setuptools>=65.5.1
30
30
  Requires-Dist: npm
31
31
  Requires-Dist: requests
32
32
  Requires-Dist: GitPython
33
+ Requires-Dist: cyclonedx-python-lib==8.5.0
33
34
  Requires-Dist: numpy; python_version < "3.8"
34
35
  Requires-Dist: numpy>=1.22.2; python_version >= "3.8"
35
36
  Requires-Dist: pygit2==1.6.1; python_version < "3.7"
@@ -7,12 +7,13 @@ fosslight_util/cover.py,sha256=qqqKzxqFwKimal764FaugRUBcHWdeKt8af6xeK0mH8E,2040
7
7
  fosslight_util/download.py,sha256=NrAThwiulcWZhV9fBsc8_i7TWLExptmJ_JDnU7TnzuA,16340
8
8
  fosslight_util/help.py,sha256=M3_XahUkP794US9Q0NS6ujmGvrFFnKBHsTU95Fg1KpA,2181
9
9
  fosslight_util/oss_item.py,sha256=8W2HlwqGH3l1iPPdvycrRYKsBSBpqAkqYyYtBVPgMtY,6868
10
- fosslight_util/output_format.py,sha256=hELTfwo0PqoRtuSnQLbSrPwoX5bjddB50q-aHErs1YE,8324
10
+ fosslight_util/output_format.py,sha256=wm3i7icqol9v7fwL_-yTBJihAjlko-G80oX3j33sAug,8578
11
11
  fosslight_util/parsing_yaml.py,sha256=2zx_N5lMkXT1dRmfJMpzlrru-y_2F_CkVbGlba6vQpU,5380
12
12
  fosslight_util/read_excel.py,sha256=-QvrdxaNqYOpIm1H7ZqIEh5NLvFPymZo6BAOZcQmQug,5263
13
13
  fosslight_util/set_log.py,sha256=Xpa94AiOyGEK8ucaYkvkAllvlen1Pq_d6UG6kPYBYBc,3780
14
14
  fosslight_util/spdx_licenses.py,sha256=GvMNe_D4v2meapTVwPu2BJXInnTo3_gIzg669eJhUu0,3691
15
15
  fosslight_util/timer_thread.py,sha256=5VbZENQPD-N0NUmzEktqGr6Am-e7vxD79K05mmr29g0,433
16
+ fosslight_util/write_cyclonedx.py,sha256=wUtF5AEoIQIlK3zfPefINIlzuYXT7OQ4DZiPYinEAbs,9764
16
17
  fosslight_util/write_excel.py,sha256=G0fIslbWoOtWZCJxbBGLCpUKbhmwrrqhI5PHwRw8_44,9931
17
18
  fosslight_util/write_opossum.py,sha256=ltmo6SkugKWdAYupeCqwE4-3lua0GwLpix1XqFC-tT8,11678
18
19
  fosslight_util/write_scancodejson.py,sha256=81n7cWNYoyIKE_V4Kx5YtL2CgjMPIjoKdnSU3inkpJY,2163
@@ -22,9 +23,9 @@ fosslight_util/write_yaml.py,sha256=QlEKoIPQsEaYERfbP53TeKgnllYzhLQWm5wYjnWtVjE,
22
23
  fosslight_util/resources/frequentLicenselist.json,sha256=GUhzK6tu7ok10fekOnmVmUgIGRC-acGABZKTNKfDyYA,4776157
23
24
  fosslight_util/resources/frequent_license_nick_list.json,sha256=ryU2C_6ZxHbz90_sUN9OvI9GXkCMLu7oGcmd9W79YYo,5005
24
25
  fosslight_util/resources/licenses.json,sha256=mK55z-bhY7Mjpj2KsO1crKGGL-X3F6MBFQJ0zLlx010,240843
25
- fosslight_util-2.1.2.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
26
- fosslight_util-2.1.2.dist-info/METADATA,sha256=_ES9pW6X4xAdZEw7uykDuE1h4R_4n4IR7hO7xMxQfqQ,6431
27
- fosslight_util-2.1.2.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
28
- fosslight_util-2.1.2.dist-info/entry_points.txt,sha256=bzXX5i7HZ13V8BLKvtu_9KO3ZjtRypH-XszOXT6I3bU,69
29
- fosslight_util-2.1.2.dist-info/top_level.txt,sha256=2qyYWGLakgBRy4BqoBNt-I5C29tBr_e93e5e1pbuTGA,15
30
- fosslight_util-2.1.2.dist-info/RECORD,,
26
+ fosslight_util-2.1.3.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
27
+ fosslight_util-2.1.3.dist-info/METADATA,sha256=_Tg1pvfaGN1PVXXUTksMNDMs9nVJlC8Sat7F-SOxW8c,6474
28
+ fosslight_util-2.1.3.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
29
+ fosslight_util-2.1.3.dist-info/entry_points.txt,sha256=bzXX5i7HZ13V8BLKvtu_9KO3ZjtRypH-XszOXT6I3bU,69
30
+ fosslight_util-2.1.3.dist-info/top_level.txt,sha256=2qyYWGLakgBRy4BqoBNt-I5C29tBr_e93e5e1pbuTGA,15
31
+ fosslight_util-2.1.3.dist-info/RECORD,,