fosslight-util 2.0.0__py3-none-any.whl → 2.1.29__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.
- fosslight_util/_get_downloadable_url.py +460 -68
- fosslight_util/compare_yaml.py +3 -1
- fosslight_util/constant.py +5 -1
- fosslight_util/correct.py +4 -6
- fosslight_util/download.py +286 -80
- fosslight_util/exclude.py +65 -0
- fosslight_util/help.py +14 -3
- fosslight_util/oss_item.py +24 -3
- fosslight_util/output_format.py +100 -18
- fosslight_util/set_log.py +8 -2
- fosslight_util/write_cyclonedx.py +210 -0
- fosslight_util/write_excel.py +5 -1
- fosslight_util/write_scancodejson.py +31 -14
- fosslight_util/write_spdx.py +161 -109
- {fosslight_util-2.0.0.dist-info → fosslight_util-2.1.29.dist-info}/METADATA +24 -22
- fosslight_util-2.1.29.dist-info/RECORD +32 -0
- {fosslight_util-2.0.0.dist-info → fosslight_util-2.1.29.dist-info}/WHEEL +1 -1
- {fosslight_util-2.0.0.dist-info → fosslight_util-2.1.29.dist-info}/entry_points.txt +0 -1
- fosslight_util/convert_excel_to_yaml.py +0 -69
- fosslight_util-2.0.0.dist-info/RECORD +0 -31
- {fosslight_util-2.0.0.dist-info → fosslight_util-2.1.29.dist-info/licenses}/LICENSE +0 -0
- {fosslight_util-2.0.0.dist-info → fosslight_util-2.1.29.dist-info}/top_level.txt +0 -0
fosslight_util/oss_item.py
CHANGED
|
@@ -5,11 +5,13 @@
|
|
|
5
5
|
|
|
6
6
|
import logging
|
|
7
7
|
import os
|
|
8
|
+
import hashlib
|
|
8
9
|
from fosslight_util.constant import LOGGER_NAME, FOSSLIGHT_SCANNER
|
|
9
10
|
from fosslight_util.cover import CoverItem
|
|
10
11
|
from typing import List, Dict
|
|
11
12
|
|
|
12
13
|
_logger = logging.getLogger(LOGGER_NAME)
|
|
14
|
+
CHECKSUM_NULL = "0"
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
class OssItem:
|
|
@@ -58,10 +60,12 @@ class OssItem:
|
|
|
58
60
|
|
|
59
61
|
@copyright.setter
|
|
60
62
|
def copyright(self, value):
|
|
61
|
-
if value
|
|
63
|
+
if value:
|
|
62
64
|
if isinstance(value, list):
|
|
63
|
-
value =
|
|
64
|
-
|
|
65
|
+
value = list(set(value))
|
|
66
|
+
else:
|
|
67
|
+
value = set(value.split("\n"))
|
|
68
|
+
value = "\n".join(value).strip()
|
|
65
69
|
self._copyright = value
|
|
66
70
|
|
|
67
71
|
@property
|
|
@@ -98,6 +102,7 @@ class FileItem:
|
|
|
98
102
|
self._comment = ""
|
|
99
103
|
self.is_binary = False
|
|
100
104
|
self.oss_items: List[OssItem] = []
|
|
105
|
+
self.checksum = CHECKSUM_NULL
|
|
101
106
|
|
|
102
107
|
def __del__(self):
|
|
103
108
|
pass
|
|
@@ -169,6 +174,22 @@ class FileItem:
|
|
|
169
174
|
return items
|
|
170
175
|
|
|
171
176
|
|
|
177
|
+
def get_checksum_sha1(source_name_or_path) -> str:
|
|
178
|
+
checksum = CHECKSUM_NULL
|
|
179
|
+
try:
|
|
180
|
+
checksum = str(hashlib.sha1(source_name_or_path.encode()).hexdigest())
|
|
181
|
+
except Exception:
|
|
182
|
+
try:
|
|
183
|
+
f = open(source_name_or_path, "rb")
|
|
184
|
+
byte = f.read()
|
|
185
|
+
checksum = str(hashlib.sha1(byte).hexdigest())
|
|
186
|
+
f.close()
|
|
187
|
+
except Exception as ex:
|
|
188
|
+
_logger.info(f"(Error) Get_checksum: {ex}")
|
|
189
|
+
|
|
190
|
+
return checksum
|
|
191
|
+
|
|
192
|
+
|
|
172
193
|
def invalid(cmd):
|
|
173
194
|
_logger.info('[{}] is invalid'.format(cmd))
|
|
174
195
|
|
fosslight_util/output_format.py
CHANGED
|
@@ -3,12 +3,17 @@
|
|
|
3
3
|
# Copyright (c) 2021 LG Electronics Inc.
|
|
4
4
|
# SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
import os
|
|
6
|
+
import platform
|
|
6
7
|
from fosslight_util.write_excel import write_result_to_excel, write_result_to_csv
|
|
7
8
|
from fosslight_util.write_opossum import write_opossum
|
|
8
9
|
from fosslight_util.write_yaml import write_yaml
|
|
10
|
+
from fosslight_util.write_spdx import write_spdx
|
|
11
|
+
from fosslight_util.write_cyclonedx import write_cyclonedx
|
|
9
12
|
from typing import Tuple
|
|
10
13
|
|
|
11
|
-
SUPPORT_FORMAT = {'excel': '.xlsx', 'csv': '.csv', 'opossum': '.json', 'yaml': '.yaml'
|
|
14
|
+
SUPPORT_FORMAT = {'excel': '.xlsx', 'csv': '.csv', 'opossum': '.json', 'yaml': '.yaml',
|
|
15
|
+
'spdx-yaml': '.yaml', 'spdx-json': '.json', 'spdx-xml': '.xml',
|
|
16
|
+
'spdx-tag': '.tag', 'cyclonedx-json': '.json', 'cyclonedx-xml': '.xml'}
|
|
12
17
|
|
|
13
18
|
|
|
14
19
|
def check_output_format(output='', format='', customized_format={}):
|
|
@@ -27,7 +32,7 @@ def check_output_format(output='', format='', customized_format={}):
|
|
|
27
32
|
format = format.lower()
|
|
28
33
|
if format not in list(support_format.keys()):
|
|
29
34
|
success = False
|
|
30
|
-
msg = 'Enter the supported format
|
|
35
|
+
msg = '(-f option) Enter the supported format: ' + ', '.join(list(support_format.keys()))
|
|
31
36
|
else:
|
|
32
37
|
output_extension = support_format[format]
|
|
33
38
|
|
|
@@ -43,11 +48,12 @@ def check_output_format(output='', format='', customized_format={}):
|
|
|
43
48
|
if format:
|
|
44
49
|
if output_extension != basename_extension:
|
|
45
50
|
success = False
|
|
46
|
-
msg = f"Enter the same extension of output file(-o:'{output}')
|
|
51
|
+
msg = f"(-o & -f option) Enter the same extension of output file(-o:'{output}') \
|
|
52
|
+
with format(-f:'{format}')."
|
|
47
53
|
else:
|
|
48
54
|
if basename_extension not in support_format.values():
|
|
49
55
|
success = False
|
|
50
|
-
msg = 'Enter the supported file extension: ' + ', '.join(list(support_format.values()))
|
|
56
|
+
msg = '(-o option) Enter the supported file extension: ' + ', '.join(list(support_format.values()))
|
|
51
57
|
if success:
|
|
52
58
|
output_file = basename_file
|
|
53
59
|
output_extension = basename_extension
|
|
@@ -75,7 +81,7 @@ def check_output_formats(output='', formats=[], customized_format={}):
|
|
|
75
81
|
for format in formats:
|
|
76
82
|
if format not in list(support_format.keys()):
|
|
77
83
|
success = False
|
|
78
|
-
msg = 'Enter the supported format
|
|
84
|
+
msg = '(-f option) Enter the supported format: ' + ', '.join(list(support_format.keys()))
|
|
79
85
|
else:
|
|
80
86
|
output_extensions.append(support_format[format])
|
|
81
87
|
|
|
@@ -91,11 +97,12 @@ def check_output_formats(output='', formats=[], customized_format={}):
|
|
|
91
97
|
if formats:
|
|
92
98
|
if basename_extension not in output_extensions:
|
|
93
99
|
success = False
|
|
94
|
-
msg = f"The format of output file(-o:'{output}')
|
|
100
|
+
msg = f"(-o & -f option) The format of output file(-o:'{output}') \
|
|
101
|
+
should be in the format list(-f:'{formats}')."
|
|
95
102
|
else:
|
|
96
103
|
if basename_extension not in support_format.values():
|
|
97
104
|
success = False
|
|
98
|
-
msg = 'Enter the supported file extension: ' + ', '.join(list(support_format.values()))
|
|
105
|
+
msg = '(-o option) Enter the supported file extension: ' + ', '.join(list(support_format.values()))
|
|
99
106
|
output_extensions.append(basename_extension)
|
|
100
107
|
output_files = [basename_file for _ in range(len(output_extensions))]
|
|
101
108
|
else:
|
|
@@ -106,8 +113,64 @@ def check_output_formats(output='', formats=[], customized_format={}):
|
|
|
106
113
|
return success, msg, output_path, output_files, output_extensions
|
|
107
114
|
|
|
108
115
|
|
|
116
|
+
def check_output_formats_v2(output='', formats=[], customized_format={}):
|
|
117
|
+
success = True
|
|
118
|
+
msg = ''
|
|
119
|
+
output_path = ''
|
|
120
|
+
output_files = []
|
|
121
|
+
output_extensions = []
|
|
122
|
+
|
|
123
|
+
if customized_format:
|
|
124
|
+
support_format = customized_format
|
|
125
|
+
else:
|
|
126
|
+
support_format = SUPPORT_FORMAT
|
|
127
|
+
|
|
128
|
+
if formats:
|
|
129
|
+
# If -f option exist
|
|
130
|
+
formats = [format.lower() for format in formats]
|
|
131
|
+
for format in formats:
|
|
132
|
+
if format not in list(support_format.keys()):
|
|
133
|
+
success = False
|
|
134
|
+
msg = '(-f option) Enter the supported format with -f option: ' + ', '.join(list(support_format.keys()))
|
|
135
|
+
else:
|
|
136
|
+
output_extensions.append(support_format[format])
|
|
137
|
+
|
|
138
|
+
if success:
|
|
139
|
+
if output != '':
|
|
140
|
+
basename_extension = ''
|
|
141
|
+
if not os.path.isdir(output):
|
|
142
|
+
output_path = os.path.dirname(output)
|
|
143
|
+
|
|
144
|
+
basename = os.path.basename(output)
|
|
145
|
+
basename_file, basename_extension = os.path.splitext(basename)
|
|
146
|
+
if basename_extension:
|
|
147
|
+
if formats:
|
|
148
|
+
if basename_extension not in output_extensions:
|
|
149
|
+
success = False
|
|
150
|
+
msg = f"(-o & -f option) The format of output file(-o:'{output}') \
|
|
151
|
+
should be in the format list(-f:'{formats}')."
|
|
152
|
+
else:
|
|
153
|
+
if basename_extension not in support_format.values():
|
|
154
|
+
success = False
|
|
155
|
+
msg = '(-o option) Enter the supported file extension: ' + ', '.join(list(support_format.values()))
|
|
156
|
+
output_extensions.append(basename_extension)
|
|
157
|
+
output_files = [basename_file for _ in range(len(output_extensions))]
|
|
158
|
+
else:
|
|
159
|
+
output_path = output
|
|
160
|
+
if not output_extensions:
|
|
161
|
+
output_extensions = ['.xlsx']
|
|
162
|
+
if not formats:
|
|
163
|
+
formats = []
|
|
164
|
+
for ext in output_extensions:
|
|
165
|
+
for key, value in support_format.items():
|
|
166
|
+
if value == ext:
|
|
167
|
+
formats.append(key)
|
|
168
|
+
break
|
|
169
|
+
return success, msg, output_path, output_files, output_extensions, formats
|
|
170
|
+
|
|
171
|
+
|
|
109
172
|
def write_output_file(output_file_without_ext: str, file_extension: str, scan_item, extended_header: dict = {},
|
|
110
|
-
hide_header: dict = {}) -> Tuple[bool, str, str]:
|
|
173
|
+
hide_header: dict = {}, format: str = '', spdx_version: str = '2.3') -> Tuple[bool, str, str]:
|
|
111
174
|
success = True
|
|
112
175
|
msg = ''
|
|
113
176
|
|
|
@@ -115,16 +178,35 @@ def write_output_file(output_file_without_ext: str, file_extension: str, scan_it
|
|
|
115
178
|
file_extension = '.xlsx'
|
|
116
179
|
result_file = output_file_without_ext + file_extension
|
|
117
180
|
|
|
118
|
-
if
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
181
|
+
if format:
|
|
182
|
+
if format == 'excel':
|
|
183
|
+
success, msg = write_result_to_excel(result_file, scan_item, extended_header, hide_header)
|
|
184
|
+
elif format == 'csv':
|
|
185
|
+
success, msg, _ = write_result_to_csv(result_file, scan_item, False, extended_header)
|
|
186
|
+
elif format == 'opossum':
|
|
187
|
+
success, msg = write_opossum(result_file, scan_item)
|
|
188
|
+
elif format == 'yaml':
|
|
189
|
+
success, msg, _ = write_yaml(result_file, scan_item, False)
|
|
190
|
+
elif format.startswith('spdx') or format.startswith('cyclonedx'):
|
|
191
|
+
if platform.system() == 'Windows' or platform.system() == 'Darwin':
|
|
192
|
+
success = False
|
|
193
|
+
msg = f'{platform.system()} not support spdx format.'
|
|
194
|
+
else:
|
|
195
|
+
if format.startswith('spdx'):
|
|
196
|
+
success, msg, _ = write_spdx(output_file_without_ext, file_extension, scan_item, spdx_version)
|
|
197
|
+
elif format.startswith('cyclonedx'):
|
|
198
|
+
success, msg, _ = write_cyclonedx(output_file_without_ext, file_extension, scan_item)
|
|
126
199
|
else:
|
|
127
|
-
|
|
128
|
-
|
|
200
|
+
if file_extension == '.xlsx':
|
|
201
|
+
success, msg = write_result_to_excel(result_file, scan_item, extended_header, hide_header)
|
|
202
|
+
elif file_extension == '.csv':
|
|
203
|
+
success, msg, result_file = write_result_to_csv(result_file, scan_item, False, extended_header)
|
|
204
|
+
elif file_extension == '.json':
|
|
205
|
+
success, msg = write_opossum(result_file, scan_item)
|
|
206
|
+
elif file_extension == '.yaml':
|
|
207
|
+
success, msg, result_file = write_yaml(result_file, scan_item, False)
|
|
208
|
+
else:
|
|
209
|
+
success = False
|
|
210
|
+
msg = f'(-f option) Not supported file extension({file_extension})'
|
|
129
211
|
|
|
130
212
|
return success, msg, result_file
|
fosslight_util/set_log.py
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
import logging
|
|
7
7
|
import os
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
import pkg_resources
|
|
10
9
|
import sys
|
|
11
10
|
import platform
|
|
12
11
|
from . import constant as constant
|
|
@@ -15,6 +14,11 @@ import coloredlogs
|
|
|
15
14
|
from typing import Tuple
|
|
16
15
|
from logging import Logger
|
|
17
16
|
|
|
17
|
+
try:
|
|
18
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
19
|
+
except ImportError:
|
|
20
|
+
from importlib_metadata import version, PackageNotFoundError # Python <3.8
|
|
21
|
+
|
|
18
22
|
|
|
19
23
|
def init_check_latest_version(pkg_version="", main_package_name=""):
|
|
20
24
|
|
|
@@ -92,9 +96,11 @@ def init_log(log_file: str, create_file: bool = True, stream_log_level: int = lo
|
|
|
92
96
|
if main_package_name != "":
|
|
93
97
|
pkg_info = main_package_name
|
|
94
98
|
try:
|
|
95
|
-
pkg_version =
|
|
99
|
+
pkg_version = version(main_package_name)
|
|
96
100
|
init_check_latest_version(pkg_version, main_package_name)
|
|
97
101
|
pkg_info = main_package_name + " v" + pkg_version
|
|
102
|
+
except PackageNotFoundError:
|
|
103
|
+
logger.debug('Cannot check the version: Package not found')
|
|
98
104
|
except Exception as error:
|
|
99
105
|
logger.debug('Cannot check the version:' + str(error))
|
|
100
106
|
_result_log["Tool Info"] = pkg_info
|
|
@@ -0,0 +1,210 @@
|
|
|
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 logging
|
|
9
|
+
import re
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from fosslight_util.constant import (LOGGER_NAME, FOSSLIGHT_DEPENDENCY, FOSSLIGHT_SCANNER,
|
|
12
|
+
FOSSLIGHT_SOURCE)
|
|
13
|
+
import traceback
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(LOGGER_NAME)
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
from packageurl import PackageURL
|
|
19
|
+
from cyclonedx.builder.this import this_component as cdx_lib_component
|
|
20
|
+
from cyclonedx.exception import MissingOptionalDependencyException
|
|
21
|
+
from cyclonedx.factory.license import LicenseFactory
|
|
22
|
+
from cyclonedx.model import XsUri, ExternalReferenceType
|
|
23
|
+
from cyclonedx.model.bom import Bom
|
|
24
|
+
from cyclonedx.model.component import Component, ComponentType, HashAlgorithm, HashType, ExternalReference
|
|
25
|
+
from cyclonedx.output import make_outputter, BaseOutput
|
|
26
|
+
from cyclonedx.output.json import JsonV1Dot6
|
|
27
|
+
from cyclonedx.schema import OutputFormat, SchemaVersion
|
|
28
|
+
from cyclonedx.validation.json import JsonStrictValidator
|
|
29
|
+
from cyclonedx.output.json import Json as JsonOutputter
|
|
30
|
+
from cyclonedx.validation.xml import XmlValidator
|
|
31
|
+
except Exception:
|
|
32
|
+
logger.info('No import cyclonedx-python-lib')
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def write_cyclonedx(output_file_without_ext, output_extension, scan_item):
|
|
36
|
+
success = True
|
|
37
|
+
error_msg = ''
|
|
38
|
+
|
|
39
|
+
bom = Bom()
|
|
40
|
+
if scan_item:
|
|
41
|
+
try:
|
|
42
|
+
cover_name = scan_item.cover.get_print_json()["Tool information"].split('(').pop(0).strip()
|
|
43
|
+
match = re.search(r"(.+) v([0-9.]+)", cover_name)
|
|
44
|
+
if match:
|
|
45
|
+
scanner_name = match.group(1)
|
|
46
|
+
else:
|
|
47
|
+
scanner_name = FOSSLIGHT_SCANNER
|
|
48
|
+
except Exception:
|
|
49
|
+
cover_name = FOSSLIGHT_SCANNER
|
|
50
|
+
scanner_name = FOSSLIGHT_SCANNER
|
|
51
|
+
|
|
52
|
+
lc_factory = LicenseFactory()
|
|
53
|
+
bom.metadata.tools.components.add(cdx_lib_component())
|
|
54
|
+
bom.metadata.tools.components.add(Component(name=scanner_name.upper(),
|
|
55
|
+
type=ComponentType.APPLICATION))
|
|
56
|
+
comp_id = 0
|
|
57
|
+
bom.metadata.component = root_component = Component(name='Root Component',
|
|
58
|
+
type=ComponentType.APPLICATION,
|
|
59
|
+
bom_ref=str(comp_id))
|
|
60
|
+
relation_tree = {}
|
|
61
|
+
|
|
62
|
+
output_dir = os.path.dirname(output_file_without_ext)
|
|
63
|
+
Path(output_dir).mkdir(parents=True, exist_ok=True)
|
|
64
|
+
try:
|
|
65
|
+
root_package = False
|
|
66
|
+
for scanner_name, file_items in scan_item.file_items.items():
|
|
67
|
+
for file_item in file_items:
|
|
68
|
+
if file_item.exclude:
|
|
69
|
+
continue
|
|
70
|
+
if scanner_name == FOSSLIGHT_SOURCE:
|
|
71
|
+
comp_type = ComponentType.FILE
|
|
72
|
+
else:
|
|
73
|
+
comp_type = ComponentType.LIBRARY
|
|
74
|
+
|
|
75
|
+
for oss_item in file_item.oss_items:
|
|
76
|
+
if oss_item.name == '' or oss_item.name == '-':
|
|
77
|
+
if scanner_name == FOSSLIGHT_DEPENDENCY:
|
|
78
|
+
continue
|
|
79
|
+
else:
|
|
80
|
+
comp_name = file_item.source_name_or_path
|
|
81
|
+
else:
|
|
82
|
+
comp_name = oss_item.name
|
|
83
|
+
|
|
84
|
+
comp_id += 1
|
|
85
|
+
comp = Component(type=comp_type,
|
|
86
|
+
name=comp_name,
|
|
87
|
+
bom_ref=str(comp_id))
|
|
88
|
+
|
|
89
|
+
if oss_item.version != '':
|
|
90
|
+
comp.version = oss_item.version
|
|
91
|
+
if oss_item.copyright != '':
|
|
92
|
+
comp.copyright = oss_item.copyright
|
|
93
|
+
if scanner_name == FOSSLIGHT_DEPENDENCY and file_item.purl:
|
|
94
|
+
comp.purl = PackageURL.from_string(file_item.purl)
|
|
95
|
+
if scanner_name != FOSSLIGHT_DEPENDENCY:
|
|
96
|
+
if file_item.checksum != '0':
|
|
97
|
+
comp.hashes = [HashType(alg=HashAlgorithm.SHA_1, content=file_item.checksum)]
|
|
98
|
+
|
|
99
|
+
if oss_item.download_location != '':
|
|
100
|
+
comp.external_references = [ExternalReference(url=XsUri(oss_item.download_location),
|
|
101
|
+
type=ExternalReferenceType.WEBSITE)]
|
|
102
|
+
|
|
103
|
+
oss_licenses = []
|
|
104
|
+
for ol in oss_item.license:
|
|
105
|
+
try:
|
|
106
|
+
oss_licenses.append(lc_factory.make_from_string(ol))
|
|
107
|
+
except Exception:
|
|
108
|
+
logger.info(f'No spdx license name: {ol}')
|
|
109
|
+
if oss_licenses:
|
|
110
|
+
comp.licenses = oss_licenses
|
|
111
|
+
|
|
112
|
+
root_package = False
|
|
113
|
+
if scanner_name == FOSSLIGHT_DEPENDENCY:
|
|
114
|
+
if oss_item.comment:
|
|
115
|
+
oss_comment = oss_item.comment.split('/')
|
|
116
|
+
for oc in oss_comment:
|
|
117
|
+
if oc in ['direct', 'transitive', 'root package']:
|
|
118
|
+
if oc == 'direct':
|
|
119
|
+
bom.register_dependency(root_component, [comp])
|
|
120
|
+
elif oc == 'root package':
|
|
121
|
+
root_package = True
|
|
122
|
+
root_component.name = comp_name
|
|
123
|
+
root_component.type = comp_type
|
|
124
|
+
comp_id -= 1
|
|
125
|
+
else:
|
|
126
|
+
bom.register_dependency(root_component, [comp])
|
|
127
|
+
if len(file_item.depends_on) > 0:
|
|
128
|
+
purl = file_item.purl
|
|
129
|
+
relation_tree[purl] = []
|
|
130
|
+
relation_tree[purl].extend(file_item.depends_on)
|
|
131
|
+
|
|
132
|
+
if not root_package:
|
|
133
|
+
bom.components.add(comp)
|
|
134
|
+
|
|
135
|
+
if len(bom.components) > 0:
|
|
136
|
+
for comp_purl in relation_tree:
|
|
137
|
+
comp = bom.get_component_by_purl(PackageURL.from_string(comp_purl))
|
|
138
|
+
if comp:
|
|
139
|
+
dep_comp_list = []
|
|
140
|
+
for dep_comp_purl in relation_tree[comp_purl]:
|
|
141
|
+
dep_comp = bom.get_component_by_purl(PackageURL.from_string(dep_comp_purl))
|
|
142
|
+
if dep_comp:
|
|
143
|
+
dep_comp_list.append(dep_comp)
|
|
144
|
+
bom.register_dependency(comp, dep_comp_list)
|
|
145
|
+
|
|
146
|
+
except Exception as e:
|
|
147
|
+
success = False
|
|
148
|
+
error_msg = f'Failed to create CycloneDX document object:{e}, {traceback.format_exc()}'
|
|
149
|
+
else:
|
|
150
|
+
success = False
|
|
151
|
+
error_msg = 'No item to write in output file.'
|
|
152
|
+
|
|
153
|
+
result_file = ''
|
|
154
|
+
if success:
|
|
155
|
+
result_file = output_file_without_ext + output_extension
|
|
156
|
+
try:
|
|
157
|
+
if output_extension == '.json':
|
|
158
|
+
write_cyclonedx_json(bom, result_file)
|
|
159
|
+
elif output_extension == '.xml':
|
|
160
|
+
write_cyclonedx_xml(bom, result_file)
|
|
161
|
+
else:
|
|
162
|
+
success = False
|
|
163
|
+
error_msg = f'Not supported output_extension({output_extension})'
|
|
164
|
+
except Exception as e:
|
|
165
|
+
success = False
|
|
166
|
+
error_msg = f'Failed to write CycloneDX document: {e}'
|
|
167
|
+
if os.path.exists(result_file):
|
|
168
|
+
os.remove(result_file)
|
|
169
|
+
|
|
170
|
+
return success, error_msg, result_file
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def write_cyclonedx_json(bom, result_file):
|
|
174
|
+
success = True
|
|
175
|
+
try:
|
|
176
|
+
my_json_outputter: 'JsonOutputter' = JsonV1Dot6(bom)
|
|
177
|
+
my_json_outputter.output_to_file(result_file)
|
|
178
|
+
serialized_json = my_json_outputter.output_as_string(indent=2)
|
|
179
|
+
my_json_validator = JsonStrictValidator(SchemaVersion.V1_6)
|
|
180
|
+
try:
|
|
181
|
+
validation_errors = my_json_validator.validate_str(serialized_json)
|
|
182
|
+
if validation_errors:
|
|
183
|
+
logger.warning(f'JSON invalid, ValidationError: {repr(validation_errors)}')
|
|
184
|
+
except MissingOptionalDependencyException as error:
|
|
185
|
+
logger.debug(f'JSON-validation was skipped due to {error}')
|
|
186
|
+
except Exception as e:
|
|
187
|
+
logger.warning(f'Fail to write cyclonedx json: {e}')
|
|
188
|
+
success = False
|
|
189
|
+
return success
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def write_cyclonedx_xml(bom, result_file):
|
|
193
|
+
success = True
|
|
194
|
+
try:
|
|
195
|
+
my_xml_outputter: BaseOutput = make_outputter(bom=bom,
|
|
196
|
+
output_format=OutputFormat.XML,
|
|
197
|
+
schema_version=SchemaVersion.V1_6)
|
|
198
|
+
my_xml_outputter.output_to_file(filename=result_file)
|
|
199
|
+
serialized_xml = my_xml_outputter.output_as_string(indent=2)
|
|
200
|
+
my_xml_validator = XmlValidator(SchemaVersion.V1_6)
|
|
201
|
+
try:
|
|
202
|
+
validation_errors = my_xml_validator.validate_str(serialized_xml)
|
|
203
|
+
if validation_errors:
|
|
204
|
+
logger.warning(f'XML invalid, ValidationError: {repr(validation_errors)}')
|
|
205
|
+
except MissingOptionalDependencyException as error:
|
|
206
|
+
logger.debug(f'XML-validation was skipped due to {error}')
|
|
207
|
+
except Exception as e:
|
|
208
|
+
logger.warning(f'Fail to write cyclonedx xml: {e}')
|
|
209
|
+
success = False
|
|
210
|
+
return success
|
fosslight_util/write_excel.py
CHANGED
|
@@ -34,6 +34,7 @@ IDX_FILE = 0
|
|
|
34
34
|
IDX_EXCLUDE = 7
|
|
35
35
|
logger = logging.getLogger(LOGGER_NAME)
|
|
36
36
|
COVER_SHEET_NAME = 'Scanner Info'
|
|
37
|
+
MAX_EXCEL_URL_LENGTH = 255
|
|
37
38
|
|
|
38
39
|
|
|
39
40
|
def get_header_row(sheet_name, extended_header={}):
|
|
@@ -181,7 +182,10 @@ def write_result_to_sheet(worksheet, sheet_contents):
|
|
|
181
182
|
for row_item in sheet_contents:
|
|
182
183
|
worksheet.write(row, 0, row)
|
|
183
184
|
for col_num, value in enumerate(row_item):
|
|
184
|
-
|
|
185
|
+
if len(value) > MAX_EXCEL_URL_LENGTH and (value.startswith("http://") or value.startswith("https://")):
|
|
186
|
+
worksheet.write_string(row, col_num + 1, str(value))
|
|
187
|
+
else:
|
|
188
|
+
worksheet.write(row, col_num + 1, str(value))
|
|
185
189
|
row += 1
|
|
186
190
|
|
|
187
191
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import logging
|
|
7
7
|
import os
|
|
8
8
|
import json
|
|
9
|
-
from fosslight_util.constant import LOGGER_NAME
|
|
9
|
+
from fosslight_util.constant import LOGGER_NAME, FOSSLIGHT_DEPENDENCY
|
|
10
10
|
from fosslight_util.oss_item import ScannerItem
|
|
11
11
|
from typing import List
|
|
12
12
|
|
|
@@ -20,22 +20,27 @@ def write_scancodejson(output_dir: str, output_filename: str, oss_list: List[Sca
|
|
|
20
20
|
json_output['summary'] = {}
|
|
21
21
|
json_output['license_detections'] = []
|
|
22
22
|
json_output['files'] = []
|
|
23
|
+
json_output['dependencies'] = []
|
|
23
24
|
|
|
24
|
-
for file_items in oss_list.file_items.
|
|
25
|
+
for scanner, file_items in oss_list.file_items.items():
|
|
25
26
|
for fi in file_items:
|
|
26
|
-
if
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
fi.
|
|
32
|
-
|
|
27
|
+
if scanner == FOSSLIGHT_DEPENDENCY:
|
|
28
|
+
json_output['dependencies'] = add_item_in_deps(fi, json_output['dependencies'])
|
|
29
|
+
else:
|
|
30
|
+
if fi.exclude:
|
|
31
|
+
continue
|
|
32
|
+
if fi.oss_items and (all(oss_item.exclude for oss_item in fi.oss_items)):
|
|
33
|
+
continue
|
|
34
|
+
if not fi.source_name_or_path:
|
|
35
|
+
fi.source_name_or_path = EMPTY_FILE_PATH
|
|
36
|
+
json_output['files'] = add_item_in_files(fi, json_output['files'])
|
|
33
37
|
|
|
34
38
|
with open(os.path.join(output_dir, output_filename), 'w') as f:
|
|
35
39
|
json.dump(json_output, f, sort_keys=False, indent=4)
|
|
36
40
|
|
|
37
41
|
|
|
38
|
-
def
|
|
42
|
+
def get_oss_item_list(oss_items):
|
|
43
|
+
scan_oss_items = []
|
|
39
44
|
for oi in oss_items:
|
|
40
45
|
if oi.exclude:
|
|
41
46
|
continue
|
|
@@ -46,9 +51,9 @@ def append_oss_item_in_filesitem(oss_items, files_item):
|
|
|
46
51
|
oss_item['copyright'] = oi.copyright
|
|
47
52
|
oss_item['download_location'] = oi.download_location
|
|
48
53
|
oss_item['comment'] = oi.comment
|
|
49
|
-
|
|
54
|
+
scan_oss_items.append(oss_item)
|
|
50
55
|
|
|
51
|
-
return
|
|
56
|
+
return scan_oss_items
|
|
52
57
|
|
|
53
58
|
|
|
54
59
|
def add_item_in_files(file_item, files_list):
|
|
@@ -57,8 +62,20 @@ def add_item_in_files(file_item, files_list):
|
|
|
57
62
|
files_item['name'] = os.path.basename(file_item.source_name_or_path)
|
|
58
63
|
files_item['is_binary'] = file_item.is_binary
|
|
59
64
|
files_item['base_name'], files_item['extension'] = os.path.splitext(os.path.basename(file_item.source_name_or_path))
|
|
60
|
-
files_item['oss'] =
|
|
61
|
-
|
|
65
|
+
files_item['oss'] = get_oss_item_list(file_item.oss_items)
|
|
66
|
+
|
|
62
67
|
files_list.append(files_item)
|
|
63
68
|
|
|
64
69
|
return files_list
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def add_item_in_deps(file_item, deps_list):
|
|
73
|
+
deps_item = {}
|
|
74
|
+
deps_item['purl'] = file_item.purl
|
|
75
|
+
deps_item['scope'] = 'dependencies'
|
|
76
|
+
deps_item['depends_on'] = file_item.depends_on
|
|
77
|
+
deps_item['oss'] = get_oss_item_list(file_item.oss_items)
|
|
78
|
+
|
|
79
|
+
deps_list.append(deps_item)
|
|
80
|
+
|
|
81
|
+
return deps_list
|