scanoss 1.12.2__py3-none-any.whl → 1.43.1__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.
- protoc_gen_swagger/__init__.py +13 -13
- protoc_gen_swagger/options/__init__.py +13 -13
- protoc_gen_swagger/options/annotations_pb2.py +18 -12
- protoc_gen_swagger/options/annotations_pb2.pyi +48 -0
- protoc_gen_swagger/options/annotations_pb2_grpc.py +20 -0
- protoc_gen_swagger/options/openapiv2_pb2.py +110 -99
- protoc_gen_swagger/options/openapiv2_pb2.pyi +1317 -0
- protoc_gen_swagger/options/openapiv2_pb2_grpc.py +20 -0
- scanoss/__init__.py +18 -18
- scanoss/api/__init__.py +17 -17
- scanoss/api/common/__init__.py +17 -17
- scanoss/api/common/v2/__init__.py +17 -17
- scanoss/api/common/v2/scanoss_common_pb2.py +49 -20
- scanoss/api/common/v2/scanoss_common_pb2_grpc.py +25 -0
- scanoss/api/components/__init__.py +17 -17
- scanoss/api/components/v2/__init__.py +17 -17
- scanoss/api/components/v2/scanoss_components_pb2.py +68 -43
- scanoss/api/components/v2/scanoss_components_pb2_grpc.py +83 -22
- scanoss/api/cryptography/v2/scanoss_cryptography_pb2.py +136 -21
- scanoss/api/cryptography/v2/scanoss_cryptography_pb2_grpc.py +766 -13
- scanoss/api/dependencies/__init__.py +17 -17
- scanoss/api/dependencies/v2/__init__.py +17 -17
- scanoss/api/dependencies/v2/scanoss_dependencies_pb2.py +56 -29
- scanoss/api/dependencies/v2/scanoss_dependencies_pb2_grpc.py +94 -8
- scanoss/api/geoprovenance/__init__.py +23 -0
- scanoss/api/geoprovenance/v2/__init__.py +23 -0
- scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2.py +92 -0
- scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2_grpc.py +381 -0
- scanoss/api/licenses/__init__.py +23 -0
- scanoss/api/licenses/v2/__init__.py +23 -0
- scanoss/api/licenses/v2/scanoss_licenses_pb2.py +84 -0
- scanoss/api/licenses/v2/scanoss_licenses_pb2_grpc.py +302 -0
- scanoss/api/scanning/__init__.py +17 -17
- scanoss/api/scanning/v2/__init__.py +17 -17
- scanoss/api/scanning/v2/scanoss_scanning_pb2.py +42 -13
- scanoss/api/scanning/v2/scanoss_scanning_pb2_grpc.py +86 -7
- scanoss/api/semgrep/__init__.py +17 -17
- scanoss/api/semgrep/v2/__init__.py +17 -17
- scanoss/api/semgrep/v2/scanoss_semgrep_pb2.py +50 -23
- scanoss/api/semgrep/v2/scanoss_semgrep_pb2_grpc.py +151 -16
- scanoss/api/vulnerabilities/__init__.py +17 -17
- scanoss/api/vulnerabilities/v2/__init__.py +17 -17
- scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2.py +78 -31
- scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2_grpc.py +282 -18
- scanoss/cli.py +2359 -370
- scanoss/components.py +187 -94
- scanoss/constants.py +22 -0
- scanoss/cryptography.py +308 -0
- scanoss/csvoutput.py +91 -58
- scanoss/cyclonedx.py +221 -63
- scanoss/data/build_date.txt +1 -1
- scanoss/data/osadl-copyleft.json +133 -0
- scanoss/data/scanoss-settings-schema.json +254 -0
- scanoss/delta.py +197 -0
- scanoss/export/__init__.py +23 -0
- scanoss/export/dependency_track.py +227 -0
- scanoss/file_filters.py +582 -0
- scanoss/filecount.py +75 -69
- scanoss/gitlabqualityreport.py +214 -0
- scanoss/header_filter.py +563 -0
- scanoss/inspection/__init__.py +23 -0
- scanoss/inspection/policy_check/__init__.py +0 -0
- scanoss/inspection/policy_check/dependency_track/__init__.py +0 -0
- scanoss/inspection/policy_check/dependency_track/project_violation.py +479 -0
- scanoss/inspection/policy_check/policy_check.py +222 -0
- scanoss/inspection/policy_check/scanoss/__init__.py +0 -0
- scanoss/inspection/policy_check/scanoss/copyleft.py +243 -0
- scanoss/inspection/policy_check/scanoss/undeclared_component.py +309 -0
- scanoss/inspection/summary/__init__.py +0 -0
- scanoss/inspection/summary/component_summary.py +170 -0
- scanoss/inspection/summary/license_summary.py +191 -0
- scanoss/inspection/summary/match_summary.py +341 -0
- scanoss/inspection/utils/file_utils.py +44 -0
- scanoss/inspection/utils/license_utils.py +123 -0
- scanoss/inspection/utils/markdown_utils.py +63 -0
- scanoss/inspection/utils/scan_result_processor.py +417 -0
- scanoss/osadl.py +125 -0
- scanoss/results.py +275 -0
- scanoss/scancodedeps.py +87 -38
- scanoss/scanner.py +431 -539
- scanoss/scanners/__init__.py +23 -0
- scanoss/scanners/container_scanner.py +476 -0
- scanoss/scanners/folder_hasher.py +358 -0
- scanoss/scanners/scanner_config.py +73 -0
- scanoss/scanners/scanner_hfh.py +252 -0
- scanoss/scanoss_settings.py +337 -0
- scanoss/scanossapi.py +140 -101
- scanoss/scanossbase.py +59 -22
- scanoss/scanossgrpc.py +799 -251
- scanoss/scanpostprocessor.py +294 -0
- scanoss/scantype.py +22 -21
- scanoss/services/dependency_track_service.py +132 -0
- scanoss/spdxlite.py +532 -174
- scanoss/threadeddependencies.py +148 -47
- scanoss/threadedscanning.py +53 -37
- scanoss/utils/__init__.py +23 -0
- scanoss/utils/abstract_presenter.py +103 -0
- scanoss/utils/crc64.py +96 -0
- scanoss/utils/file.py +84 -0
- scanoss/utils/scanoss_scan_results_utils.py +41 -0
- scanoss/utils/simhash.py +198 -0
- scanoss/winnowing.py +241 -63
- {scanoss-1.12.2.dist-info → scanoss-1.43.1.dist-info}/METADATA +18 -9
- scanoss-1.43.1.dist-info/RECORD +110 -0
- {scanoss-1.12.2.dist-info → scanoss-1.43.1.dist-info}/WHEEL +1 -1
- scanoss-1.12.2.dist-info/RECORD +0 -58
- {scanoss-1.12.2.dist-info → scanoss-1.43.1.dist-info}/entry_points.txt +0 -0
- {scanoss-1.12.2.dist-info → scanoss-1.43.1.dist-info/licenses}/LICENSE +0 -0
- {scanoss-1.12.2.dist-info → scanoss-1.43.1.dist-info}/top_level.txt +0 -0
scanoss/cyclonedx.py
CHANGED
|
@@ -1,31 +1,37 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
2
|
+
SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2021, SCANOSS
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
|
14
|
+
all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
22
|
+
THE SOFTWARE.
|
|
23
23
|
"""
|
|
24
|
+
|
|
25
|
+
import datetime
|
|
24
26
|
import json
|
|
25
27
|
import os.path
|
|
26
28
|
import sys
|
|
27
29
|
import uuid
|
|
28
30
|
|
|
31
|
+
from cyclonedx.schema import SchemaVersion
|
|
32
|
+
from cyclonedx.validation.json import JsonValidator
|
|
33
|
+
|
|
34
|
+
from . import __version__
|
|
29
35
|
from .scanossbase import ScanossBase
|
|
30
36
|
from .spdxlite import SpdxLite
|
|
31
37
|
|
|
@@ -45,33 +51,36 @@ class CycloneDx(ScanossBase):
|
|
|
45
51
|
self.debug = debug
|
|
46
52
|
self._spdx = SpdxLite(debug=debug)
|
|
47
53
|
|
|
48
|
-
def parse(self, data:
|
|
54
|
+
def parse(self, data: dict): # noqa: PLR0912, PLR0915
|
|
49
55
|
"""
|
|
50
56
|
Parse the given input (raw/plain) JSON string and return CycloneDX summary
|
|
51
|
-
:param data:
|
|
57
|
+
:param data: dict - JSON object
|
|
52
58
|
:return: CycloneDX dictionary, and vulnerability dictionary
|
|
53
59
|
"""
|
|
54
|
-
if
|
|
60
|
+
if data is None:
|
|
55
61
|
self.print_stderr('ERROR: No JSON data provided to parse.')
|
|
56
62
|
return None, None
|
|
57
|
-
|
|
63
|
+
if len(data) == 0:
|
|
64
|
+
self.print_msg('Warning: Empty scan results provided. Returning empty component dictionary.')
|
|
65
|
+
return {}, {}
|
|
66
|
+
self.print_debug('Processing raw results into CycloneDX format...')
|
|
58
67
|
cdx = {}
|
|
59
68
|
vdx = {}
|
|
60
69
|
for f in data:
|
|
61
70
|
file_details = data.get(f)
|
|
62
71
|
# print(f'File: {f}: {file_details}')
|
|
63
72
|
for d in file_details:
|
|
64
|
-
id_details = d.get(
|
|
73
|
+
id_details = d.get('id')
|
|
65
74
|
if not id_details or id_details == 'none':
|
|
66
75
|
continue
|
|
67
76
|
purl = None
|
|
68
77
|
if id_details == 'dependency':
|
|
69
|
-
dependencies = d.get(
|
|
78
|
+
dependencies = d.get('dependencies')
|
|
70
79
|
if not dependencies:
|
|
71
80
|
self.print_stderr(f'Warning: No Dependencies found for {f}: {file_details}')
|
|
72
81
|
continue
|
|
73
82
|
for deps in dependencies:
|
|
74
|
-
purl = deps.get(
|
|
83
|
+
purl = deps.get('purl')
|
|
75
84
|
if not purl:
|
|
76
85
|
self.print_stderr(f'Warning: No PURL found for {f}: {deps}')
|
|
77
86
|
continue
|
|
@@ -83,12 +92,13 @@ class CycloneDx(ScanossBase):
|
|
|
83
92
|
fd[field] = deps.get(field, '')
|
|
84
93
|
licenses = deps.get('licenses')
|
|
85
94
|
fdl = []
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
95
|
+
if licenses:
|
|
96
|
+
dc = []
|
|
97
|
+
for lic in licenses:
|
|
98
|
+
name = lic.get('name')
|
|
99
|
+
if name not in dc: # Only save the license name once
|
|
100
|
+
fdl.append({'id': name})
|
|
101
|
+
dc.append(name)
|
|
92
102
|
fd['licenses'] = fdl
|
|
93
103
|
cdx[purl] = fd
|
|
94
104
|
else:
|
|
@@ -104,30 +114,33 @@ class CycloneDx(ScanossBase):
|
|
|
104
114
|
self.print_stderr(f'Warning: No PURL found for {f}: {file_details}')
|
|
105
115
|
continue
|
|
106
116
|
fd = {}
|
|
107
|
-
vulnerabilities = d.get(
|
|
117
|
+
vulnerabilities = d.get('vulnerabilities')
|
|
108
118
|
if vulnerabilities:
|
|
109
119
|
for vuln in vulnerabilities:
|
|
110
|
-
vuln_id = vuln.get(
|
|
120
|
+
vuln_id = vuln.get('ID')
|
|
111
121
|
if vuln_id == '':
|
|
112
|
-
vuln_id = vuln.get(
|
|
122
|
+
vuln_id = vuln.get('id')
|
|
113
123
|
if not vuln_id or vuln_id == '': # Skip empty ids
|
|
114
124
|
continue
|
|
115
|
-
vuln_cve = vuln.get(
|
|
125
|
+
vuln_cve = vuln.get('CVE', '')
|
|
116
126
|
if vuln_cve == '':
|
|
117
|
-
vuln_cve = vuln.get(
|
|
118
|
-
if vuln_id.upper().startswith(
|
|
119
|
-
fd['cpe'] = vuln_id
|
|
127
|
+
vuln_cve = vuln.get('cve', '')
|
|
128
|
+
if vuln_id.upper().startswith('CPE:'):
|
|
129
|
+
fd['cpe'] = vuln_id # Save the component CPE if we have one
|
|
120
130
|
if vuln_cve != '':
|
|
121
131
|
vuln_id = vuln_cve
|
|
122
132
|
vd = vdx.get(vuln_id) # Check if we've already encountered this vulnerability
|
|
123
133
|
if not vd:
|
|
124
134
|
vuln_source = vuln.get('source', '').lower()
|
|
125
|
-
vd = {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
135
|
+
vd = {
|
|
136
|
+
'cve': vuln_cve,
|
|
137
|
+
'source': 'NVD' if vuln_source == 'nvd' else 'GitHub Advisories',
|
|
138
|
+
'url': f'https://nvd.nist.gov/vuln/detail/{vuln_cve}'
|
|
139
|
+
if vuln_source == 'nvd'
|
|
140
|
+
else f'https://github.com/advisories/{vuln_id}',
|
|
141
|
+
'severity': self._sev_lookup(vuln.get('severity', 'unknown').lower()),
|
|
142
|
+
'affects': set(),
|
|
143
|
+
}
|
|
131
144
|
vd.get('affects').add(purl)
|
|
132
145
|
vdx[vuln_id] = vd
|
|
133
146
|
if cdx.get(purl):
|
|
@@ -137,8 +150,13 @@ class CycloneDx(ScanossBase):
|
|
|
137
150
|
fd[field] = d.get(field)
|
|
138
151
|
licenses = d.get('licenses')
|
|
139
152
|
fdl = []
|
|
140
|
-
|
|
141
|
-
|
|
153
|
+
if licenses:
|
|
154
|
+
for lic in licenses:
|
|
155
|
+
name = lic.get('name')
|
|
156
|
+
source = lic.get('source')
|
|
157
|
+
if source not in ('component_declared', 'license_file', 'file_header'):
|
|
158
|
+
continue
|
|
159
|
+
fdl.append({'id': name})
|
|
142
160
|
fd['licenses'] = fdl
|
|
143
161
|
cdx[purl] = fd
|
|
144
162
|
# self.print_stderr(f'VD: {vdx}')
|
|
@@ -162,17 +180,24 @@ class CycloneDx(ScanossBase):
|
|
|
162
180
|
success = self.produce_from_str(f.read(), output_file)
|
|
163
181
|
return success
|
|
164
182
|
|
|
165
|
-
def produce_from_json(self, data:
|
|
183
|
+
def produce_from_json(self, data: dict, output_file: str = None) -> tuple[bool, dict]: # noqa: PLR0912
|
|
166
184
|
"""
|
|
167
|
-
Produce the CycloneDX output from the input data
|
|
168
|
-
|
|
169
|
-
:
|
|
170
|
-
|
|
185
|
+
Produce the CycloneDX output from the raw scan results input data
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
data (dict): JSON object
|
|
189
|
+
output_file (str, optional): Output file (optional). Defaults to None.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
bool: True if successful, False otherwise
|
|
193
|
+
json: The CycloneDX output
|
|
171
194
|
"""
|
|
172
195
|
cdx, vdx = self.parse(data)
|
|
173
|
-
if
|
|
196
|
+
if cdx is None:
|
|
174
197
|
self.print_stderr('ERROR: No CycloneDX data returned for the JSON string provided.')
|
|
175
|
-
return False
|
|
198
|
+
return False, {}
|
|
199
|
+
if len(cdx) == 0:
|
|
200
|
+
self.print_msg('Warning: Empty scan results - generating minimal CycloneDX SBOM with no components.')
|
|
176
201
|
self._spdx.load_license_data() # Load SPDX license name data for later reference
|
|
177
202
|
#
|
|
178
203
|
# Using CDX version 1.4: https://cyclonedx.org/docs/1.4/json/
|
|
@@ -184,8 +209,19 @@ class CycloneDx(ScanossBase):
|
|
|
184
209
|
'specVersion': '1.4',
|
|
185
210
|
'serialNumber': f'urn:uuid:{uuid.uuid4()}',
|
|
186
211
|
'version': 1,
|
|
212
|
+
'metadata': {
|
|
213
|
+
'timestamp': datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ'),
|
|
214
|
+
'tools': [
|
|
215
|
+
{
|
|
216
|
+
'vendor': 'SCANOSS',
|
|
217
|
+
'name': 'scanoss-py',
|
|
218
|
+
'version': __version__,
|
|
219
|
+
}
|
|
220
|
+
],
|
|
221
|
+
'component': {'type': 'application', 'name': 'NOASSERTION', 'version': 'NOASSERTION'},
|
|
222
|
+
},
|
|
187
223
|
'components': [],
|
|
188
|
-
'vulnerabilities': []
|
|
224
|
+
'vulnerabilities': [],
|
|
189
225
|
}
|
|
190
226
|
for purl in cdx:
|
|
191
227
|
comp = cdx.get(purl)
|
|
@@ -195,6 +231,8 @@ class CycloneDx(ScanossBase):
|
|
|
195
231
|
lic_set = set()
|
|
196
232
|
for lic in licenses: # Get a unique set of licenses
|
|
197
233
|
lc_id = lic.get('id')
|
|
234
|
+
if not lc_id:
|
|
235
|
+
continue
|
|
198
236
|
spdx_id = self._spdx.get_spdx_license_id(lc_id)
|
|
199
237
|
lic_set.add(spdx_id if spdx_id else lc_id)
|
|
200
238
|
for lc_id in lic_set: # Store licenses for later inclusion
|
|
@@ -210,7 +248,7 @@ class CycloneDx(ScanossBase):
|
|
|
210
248
|
'version': comp.get('version'),
|
|
211
249
|
'purl': purl,
|
|
212
250
|
'bom-ref': purl,
|
|
213
|
-
'licenses': lic_text
|
|
251
|
+
'licenses': lic_text,
|
|
214
252
|
}
|
|
215
253
|
cpe = comp.get('cpe', '')
|
|
216
254
|
if cpe and cpe != '':
|
|
@@ -230,7 +268,7 @@ class CycloneDx(ScanossBase):
|
|
|
230
268
|
'id': vuln_id,
|
|
231
269
|
'source': {'name': v_source, 'url': vulns.get('url')},
|
|
232
270
|
'ratings': [{'severity': vulns.get('severity', 'unknown')}],
|
|
233
|
-
'affects': affects
|
|
271
|
+
'affects': affects,
|
|
234
272
|
}
|
|
235
273
|
data['vulnerabilities'].append(vd)
|
|
236
274
|
# End for loop
|
|
@@ -244,7 +282,7 @@ class CycloneDx(ScanossBase):
|
|
|
244
282
|
if output_file:
|
|
245
283
|
file.close()
|
|
246
284
|
|
|
247
|
-
return True
|
|
285
|
+
return True, data
|
|
248
286
|
|
|
249
287
|
def produce_from_str(self, json_str: str, output_file: str = None) -> bool:
|
|
250
288
|
"""
|
|
@@ -261,7 +299,84 @@ class CycloneDx(ScanossBase):
|
|
|
261
299
|
except Exception as e:
|
|
262
300
|
self.print_stderr(f'ERROR: Problem parsing input JSON: {e}')
|
|
263
301
|
return False
|
|
264
|
-
|
|
302
|
+
success, _ = self.produce_from_json(data, output_file)
|
|
303
|
+
return success
|
|
304
|
+
|
|
305
|
+
def _normalize_vulnerability_id(self, vuln: dict) -> tuple[str, str]:
|
|
306
|
+
"""
|
|
307
|
+
Normalize vulnerability ID and CVE from different possible field names.
|
|
308
|
+
Returns tuple of (vuln_id, vuln_cve).
|
|
309
|
+
"""
|
|
310
|
+
vuln_id = vuln.get('ID', '') or vuln.get('id', '')
|
|
311
|
+
vuln_cve = vuln.get('CVE', '') or vuln.get('cve', '')
|
|
312
|
+
|
|
313
|
+
# Skip CPE entries, use CVE if available
|
|
314
|
+
if vuln_id.upper().startswith('CPE:') and vuln_cve:
|
|
315
|
+
vuln_id = vuln_cve
|
|
316
|
+
|
|
317
|
+
return vuln_id, vuln_cve
|
|
318
|
+
|
|
319
|
+
def _create_vulnerability_entry(self, vuln_id: str, vuln: dict, vuln_cve: str, purl: str) -> dict:
|
|
320
|
+
"""
|
|
321
|
+
Create a new vulnerability entry for CycloneDX format.
|
|
322
|
+
"""
|
|
323
|
+
vuln_source = vuln.get('source', '').lower()
|
|
324
|
+
return {
|
|
325
|
+
'id': vuln_id,
|
|
326
|
+
'source': {
|
|
327
|
+
'name': 'NVD' if vuln_source == 'nvd' else 'GitHub Advisories',
|
|
328
|
+
'url': f'https://nvd.nist.gov/vuln/detail/{vuln_cve}'
|
|
329
|
+
if vuln_source == 'nvd'
|
|
330
|
+
else f'https://github.com/advisories/{vuln_id}',
|
|
331
|
+
},
|
|
332
|
+
'ratings': [{'severity': self._sev_lookup(vuln.get('severity', 'unknown').lower())}],
|
|
333
|
+
'affects': [{'ref': purl}],
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
def append_vulnerabilities(self, cdx_dict: dict, vulnerabilities_data: dict, purl: str) -> dict:
|
|
337
|
+
"""
|
|
338
|
+
Append vulnerabilities to an existing CycloneDX dictionary
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
cdx_dict (dict): The existing CycloneDX dictionary
|
|
342
|
+
vulnerabilities_data (dict): The vulnerabilities data from get_vulnerabilities_json
|
|
343
|
+
purl (str): The PURL of the component these vulnerabilities affect
|
|
344
|
+
|
|
345
|
+
Returns:
|
|
346
|
+
dict: The updated CycloneDX dictionary with vulnerabilities appended
|
|
347
|
+
"""
|
|
348
|
+
if not cdx_dict or not vulnerabilities_data:
|
|
349
|
+
return cdx_dict
|
|
350
|
+
|
|
351
|
+
if 'vulnerabilities' not in cdx_dict:
|
|
352
|
+
cdx_dict['vulnerabilities'] = []
|
|
353
|
+
|
|
354
|
+
# Extract vulnerabilities from the response
|
|
355
|
+
vulns_list = vulnerabilities_data.get('purls', [])
|
|
356
|
+
if not vulns_list:
|
|
357
|
+
return cdx_dict
|
|
358
|
+
|
|
359
|
+
vuln_items = vulns_list[0].get('vulnerabilities', [])
|
|
360
|
+
|
|
361
|
+
for vuln in vuln_items:
|
|
362
|
+
vuln_id, vuln_cve = self._normalize_vulnerability_id(vuln)
|
|
363
|
+
|
|
364
|
+
# Skip empty IDs or CPE-only entries
|
|
365
|
+
if not vuln_id or vuln_id.upper().startswith('CPE:'):
|
|
366
|
+
continue
|
|
367
|
+
|
|
368
|
+
# Check if vulnerability already exists
|
|
369
|
+
existing_vuln = next((v for v in cdx_dict['vulnerabilities'] if v.get('id') == vuln_id), None)
|
|
370
|
+
|
|
371
|
+
if existing_vuln:
|
|
372
|
+
# Add this PURL to the affects list if not already present
|
|
373
|
+
if not any(ref.get('ref') == purl for ref in existing_vuln.get('affects', [])):
|
|
374
|
+
existing_vuln['affects'].append({'ref': purl})
|
|
375
|
+
else:
|
|
376
|
+
# Create new vulnerability entry
|
|
377
|
+
cdx_dict['vulnerabilities'].append(self._create_vulnerability_entry(vuln_id, vuln, vuln_cve, purl))
|
|
378
|
+
|
|
379
|
+
return cdx_dict
|
|
265
380
|
|
|
266
381
|
@staticmethod
|
|
267
382
|
def _sev_lookup(value: str):
|
|
@@ -278,8 +393,51 @@ class CycloneDx(ScanossBase):
|
|
|
278
393
|
'low': 'low',
|
|
279
394
|
'info': 'info',
|
|
280
395
|
'none': 'none',
|
|
281
|
-
'unknown': 'unknown'
|
|
282
|
-
|
|
396
|
+
'unknown': 'unknown',
|
|
397
|
+
}.get(value, 'unknown')
|
|
398
|
+
|
|
399
|
+
def is_cyclonedx_json(self, json_string: str) -> bool:
|
|
400
|
+
"""
|
|
401
|
+
Validate if the given JSON string is a valid CycloneDX JSON string
|
|
402
|
+
|
|
403
|
+
Args:
|
|
404
|
+
json_string (str): JSON string to validate
|
|
405
|
+
Returns:
|
|
406
|
+
bool: True if the JSON string is valid, False otherwise
|
|
407
|
+
"""
|
|
408
|
+
try:
|
|
409
|
+
cdx_json_validator = JsonValidator(SchemaVersion.V1_6)
|
|
410
|
+
json_validation_errors = cdx_json_validator.validate_str(json_string)
|
|
411
|
+
if json_validation_errors:
|
|
412
|
+
self.print_stderr(f'ERROR: Problem parsing input JSON: {json_validation_errors}')
|
|
413
|
+
return False
|
|
414
|
+
return True
|
|
415
|
+
except Exception as e:
|
|
416
|
+
self.print_stderr(f'ERROR: Problem parsing input JSON: {e}')
|
|
417
|
+
return False
|
|
418
|
+
|
|
419
|
+
def get_purls_request_from_cdx(self, cdx_dict: dict, field: str = 'purls') -> dict:
|
|
420
|
+
"""
|
|
421
|
+
Get the list of PURL requests (purl + requirement) from the given CDX dictionary
|
|
422
|
+
|
|
423
|
+
Args:
|
|
424
|
+
cdx_dict (dict): CDX dictionary to parse
|
|
425
|
+
field (str): Field to extract from the CDX dictionary
|
|
426
|
+
Returns:
|
|
427
|
+
list[dict]: List of PURL requests (purl + requirement)
|
|
428
|
+
"""
|
|
429
|
+
components = cdx_dict.get('components', [])
|
|
430
|
+
parsed_purls = []
|
|
431
|
+
for component in components:
|
|
432
|
+
version = component.get('version')
|
|
433
|
+
if version:
|
|
434
|
+
parsed_purls.append({'purl': component.get('purl'), 'requirement': version})
|
|
435
|
+
else:
|
|
436
|
+
parsed_purls.append({'purl': component.get('purl')})
|
|
437
|
+
purl_request = {field: parsed_purls}
|
|
438
|
+
return purl_request
|
|
439
|
+
|
|
440
|
+
|
|
283
441
|
#
|
|
284
442
|
# End of CycloneDX Class
|
|
285
443
|
#
|
scanoss/data/build_date.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
date:
|
|
1
|
+
date: 20260105120224, utime: 1767614544
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "OSADL Open Source License Obligations Checklist (https:\/\/www.osadl.org\/Checklists)",
|
|
3
|
+
"license": "Creative Commons Attribution 4.0 International license (CC-BY-4.0)",
|
|
4
|
+
"attribution": "A project by the Open Source Automation Development Lab (OSADL) eG. For further information about the project see the description at www.osadl.org\/checklists.",
|
|
5
|
+
"copyright": "(C) 2017 - 2024 Open Source Automation Development Lab (OSADL) eG and contributors, info@osadl.org",
|
|
6
|
+
"disclaimer": "The checklists and particularly the copyleft data have been assembled with maximum diligence and care; however, the authors do not warrant nor can be held liable in any way for its correctness, usefulness, merchantibility or fitness for a particular purpose as far as permissible by applicable law. Anyone who uses the information does this on his or her sole responsibility. For any individual legal advice, it is recommended to contact a lawyer.",
|
|
7
|
+
"timeformat": "%Y-%m-%dT%H:%M:%S%z",
|
|
8
|
+
"timestamp": "2025-10-30T11:23:00+0000",
|
|
9
|
+
"copyleft":
|
|
10
|
+
{
|
|
11
|
+
"0BSD": "No",
|
|
12
|
+
"AFL-2.0": "No",
|
|
13
|
+
"AFL-2.1": "No",
|
|
14
|
+
"AFL-3.0": "No",
|
|
15
|
+
"AGPL-3.0-only": "Yes",
|
|
16
|
+
"AGPL-3.0-or-later": "Yes",
|
|
17
|
+
"Apache-1.0": "No",
|
|
18
|
+
"Apache-1.1": "No",
|
|
19
|
+
"Apache-2.0": "No",
|
|
20
|
+
"APSL-2.0": "Yes (restricted)",
|
|
21
|
+
"Artistic-1.0": "No",
|
|
22
|
+
"Artistic-1.0-Perl": "No",
|
|
23
|
+
"Artistic-2.0": "No",
|
|
24
|
+
"Bitstream-Vera": "No",
|
|
25
|
+
"blessing": "No",
|
|
26
|
+
"BlueOak-1.0.0": "No",
|
|
27
|
+
"BSD-1-Clause": "No",
|
|
28
|
+
"BSD-2-Clause": "No",
|
|
29
|
+
"BSD-2-Clause-Patent": "No",
|
|
30
|
+
"BSD-3-Clause": "No",
|
|
31
|
+
"BSD-3-Clause-Open-MPI": "No",
|
|
32
|
+
"BSD-4-Clause": "No",
|
|
33
|
+
"BSD-4-Clause-UC": "No",
|
|
34
|
+
"BSD-4.3TAHOE": "No",
|
|
35
|
+
"BSD-Source-Code": "No",
|
|
36
|
+
"BSL-1.0": "No",
|
|
37
|
+
"bzip2-1.0.5": "No",
|
|
38
|
+
"bzip2-1.0.6": "No",
|
|
39
|
+
"CC-BY-2.5": "No",
|
|
40
|
+
"CC-BY-3.0": "No",
|
|
41
|
+
"CDDL-1.0": "Yes (restricted)",
|
|
42
|
+
"CDDL-1.1": "Yes (restricted)",
|
|
43
|
+
"CPL-1.0": "Yes",
|
|
44
|
+
"curl": "No",
|
|
45
|
+
"ECL-1.0": "No",
|
|
46
|
+
"ECL-2.0": "No",
|
|
47
|
+
"EFL-2.0": "No",
|
|
48
|
+
"EPL-1.0": "Yes",
|
|
49
|
+
"EPL-2.0": "Yes (restricted)",
|
|
50
|
+
"EUPL-1.1": "Yes",
|
|
51
|
+
"EUPL-1.2": "Yes",
|
|
52
|
+
"FSFAP": "No",
|
|
53
|
+
"FSFUL": "No",
|
|
54
|
+
"FSFULLR": "No",
|
|
55
|
+
"FSFULLRWD": "No",
|
|
56
|
+
"FTL": "No",
|
|
57
|
+
"GPL-1.0-only": "Yes",
|
|
58
|
+
"GPL-1.0-or-later": "Yes",
|
|
59
|
+
"GPL-2.0-only": "Yes",
|
|
60
|
+
"GPL-2.0-only WITH Classpath-exception-2.0": "Yes (restricted)",
|
|
61
|
+
"GPL-2.0-or-later": "Yes",
|
|
62
|
+
"GPL-3.0-only": "Yes",
|
|
63
|
+
"GPL-3.0-or-later": "Yes",
|
|
64
|
+
"HPND": "No",
|
|
65
|
+
"IBM-pibs": "No",
|
|
66
|
+
"ICU": "No",
|
|
67
|
+
"IJG": "No",
|
|
68
|
+
"ImageMagick": "No",
|
|
69
|
+
"Info-ZIP": "No",
|
|
70
|
+
"IPL-1.0": "Yes",
|
|
71
|
+
"ISC": "No",
|
|
72
|
+
"JasPer-2.0": "No",
|
|
73
|
+
"LGPL-2.0-only": "Yes (restricted)",
|
|
74
|
+
"LGPL-2.0-or-later": "Yes (restricted)",
|
|
75
|
+
"LGPL-2.1-only": "Yes (restricted)",
|
|
76
|
+
"LGPL-2.1-or-later": "Yes (restricted)",
|
|
77
|
+
"LGPL-3.0-only": "Yes (restricted)",
|
|
78
|
+
"LGPL-3.0-or-later": "Yes (restricted)",
|
|
79
|
+
"Libpng": "No",
|
|
80
|
+
"libpng-2.0": "No",
|
|
81
|
+
"libtiff": "No",
|
|
82
|
+
"LicenseRef-scancode-bsla-no-advert": "No",
|
|
83
|
+
"LicenseRef-scancode-info-zip-2003-05": "No",
|
|
84
|
+
"LicenseRef-scancode-ppp": "No",
|
|
85
|
+
"Minpack": "No",
|
|
86
|
+
"MirOS": "No",
|
|
87
|
+
"MIT": "No",
|
|
88
|
+
"MIT-0": "No",
|
|
89
|
+
"MIT-CMU": "No",
|
|
90
|
+
"MPL-1.1": "Yes (restricted)",
|
|
91
|
+
"MPL-2.0": "Yes (restricted)",
|
|
92
|
+
"MPL-2.0-no-copyleft-exception": "Yes (restricted)",
|
|
93
|
+
"MS-PL": "Questionable",
|
|
94
|
+
"MS-RL": "Yes (restricted)",
|
|
95
|
+
"NBPL-1.0": "No",
|
|
96
|
+
"NCSA": "No",
|
|
97
|
+
"NTP": "No",
|
|
98
|
+
"OFL-1.1": "Yes (restricted)",
|
|
99
|
+
"OGC-1.0": "No",
|
|
100
|
+
"OLDAP-2.8": "No",
|
|
101
|
+
"OpenSSL": "Questionable",
|
|
102
|
+
"OSL-3.0": "Yes",
|
|
103
|
+
"PHP-3.01": "No",
|
|
104
|
+
"PostgreSQL": "No",
|
|
105
|
+
"PSF-2.0": "No",
|
|
106
|
+
"Python-2.0": "No",
|
|
107
|
+
"Qhull": "No",
|
|
108
|
+
"RSA-MD": "No",
|
|
109
|
+
"Saxpath": "No",
|
|
110
|
+
"SGI-B-2.0": "No",
|
|
111
|
+
"Sleepycat": "Yes",
|
|
112
|
+
"SMLNJ": "No",
|
|
113
|
+
"Spencer-86": "No",
|
|
114
|
+
"SSH-OpenSSH": "No",
|
|
115
|
+
"SSH-short": "No",
|
|
116
|
+
"SunPro": "No",
|
|
117
|
+
"Ubuntu-font-1.0": "Yes (restricted)",
|
|
118
|
+
"Unicode-3.0": "No",
|
|
119
|
+
"Unicode-DFS-2015": "No",
|
|
120
|
+
"Unicode-DFS-2016": "No",
|
|
121
|
+
"Unlicense": "No",
|
|
122
|
+
"UPL-1.0": "No",
|
|
123
|
+
"W3C": "No",
|
|
124
|
+
"W3C-19980720": "No",
|
|
125
|
+
"W3C-20150513": "No",
|
|
126
|
+
"WTFPL": "No",
|
|
127
|
+
"X11": "No",
|
|
128
|
+
"XFree86-1.1": "No",
|
|
129
|
+
"Zlib": "No",
|
|
130
|
+
"zlib-acknowledgement": "No",
|
|
131
|
+
"ZPL-2.0": "No"
|
|
132
|
+
}
|
|
133
|
+
}
|