fosslight-util 1.4.34__py3-none-any.whl → 2.1.28__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 +466 -36
- fosslight_util/compare_yaml.py +20 -11
- fosslight_util/constant.py +35 -0
- fosslight_util/correct.py +46 -78
- fosslight_util/cover.py +60 -0
- fosslight_util/download.py +302 -95
- fosslight_util/exclude.py +65 -0
- fosslight_util/help.py +20 -8
- fosslight_util/oss_item.py +171 -110
- fosslight_util/output_format.py +147 -19
- fosslight_util/parsing_yaml.py +45 -23
- fosslight_util/read_excel.py +40 -39
- fosslight_util/set_log.py +30 -5
- fosslight_util/spdx_licenses.py +2 -1
- fosslight_util/write_cyclonedx.py +210 -0
- fosslight_util/write_excel.py +141 -133
- fosslight_util/write_opossum.py +14 -20
- fosslight_util/write_scancodejson.py +51 -32
- fosslight_util/write_spdx.py +162 -115
- fosslight_util/write_txt.py +2 -1
- fosslight_util/write_yaml.py +43 -49
- {fosslight_util-1.4.34.dist-info → fosslight_util-2.1.28.dist-info}/METADATA +32 -24
- fosslight_util-2.1.28.dist-info/RECORD +32 -0
- {fosslight_util-1.4.34.dist-info → fosslight_util-2.1.28.dist-info}/WHEEL +1 -1
- {fosslight_util-1.4.34.dist-info → fosslight_util-2.1.28.dist-info}/entry_points.txt +0 -1
- fosslight_util/convert_excel_to_yaml.py +0 -69
- fosslight_util-1.4.34.dist-info/RECORD +0 -30
- {fosslight_util-1.4.34.dist-info → fosslight_util-2.1.28.dist-info/licenses}/LICENSE +0 -0
- {fosslight_util-1.4.34.dist-info → fosslight_util-2.1.28.dist-info}/top_level.txt +0 -0
fosslight_util/read_excel.py
CHANGED
|
@@ -3,26 +3,25 @@
|
|
|
3
3
|
# Copyright (c) 2021 LG Electronics Inc.
|
|
4
4
|
# SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
import logging
|
|
6
|
-
import
|
|
6
|
+
from typing import List, Dict, Any
|
|
7
|
+
import pandas as pd
|
|
7
8
|
import json
|
|
8
9
|
from fosslight_util.constant import LOGGER_NAME
|
|
9
|
-
from fosslight_util.oss_item import OssItem
|
|
10
|
+
from fosslight_util.oss_item import OssItem, FileItem
|
|
10
11
|
from fosslight_util.parsing_yaml import set_value_switch
|
|
11
12
|
|
|
12
13
|
logger = logging.getLogger(LOGGER_NAME)
|
|
13
14
|
IDX_CANNOT_FOUND = -1
|
|
14
15
|
PREFIX_BIN = "bin"
|
|
15
|
-
|
|
16
|
-
xlrd.xlsx.Element_has_iter = True
|
|
16
|
+
SHEET_PREFIX_TO_READ = ["bin", "bom", "src"]
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
def read_oss_report(excel_file, sheet_names=""):
|
|
20
|
-
|
|
21
|
-
xl_sheets = {}
|
|
22
|
-
all_sheet_to_read = []
|
|
23
|
-
not_matched_sheet = []
|
|
19
|
+
def read_oss_report(excel_file: str, sheet_names: str = "", basepath: str = "") -> List[FileItem]:
|
|
20
|
+
fileitems: List[FileItem] = []
|
|
21
|
+
xl_sheets: Dict[str, Any] = {}
|
|
22
|
+
all_sheet_to_read: List[str] = []
|
|
23
|
+
not_matched_sheet: List[str] = []
|
|
24
24
|
any_sheet_matched = False
|
|
25
|
-
SHEET_PREFIX_TO_READ = ["bin", "bom", "src"]
|
|
26
25
|
if sheet_names:
|
|
27
26
|
sheet_name_prefix_match = False
|
|
28
27
|
sheet_name_to_read = sheet_names.split(",")
|
|
@@ -32,9 +31,8 @@ def read_oss_report(excel_file, sheet_names=""):
|
|
|
32
31
|
|
|
33
32
|
try:
|
|
34
33
|
logger.info(f"Read data from : {excel_file}")
|
|
35
|
-
xl_workbook =
|
|
36
|
-
all_sheet_in_excel = xl_workbook.sheet_names
|
|
37
|
-
|
|
34
|
+
xl_workbook = pd.ExcelFile(excel_file, engine='openpyxl')
|
|
35
|
+
all_sheet_in_excel = xl_workbook.sheet_names
|
|
38
36
|
for sheet_to_read in sheet_name_to_read:
|
|
39
37
|
try:
|
|
40
38
|
any_sheet_matched = False
|
|
@@ -44,10 +42,9 @@ def read_oss_report(excel_file, sheet_names=""):
|
|
|
44
42
|
sheet_name_lower = sheet_name.lower()
|
|
45
43
|
if (sheet_name_prefix_match and sheet_name_lower.startswith(sheet_to_read_lower)) \
|
|
46
44
|
or sheet_to_read_lower == sheet_name_lower:
|
|
47
|
-
sheet =
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
any_sheet_matched = True
|
|
45
|
+
sheet = pd.read_excel(excel_file, sheet_name=sheet_name, engine='openpyxl', na_values='')
|
|
46
|
+
xl_sheets[sheet_name] = sheet.fillna('')
|
|
47
|
+
any_sheet_matched = True
|
|
51
48
|
if not any_sheet_matched:
|
|
52
49
|
not_matched_sheet.append(sheet_to_read)
|
|
53
50
|
except Exception as error:
|
|
@@ -60,11 +57,14 @@ def read_oss_report(excel_file, sheet_names=""):
|
|
|
60
57
|
elif (not sheet_name_prefix_match) and not_matched_sheet:
|
|
61
58
|
logger.warning(f"Not matched sheet name: {not_matched_sheet}")
|
|
62
59
|
|
|
60
|
+
filepath_list = []
|
|
63
61
|
for sheet_name, xl_sheet in xl_sheets.items():
|
|
64
62
|
_item_idx = {
|
|
65
63
|
"ID": IDX_CANNOT_FOUND,
|
|
66
64
|
"Source Name or Path": IDX_CANNOT_FOUND,
|
|
65
|
+
"Source Path": IDX_CANNOT_FOUND,
|
|
67
66
|
"Binary Name": IDX_CANNOT_FOUND,
|
|
67
|
+
"Binary Path": IDX_CANNOT_FOUND,
|
|
68
68
|
"OSS Name": IDX_CANNOT_FOUND,
|
|
69
69
|
"OSS Version": IDX_CANNOT_FOUND,
|
|
70
70
|
"License": IDX_CANNOT_FOUND,
|
|
@@ -73,48 +73,49 @@ def read_oss_report(excel_file, sheet_names=""):
|
|
|
73
73
|
"Exclude": IDX_CANNOT_FOUND,
|
|
74
74
|
"Copyright Text": IDX_CANNOT_FOUND,
|
|
75
75
|
"Comment": IDX_CANNOT_FOUND,
|
|
76
|
-
"File Name or Path": IDX_CANNOT_FOUND
|
|
76
|
+
"File Name or Path": IDX_CANNOT_FOUND,
|
|
77
|
+
"Vulnerability Link": IDX_CANNOT_FOUND,
|
|
78
|
+
"TLSH": IDX_CANNOT_FOUND,
|
|
79
|
+
"SHA1": IDX_CANNOT_FOUND
|
|
77
80
|
}
|
|
78
|
-
num_cols = xl_sheet.ncols
|
|
79
|
-
num_rows = xl_sheet.nrows
|
|
80
|
-
MAX_FIND_HEADER_COLUMN = 5 if num_rows > 5 else num_rows
|
|
81
|
-
DATA_START_ROW_IDX = 1
|
|
82
|
-
for row_idx in range(0, MAX_FIND_HEADER_COLUMN):
|
|
83
|
-
for col_idx in range(row_idx, num_cols):
|
|
84
|
-
cell_obj = xl_sheet.cell(row_idx, col_idx)
|
|
85
|
-
if cell_obj.value in _item_idx:
|
|
86
|
-
_item_idx[cell_obj.value] = col_idx
|
|
87
81
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
break
|
|
82
|
+
for index, value in enumerate(xl_sheet.columns.tolist()):
|
|
83
|
+
_item_idx[value] = index
|
|
91
84
|
|
|
92
85
|
# Get all values, iterating through rows and columns
|
|
93
86
|
column_keys = json.loads(json.dumps(_item_idx))
|
|
94
87
|
|
|
95
88
|
is_bin = True if sheet_name.lower().startswith(PREFIX_BIN) else False
|
|
96
89
|
|
|
97
|
-
for row_idx in
|
|
98
|
-
item = OssItem("")
|
|
99
|
-
item.is_binary = is_bin
|
|
90
|
+
for row_idx, row in xl_sheet.iterrows():
|
|
100
91
|
valid_row = True
|
|
101
92
|
load_data_cnt = 0
|
|
102
|
-
|
|
93
|
+
source_path = row[1]
|
|
94
|
+
if source_path not in filepath_list:
|
|
95
|
+
filepath_list.append(source_path)
|
|
96
|
+
fileitem = FileItem(basepath)
|
|
97
|
+
fileitem.source_name_or_path = source_path
|
|
98
|
+
fileitems.append(fileitem)
|
|
99
|
+
else:
|
|
100
|
+
fileitem = next((i for i in fileitems if i.source_name_or_path == source_path), None)
|
|
101
|
+
fileitem.is_binary = is_bin
|
|
102
|
+
ossitem = OssItem()
|
|
103
103
|
for column_key, column_idx in column_keys.items():
|
|
104
104
|
if column_idx != IDX_CANNOT_FOUND:
|
|
105
|
-
cell_obj = xl_sheet.
|
|
106
|
-
cell_value = cell_obj
|
|
105
|
+
cell_obj = xl_sheet.iloc[row_idx, column_idx]
|
|
106
|
+
cell_value = cell_obj
|
|
107
|
+
|
|
107
108
|
if cell_value != "":
|
|
108
109
|
if column_key != "ID":
|
|
109
110
|
if column_key:
|
|
110
111
|
column_key = column_key.lower().strip()
|
|
111
|
-
set_value_switch(
|
|
112
|
+
set_value_switch(ossitem, column_key, cell_value)
|
|
112
113
|
load_data_cnt += 1
|
|
113
114
|
else:
|
|
114
115
|
valid_row = False if cell_value == "-" else True
|
|
115
116
|
if valid_row and load_data_cnt > 0:
|
|
116
|
-
|
|
117
|
+
fileitem.oss_items.append(ossitem)
|
|
117
118
|
|
|
118
119
|
except Exception as error:
|
|
119
120
|
logger.error(f"Parsing a OSS Report: {error}")
|
|
120
|
-
return
|
|
121
|
+
return fileitems
|
fosslight_util/set_log.py
CHANGED
|
@@ -6,12 +6,18 @@
|
|
|
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
|
|
13
12
|
from lastversion import lastversion
|
|
14
13
|
import coloredlogs
|
|
14
|
+
from typing import Tuple
|
|
15
|
+
from logging import Logger
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
19
|
+
except ImportError:
|
|
20
|
+
from importlib_metadata import version, PackageNotFoundError # Python <3.8
|
|
15
21
|
|
|
16
22
|
|
|
17
23
|
def init_check_latest_version(pkg_version="", main_package_name=""):
|
|
@@ -32,6 +38,21 @@ def init_check_latest_version(pkg_version="", main_package_name=""):
|
|
|
32
38
|
logger.debug('Cannot check the latest version:' + str(error))
|
|
33
39
|
|
|
34
40
|
|
|
41
|
+
def get_os_version():
|
|
42
|
+
|
|
43
|
+
logger = logging.getLogger(constant.LOGGER_NAME)
|
|
44
|
+
|
|
45
|
+
os_version = platform.system() + " " + platform.release()
|
|
46
|
+
if os_version == "Windows 10":
|
|
47
|
+
try:
|
|
48
|
+
windows_build = sys.getwindowsversion().build
|
|
49
|
+
if windows_build >= 22000:
|
|
50
|
+
os_version = "Windows 11"
|
|
51
|
+
except Exception as error:
|
|
52
|
+
logger.debug(str(error))
|
|
53
|
+
return os_version
|
|
54
|
+
|
|
55
|
+
|
|
35
56
|
class CustomAdapter(logging.LoggerAdapter):
|
|
36
57
|
def __init__(self, logger, extra):
|
|
37
58
|
super(CustomAdapter, self).__init__(logger, {})
|
|
@@ -41,8 +62,8 @@ class CustomAdapter(logging.LoggerAdapter):
|
|
|
41
62
|
return '[%s] %s' % (self.extra, msg), kwargs
|
|
42
63
|
|
|
43
64
|
|
|
44
|
-
def init_log(log_file, create_file=True, stream_log_level=logging.INFO,
|
|
45
|
-
|
|
65
|
+
def init_log(log_file: str, create_file: bool = True, stream_log_level: int = logging.INFO, file_log_level: int = logging.DEBUG,
|
|
66
|
+
main_package_name: str = "", path_to_analyze: str = "", path_to_exclude: list = []) -> Tuple[Logger, dict]:
|
|
46
67
|
|
|
47
68
|
logger = logging.getLogger(constant.LOGGER_NAME)
|
|
48
69
|
|
|
@@ -70,18 +91,22 @@ def init_log(log_file, create_file=True, stream_log_level=logging.INFO,
|
|
|
70
91
|
_result_log = {
|
|
71
92
|
"Tool Info": main_package_name,
|
|
72
93
|
"Python version": _PYTHON_VERSION,
|
|
73
|
-
"OS":
|
|
94
|
+
"OS": get_os_version(),
|
|
74
95
|
}
|
|
75
96
|
if main_package_name != "":
|
|
76
97
|
pkg_info = main_package_name
|
|
77
98
|
try:
|
|
78
|
-
pkg_version =
|
|
99
|
+
pkg_version = version(main_package_name)
|
|
79
100
|
init_check_latest_version(pkg_version, main_package_name)
|
|
80
101
|
pkg_info = main_package_name + " v" + pkg_version
|
|
102
|
+
except PackageNotFoundError:
|
|
103
|
+
logger.debug('Cannot check the version: Package not found')
|
|
81
104
|
except Exception as error:
|
|
82
105
|
logger.debug('Cannot check the version:' + str(error))
|
|
83
106
|
_result_log["Tool Info"] = pkg_info
|
|
84
107
|
if path_to_analyze != "":
|
|
85
108
|
_result_log["Path to analyze"] = path_to_analyze
|
|
109
|
+
if path_to_exclude != []:
|
|
110
|
+
_result_log["Path to exclude"] = ", ".join(path_to_exclude)
|
|
86
111
|
|
|
87
112
|
return logger, _result_log
|
fosslight_util/spdx_licenses.py
CHANGED
|
@@ -8,6 +8,7 @@ import os
|
|
|
8
8
|
import sys
|
|
9
9
|
import json
|
|
10
10
|
import traceback
|
|
11
|
+
from typing import Tuple
|
|
11
12
|
|
|
12
13
|
_resources_dir = 'resources'
|
|
13
14
|
_licenses_json_file = 'licenses.json'
|
|
@@ -34,7 +35,7 @@ def get_license_from_nick():
|
|
|
34
35
|
return licenses
|
|
35
36
|
|
|
36
37
|
|
|
37
|
-
def get_spdx_licenses_json():
|
|
38
|
+
def get_spdx_licenses_json() -> Tuple[bool, str, str]:
|
|
38
39
|
success = True
|
|
39
40
|
error_msg = ''
|
|
40
41
|
licenses = ''
|
|
@@ -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
|