capycli 2.0.0.dev8__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.
- License.md +27 -0
- capycli/__init__.py +214 -0
- capycli/__main__.py +13 -0
- capycli/bom/__init__.py +10 -0
- capycli/bom/bom_convert.py +163 -0
- capycli/bom/check_bom.py +187 -0
- capycli/bom/check_bom_item_status.py +197 -0
- capycli/bom/check_granularity.py +244 -0
- capycli/bom/create_components.py +644 -0
- capycli/bom/csv.py +69 -0
- capycli/bom/diff_bom.py +279 -0
- capycli/bom/download_sources.py +227 -0
- capycli/bom/filter_bom.py +323 -0
- capycli/bom/findsources.py +278 -0
- capycli/bom/handle_bom.py +134 -0
- capycli/bom/html.py +67 -0
- capycli/bom/legacy.py +312 -0
- capycli/bom/legacy_cx.py +151 -0
- capycli/bom/map_bom.py +1039 -0
- capycli/bom/merge_bom.py +155 -0
- capycli/bom/plaintext.py +69 -0
- capycli/bom/show_bom.py +77 -0
- capycli/common/__init__.py +9 -0
- capycli/common/capycli_bom_support.py +629 -0
- capycli/common/comparable_version.py +161 -0
- capycli/common/component_cache.py +240 -0
- capycli/common/dependencies_base.py +48 -0
- capycli/common/file_support.py +28 -0
- capycli/common/html_support.py +119 -0
- capycli/common/json_support.py +36 -0
- capycli/common/map_result.py +116 -0
- capycli/common/print.py +55 -0
- capycli/common/purl_service.py +169 -0
- capycli/common/purl_store.py +100 -0
- capycli/common/purl_utils.py +85 -0
- capycli/common/script_base.py +165 -0
- capycli/common/script_support.py +78 -0
- capycli/data/__init__.py +9 -0
- capycli/data/granularity_list.csv +1338 -0
- capycli/dependencies/__init__.py +9 -0
- capycli/dependencies/handle_dependencies.py +70 -0
- capycli/dependencies/javascript.py +261 -0
- capycli/dependencies/maven_list.py +333 -0
- capycli/dependencies/maven_pom.py +150 -0
- capycli/dependencies/nuget.py +184 -0
- capycli/dependencies/python.py +345 -0
- capycli/main/__init__.py +9 -0
- capycli/main/application.py +165 -0
- capycli/main/argument_parser.py +101 -0
- capycli/main/cli.py +28 -0
- capycli/main/exceptions.py +14 -0
- capycli/main/options.py +424 -0
- capycli/main/result_codes.py +41 -0
- capycli/mapping/handle_mapping.py +46 -0
- capycli/mapping/mapping_to_html.py +182 -0
- capycli/mapping/mapping_to_xlsx.py +197 -0
- capycli/moverview/handle_moverview.py +46 -0
- capycli/moverview/moverview_to_html.py +122 -0
- capycli/moverview/moverview_to_xlsx.py +170 -0
- capycli/project/__init__.py +9 -0
- capycli/project/check_prerequisites.py +304 -0
- capycli/project/create_bom.py +190 -0
- capycli/project/create_project.py +335 -0
- capycli/project/create_readme.py +546 -0
- capycli/project/find_project.py +128 -0
- capycli/project/get_license_info.py +246 -0
- capycli/project/handle_project.py +118 -0
- capycli/project/show_ecc.py +200 -0
- capycli/project/show_licenses.py +211 -0
- capycli/project/show_project.py +215 -0
- capycli/project/show_vulnerabilities.py +238 -0
- capycli-2.0.0.dev8.dist-info/LICENSES/CC0-1.0.txt +121 -0
- capycli-2.0.0.dev8.dist-info/LICENSES/MIT.txt +27 -0
- capycli-2.0.0.dev8.dist-info/License.md +27 -0
- capycli-2.0.0.dev8.dist-info/METADATA +268 -0
- capycli-2.0.0.dev8.dist-info/RECORD +78 -0
- capycli-2.0.0.dev8.dist-info/WHEEL +4 -0
- capycli-2.0.0.dev8.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) 2019-23 Siemens
|
|
3
|
+
# All Rights Reserved.
|
|
4
|
+
# Author: thomas.graf@siemens.com
|
|
5
|
+
#
|
|
6
|
+
# SPDX-License-Identifier: MIT
|
|
7
|
+
# -------------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
|
|
13
|
+
import requests
|
|
14
|
+
import sw360.sw360_api
|
|
15
|
+
from colorama import Fore, Style
|
|
16
|
+
from cyclonedx.model.bom import Bom
|
|
17
|
+
from cyclonedx.model.component import Component
|
|
18
|
+
|
|
19
|
+
import capycli.common.script_base
|
|
20
|
+
from capycli.common.capycli_bom_support import CaPyCliBom, CycloneDxSupport
|
|
21
|
+
from capycli.common.print import print_red, print_text
|
|
22
|
+
from capycli.main.result_codes import ResultCode
|
|
23
|
+
|
|
24
|
+
LOG = capycli.get_logger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CheckBomItemStatus(capycli.common.script_base.ScriptBase):
|
|
28
|
+
"""Print SBOM item status to stdout"""
|
|
29
|
+
|
|
30
|
+
def _bom_has_items_without_id(self, bom: Bom) -> bool:
|
|
31
|
+
"""Determines whether there is at least one SBOM item
|
|
32
|
+
without Sw360Id."""
|
|
33
|
+
for item in bom.components:
|
|
34
|
+
sw360id = CycloneDxSupport.get_property_value(item, CycloneDxSupport.CDX_PROP_SW360ID)
|
|
35
|
+
if not sw360id:
|
|
36
|
+
return True
|
|
37
|
+
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
def _find_by_id(self, component: Component) -> dict | None:
|
|
41
|
+
sw360id = CycloneDxSupport.get_property_value(component, CycloneDxSupport.CDX_PROP_SW360ID)
|
|
42
|
+
try:
|
|
43
|
+
release_details = self.client.get_release(sw360id)
|
|
44
|
+
return release_details
|
|
45
|
+
except sw360.sw360_api.SW360Error as swex:
|
|
46
|
+
if swex.response.status_code == requests.codes['not_found']:
|
|
47
|
+
print(
|
|
48
|
+
Fore.LIGHTYELLOW_EX + " Not found " + component.name +
|
|
49
|
+
", " + component.version + ", " +
|
|
50
|
+
sw360id + Style.RESET_ALL)
|
|
51
|
+
else:
|
|
52
|
+
print(Fore.LIGHTRED_EX + " Error retrieving release data: ")
|
|
53
|
+
print(
|
|
54
|
+
" " + str(component.name) + ", " + str(component.version) +
|
|
55
|
+
", " + sw360id)
|
|
56
|
+
print(" Status Code: " + str(swex.response.status_code))
|
|
57
|
+
if swex.message:
|
|
58
|
+
print(" Message: " + swex.message)
|
|
59
|
+
print(Style.RESET_ALL)
|
|
60
|
+
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
def _find_by_name(self, component: Component) -> dict | None:
|
|
64
|
+
try:
|
|
65
|
+
releases = self.client.get_releases_by_name(component.name)
|
|
66
|
+
if not releases:
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
for r in releases:
|
|
70
|
+
if r.get("version", "") == component.version:
|
|
71
|
+
return self.client.get_release_by_url(r["_links"]["self"]["href"])
|
|
72
|
+
|
|
73
|
+
return None
|
|
74
|
+
except sw360.sw360_api.SW360Error as swex:
|
|
75
|
+
if swex.response.status_code == requests.codes['not_found']:
|
|
76
|
+
print(
|
|
77
|
+
Fore.LIGHTYELLOW_EX + " Not found " + component.name +
|
|
78
|
+
", " + component.version + ", " +
|
|
79
|
+
Style.RESET_ALL)
|
|
80
|
+
else:
|
|
81
|
+
print(Fore.LIGHTRED_EX + " Error retrieving release data: ")
|
|
82
|
+
print(" " + str(component.name) + ", " + str(component.version))
|
|
83
|
+
print(" Status Code: " + str(swex.response.status_code))
|
|
84
|
+
if swex.message:
|
|
85
|
+
print(" Message: " + swex.message)
|
|
86
|
+
print(Style.RESET_ALL)
|
|
87
|
+
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
def show_bom_item_status(self, bom: Bom, all: bool = False) -> None:
|
|
91
|
+
for component in bom.components:
|
|
92
|
+
release = None
|
|
93
|
+
id = CycloneDxSupport.get_property_value(component, CycloneDxSupport.CDX_PROP_SW360ID)
|
|
94
|
+
if id:
|
|
95
|
+
release = self._find_by_id(component)
|
|
96
|
+
else:
|
|
97
|
+
release = self._find_by_name(component)
|
|
98
|
+
|
|
99
|
+
if release:
|
|
100
|
+
if not all:
|
|
101
|
+
cs = release.get("clearingState", "(unknown clearing state)")
|
|
102
|
+
color = Fore.WHITE
|
|
103
|
+
if cs == "APPROVED":
|
|
104
|
+
color = Fore.LIGHTGREEN_EX
|
|
105
|
+
|
|
106
|
+
print(
|
|
107
|
+
color +
|
|
108
|
+
" " + component.name + ", " + component.version +
|
|
109
|
+
" => " + cs + ", " +
|
|
110
|
+
release.get("mainlineState", "(unknown mainline state)") +
|
|
111
|
+
Style.RESET_ALL)
|
|
112
|
+
continue
|
|
113
|
+
|
|
114
|
+
comp_sw360 = self.client.get_component(
|
|
115
|
+
self.client.get_id_from_href(
|
|
116
|
+
release["_links"]["sw360:component"]["href"]
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
rel_list = comp_sw360["_embedded"]["sw360:releases"]
|
|
121
|
+
print(" " + component.name + ", " + component.version + " => ", end="", flush=True)
|
|
122
|
+
print("releases for component found = " + str(len(rel_list)))
|
|
123
|
+
for orel in rel_list:
|
|
124
|
+
href = orel["_links"]["self"]["href"]
|
|
125
|
+
rel = self.client.get_release_by_url(href)
|
|
126
|
+
cs = rel.get("clearingState", "(unkown clearing state)")
|
|
127
|
+
if cs == "APPROVED":
|
|
128
|
+
print(Fore.LIGHTGREEN_EX, end="", flush=True)
|
|
129
|
+
print(
|
|
130
|
+
" " + orel["version"] + ", " + cs + ", " +
|
|
131
|
+
rel.get("mainlineState", "(unknown mainline state)"))
|
|
132
|
+
print(Style.RESET_ALL, end="", flush=True)
|
|
133
|
+
|
|
134
|
+
print("")
|
|
135
|
+
continue
|
|
136
|
+
|
|
137
|
+
if not id:
|
|
138
|
+
print_red(
|
|
139
|
+
" " + component.name + ", " + component.version +
|
|
140
|
+
" => --- no id ---")
|
|
141
|
+
continue
|
|
142
|
+
|
|
143
|
+
def run(self, args) -> None:
|
|
144
|
+
"""Main method()"""
|
|
145
|
+
if args.debug:
|
|
146
|
+
global LOG
|
|
147
|
+
LOG = capycli.get_logger(__name__)
|
|
148
|
+
else:
|
|
149
|
+
# suppress (debug) log output from requests and urllib
|
|
150
|
+
logging.getLogger("requests").setLevel(logging.WARNING)
|
|
151
|
+
logging.getLogger("urllib3").setLevel(logging.WARNING)
|
|
152
|
+
logging.getLogger("urllib3.connectionpool").setLevel(logging.WARNING)
|
|
153
|
+
|
|
154
|
+
print_text(
|
|
155
|
+
"\n" + capycli.APP_NAME + ", " + capycli.get_app_version()
|
|
156
|
+
+ " - check the status of the items on SW360\n")
|
|
157
|
+
|
|
158
|
+
if args.help:
|
|
159
|
+
print("usage: capycli bom CheckItemStatus [-h] [-all] -i bomfile")
|
|
160
|
+
print("")
|
|
161
|
+
print("optional arguments:")
|
|
162
|
+
print("-h, --help show this help message and exit")
|
|
163
|
+
print("-i INPUTFILE input file to read from")
|
|
164
|
+
print("-all show status of all versions of the component")
|
|
165
|
+
return
|
|
166
|
+
|
|
167
|
+
if not args.inputfile:
|
|
168
|
+
print_red("No input file specified!")
|
|
169
|
+
sys.exit(ResultCode.RESULT_COMMAND_ERROR)
|
|
170
|
+
|
|
171
|
+
if not os.path.isfile(args.inputfile):
|
|
172
|
+
print_red("Input file not found!")
|
|
173
|
+
sys.exit(ResultCode.RESULT_FILE_NOT_FOUND)
|
|
174
|
+
|
|
175
|
+
print("Loading SBOM file", args.inputfile)
|
|
176
|
+
try:
|
|
177
|
+
bom = CaPyCliBom.read_sbom(args.inputfile)
|
|
178
|
+
except Exception as ex:
|
|
179
|
+
print_red("Error reading SBOM: " + repr(ex))
|
|
180
|
+
sys.exit(ResultCode.RESULT_ERROR_READING_BOM)
|
|
181
|
+
|
|
182
|
+
if args.verbose:
|
|
183
|
+
print_text(" ", self.get_comp_count_text(bom), " read from SBOM")
|
|
184
|
+
|
|
185
|
+
if self._bom_has_items_without_id(bom):
|
|
186
|
+
print("There are SBOM items without Sw360 id - searching per name may take a little bit longer...")
|
|
187
|
+
|
|
188
|
+
if args.sw360_token and args.oauth2:
|
|
189
|
+
self.analyze_token(args.sw360_token)
|
|
190
|
+
|
|
191
|
+
if not self.login(token=args.sw360_token, url=args.sw360_url, oauth2=args.oauth2):
|
|
192
|
+
print_red("ERROR: login failed!")
|
|
193
|
+
sys.exit(ResultCode.RESULT_AUTH_ERROR)
|
|
194
|
+
|
|
195
|
+
self.show_bom_item_status(bom, args.all)
|
|
196
|
+
|
|
197
|
+
print()
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) 2021-2023 Siemens
|
|
3
|
+
# All Rights Reserved.
|
|
4
|
+
# Author: thomas.graf@siemens.com
|
|
5
|
+
#
|
|
6
|
+
# SPDX-License-Identifier: MIT
|
|
7
|
+
# -------------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
import importlib.resources
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
|
|
13
|
+
from cyclonedx.model import ExternalReferenceType
|
|
14
|
+
from cyclonedx.model.bom import Bom
|
|
15
|
+
from cyclonedx.model.component import Component
|
|
16
|
+
from packageurl import PackageURL
|
|
17
|
+
from sortedcontainers import SortedSet
|
|
18
|
+
|
|
19
|
+
import capycli.common.json_support
|
|
20
|
+
import capycli.common.script_base
|
|
21
|
+
from capycli.common.capycli_bom_support import CaPyCliBom, CycloneDxSupport, SbomWriter
|
|
22
|
+
from capycli.common.print import print_red, print_text, print_yellow
|
|
23
|
+
from capycli.dependencies.javascript import GetJavascriptDependencies
|
|
24
|
+
from capycli.main.result_codes import ResultCode
|
|
25
|
+
|
|
26
|
+
LOG = capycli.get_logger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class PotentialGranularityIssue:
|
|
30
|
+
"""Class to hold potential granularity issues."""
|
|
31
|
+
def __init__(self, component, replacement, comment="", source_url=""):
|
|
32
|
+
self.component = component
|
|
33
|
+
self.replacement = replacement
|
|
34
|
+
self.comment = comment
|
|
35
|
+
self.source_url = source_url
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class CheckGranularity(capycli.common.script_base.ScriptBase):
|
|
39
|
+
"""
|
|
40
|
+
Check the granularity of all releases in the SBOM.
|
|
41
|
+
"""
|
|
42
|
+
def __init__(self):
|
|
43
|
+
self.granularity_list = []
|
|
44
|
+
|
|
45
|
+
def read_granularity_list(self) -> None:
|
|
46
|
+
"""Reads the granularity list from file."""
|
|
47
|
+
self.granularity_list = []
|
|
48
|
+
|
|
49
|
+
# read CSV from data resource
|
|
50
|
+
resources = importlib.resources.files("capycli.data")
|
|
51
|
+
text_list = (resources / "granularity_list.csv").read_text()
|
|
52
|
+
for line in text_list.splitlines():
|
|
53
|
+
# ignore header (first) line
|
|
54
|
+
if line.startswith("component_name;replacement_name"):
|
|
55
|
+
continue
|
|
56
|
+
|
|
57
|
+
# ignore comments
|
|
58
|
+
if line.startswith("#"):
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
line = line.strip()
|
|
62
|
+
if not line:
|
|
63
|
+
continue
|
|
64
|
+
|
|
65
|
+
# split line
|
|
66
|
+
parts = line.split(";")
|
|
67
|
+
if len(parts) < 2:
|
|
68
|
+
continue
|
|
69
|
+
|
|
70
|
+
component = parts[0]
|
|
71
|
+
replacement = parts[1]
|
|
72
|
+
comment = ""
|
|
73
|
+
if len(parts) > 2:
|
|
74
|
+
comment = parts[2]
|
|
75
|
+
|
|
76
|
+
source_url = ""
|
|
77
|
+
if len(parts) > 3:
|
|
78
|
+
source_url = parts[3]
|
|
79
|
+
|
|
80
|
+
issue = PotentialGranularityIssue(component, replacement, comment, source_url)
|
|
81
|
+
self.granularity_list.append(issue)
|
|
82
|
+
|
|
83
|
+
def find_match(self, name: str) -> PotentialGranularityIssue or None:
|
|
84
|
+
"""Finds a match by component name."""
|
|
85
|
+
for match in self.granularity_list:
|
|
86
|
+
if match.component.lower() == name.lower():
|
|
87
|
+
return match
|
|
88
|
+
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
def get_new_fixed_component(self, component: Component, new_name: str, new_src_url: str) -> Component:
|
|
92
|
+
"""Get a !NEW! CycloneDX component to replace the old one."""
|
|
93
|
+
source_url_bak = CycloneDxSupport.get_ext_ref_source_url(component)
|
|
94
|
+
if new_src_url:
|
|
95
|
+
source_url_bak = new_src_url
|
|
96
|
+
language_bak = CycloneDxSupport.get_property(component, CycloneDxSupport.CDX_PROP_LANGUAGE)
|
|
97
|
+
|
|
98
|
+
# build new package-url
|
|
99
|
+
purl = ""
|
|
100
|
+
if component.purl:
|
|
101
|
+
old_purl = PackageURL.from_string(component.purl)
|
|
102
|
+
purl = PackageURL(old_purl.type, old_purl.namespace, new_name, component.version).to_string()
|
|
103
|
+
|
|
104
|
+
if self.search_meta_data:
|
|
105
|
+
if str(component.purl).startswith("pkg:npm"):
|
|
106
|
+
GetJavascriptDependencies().try_find_component_metadata(component, "")
|
|
107
|
+
else:
|
|
108
|
+
LOG.warning(" No package-url available - creating default purl")
|
|
109
|
+
purl = PackageURL("generic", "", new_name, component.version).to_string()
|
|
110
|
+
|
|
111
|
+
# create new component (this is the only way to set a new bom_ref)
|
|
112
|
+
component_new = Component(
|
|
113
|
+
name=new_name,
|
|
114
|
+
version=component.version,
|
|
115
|
+
purl=purl,
|
|
116
|
+
bom_ref=purl
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# restore properties we can keep
|
|
120
|
+
if source_url_bak:
|
|
121
|
+
CycloneDxSupport.update_or_set_ext_ref(
|
|
122
|
+
component_new,
|
|
123
|
+
ExternalReferenceType.DISTRIBUTION,
|
|
124
|
+
CaPyCliBom.SOURCE_URL_COMMENT,
|
|
125
|
+
source_url_bak
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
if language_bak:
|
|
129
|
+
component_new.properties.add(language_bak)
|
|
130
|
+
|
|
131
|
+
if component.purl and self.search_meta_data:
|
|
132
|
+
if str(component.purl).startswith("pkg:npm"):
|
|
133
|
+
component_new = GetJavascriptDependencies().try_find_component_metadata(component_new, "")
|
|
134
|
+
|
|
135
|
+
return component_new
|
|
136
|
+
|
|
137
|
+
def merge_duplicates(self, clist: list[Component]) -> list[Component]:
|
|
138
|
+
"""Checks for each release if there are duplicates after granularity check."""
|
|
139
|
+
new_list = []
|
|
140
|
+
for release in clist:
|
|
141
|
+
count = len([item for item in new_list if item.name == release.name
|
|
142
|
+
and item.version == release.version])
|
|
143
|
+
if count > 0:
|
|
144
|
+
continue
|
|
145
|
+
else:
|
|
146
|
+
new_list.append(release)
|
|
147
|
+
|
|
148
|
+
print()
|
|
149
|
+
print_text(str(len(clist) - len(new_list)) + " items can be reduced by granularity check")
|
|
150
|
+
|
|
151
|
+
return new_list
|
|
152
|
+
|
|
153
|
+
def check_bom_items(self, sbom: Bom):
|
|
154
|
+
"""Checks for each release in the list whether it can be found on the specified
|
|
155
|
+
SW360 instance."""
|
|
156
|
+
|
|
157
|
+
new_comp_list = []
|
|
158
|
+
for component in sbom.components:
|
|
159
|
+
match = self.find_match(component.name)
|
|
160
|
+
if not match:
|
|
161
|
+
new_comp_list.append(component)
|
|
162
|
+
continue
|
|
163
|
+
|
|
164
|
+
print_yellow(
|
|
165
|
+
component.name + ", " +
|
|
166
|
+
component.version + " should get replaced by " +
|
|
167
|
+
match.replacement)
|
|
168
|
+
|
|
169
|
+
new_component = self.get_new_fixed_component(
|
|
170
|
+
component,
|
|
171
|
+
match.replacement,
|
|
172
|
+
match.source_url)
|
|
173
|
+
|
|
174
|
+
new_comp_list.append(new_component)
|
|
175
|
+
|
|
176
|
+
reduced = self.merge_duplicates(new_comp_list)
|
|
177
|
+
sbom.components = SortedSet(reduced)
|
|
178
|
+
return sbom
|
|
179
|
+
|
|
180
|
+
def run(self, args):
|
|
181
|
+
"""Main method()"""
|
|
182
|
+
if args.debug:
|
|
183
|
+
global LOG
|
|
184
|
+
LOG = capycli.get_logger(__name__)
|
|
185
|
+
|
|
186
|
+
print_text(
|
|
187
|
+
"\n" + capycli.APP_NAME + ", " + capycli.get_app_version() +
|
|
188
|
+
" - Check the granularity of all releases in the SBOM.\n")
|
|
189
|
+
|
|
190
|
+
if args.help:
|
|
191
|
+
print("usage: CaPyCli bom granularity [-h] [-v] -i bomfile -o updated")
|
|
192
|
+
print("")
|
|
193
|
+
print("optional arguments:")
|
|
194
|
+
print(" -h, --help show this help message and exit")
|
|
195
|
+
print(" -i INPUTFILE SBOM file to read from (JSON)")
|
|
196
|
+
print(" -o OUTPUTFILE write updated to this file (optinal)")
|
|
197
|
+
print(" -v be verbose")
|
|
198
|
+
return
|
|
199
|
+
|
|
200
|
+
if not args.inputfile:
|
|
201
|
+
print_red("No input file specified!")
|
|
202
|
+
sys.exit(ResultCode.RESULT_COMMAND_ERROR)
|
|
203
|
+
|
|
204
|
+
if not os.path.isfile(args.inputfile):
|
|
205
|
+
print_red("Input file not found!")
|
|
206
|
+
sys.exit(ResultCode.RESULT_FILE_NOT_FOUND)
|
|
207
|
+
|
|
208
|
+
print_text("Reading granularity data from granularity_list.csv...")
|
|
209
|
+
try:
|
|
210
|
+
self.read_granularity_list()
|
|
211
|
+
except Exception as ex:
|
|
212
|
+
print_red("Error reading granularity data: " + repr(ex))
|
|
213
|
+
sys.exit(ResultCode.RESULT_GENERAL_ERROR)
|
|
214
|
+
print(" " + str(len(self.granularity_list)) + " items read.")
|
|
215
|
+
|
|
216
|
+
print_text("\nLoading SBOM file", args.inputfile)
|
|
217
|
+
try:
|
|
218
|
+
sbom = CaPyCliBom.read_sbom(args.inputfile)
|
|
219
|
+
except Exception as ex:
|
|
220
|
+
print_red("Error reading SBOM: " + repr(ex))
|
|
221
|
+
sys.exit(ResultCode.RESULT_ERROR_READING_BOM)
|
|
222
|
+
|
|
223
|
+
if args.verbose:
|
|
224
|
+
print_text(" ", self.get_comp_count_text(sbom), " read from SBOM")
|
|
225
|
+
|
|
226
|
+
self.search_meta_data = args.search_meta_data
|
|
227
|
+
|
|
228
|
+
new_sbom = self.check_bom_items(sbom)
|
|
229
|
+
|
|
230
|
+
print()
|
|
231
|
+
if args.outputfile:
|
|
232
|
+
print_text("Writing new SBOM to " + args.outputfile)
|
|
233
|
+
|
|
234
|
+
try:
|
|
235
|
+
SbomWriter.write_to_json(new_sbom, args.outputfile, True)
|
|
236
|
+
except Exception as ex:
|
|
237
|
+
print_red("Error writing new SBOM: " + repr(ex))
|
|
238
|
+
sys.exit(ResultCode.RESULT_ERROR_WRITING_BOM)
|
|
239
|
+
|
|
240
|
+
print_text(" " + self.get_comp_count_text(new_sbom) + " written to file " + args.outputfile)
|
|
241
|
+
else:
|
|
242
|
+
print_text("To get updated SBOM file - use the '-o <filename>' parameter")
|
|
243
|
+
|
|
244
|
+
print_text("\nDone.")
|