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.
Files changed (109) hide show
  1. protoc_gen_swagger/__init__.py +13 -13
  2. protoc_gen_swagger/options/__init__.py +13 -13
  3. protoc_gen_swagger/options/annotations_pb2.py +18 -12
  4. protoc_gen_swagger/options/annotations_pb2.pyi +48 -0
  5. protoc_gen_swagger/options/annotations_pb2_grpc.py +20 -0
  6. protoc_gen_swagger/options/openapiv2_pb2.py +110 -99
  7. protoc_gen_swagger/options/openapiv2_pb2.pyi +1317 -0
  8. protoc_gen_swagger/options/openapiv2_pb2_grpc.py +20 -0
  9. scanoss/__init__.py +18 -18
  10. scanoss/api/__init__.py +17 -17
  11. scanoss/api/common/__init__.py +17 -17
  12. scanoss/api/common/v2/__init__.py +17 -17
  13. scanoss/api/common/v2/scanoss_common_pb2.py +49 -20
  14. scanoss/api/common/v2/scanoss_common_pb2_grpc.py +25 -0
  15. scanoss/api/components/__init__.py +17 -17
  16. scanoss/api/components/v2/__init__.py +17 -17
  17. scanoss/api/components/v2/scanoss_components_pb2.py +68 -43
  18. scanoss/api/components/v2/scanoss_components_pb2_grpc.py +83 -22
  19. scanoss/api/cryptography/v2/scanoss_cryptography_pb2.py +136 -21
  20. scanoss/api/cryptography/v2/scanoss_cryptography_pb2_grpc.py +766 -13
  21. scanoss/api/dependencies/__init__.py +17 -17
  22. scanoss/api/dependencies/v2/__init__.py +17 -17
  23. scanoss/api/dependencies/v2/scanoss_dependencies_pb2.py +56 -29
  24. scanoss/api/dependencies/v2/scanoss_dependencies_pb2_grpc.py +94 -8
  25. scanoss/api/geoprovenance/__init__.py +23 -0
  26. scanoss/api/geoprovenance/v2/__init__.py +23 -0
  27. scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2.py +92 -0
  28. scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2_grpc.py +381 -0
  29. scanoss/api/licenses/__init__.py +23 -0
  30. scanoss/api/licenses/v2/__init__.py +23 -0
  31. scanoss/api/licenses/v2/scanoss_licenses_pb2.py +84 -0
  32. scanoss/api/licenses/v2/scanoss_licenses_pb2_grpc.py +302 -0
  33. scanoss/api/scanning/__init__.py +17 -17
  34. scanoss/api/scanning/v2/__init__.py +17 -17
  35. scanoss/api/scanning/v2/scanoss_scanning_pb2.py +42 -13
  36. scanoss/api/scanning/v2/scanoss_scanning_pb2_grpc.py +86 -7
  37. scanoss/api/semgrep/__init__.py +17 -17
  38. scanoss/api/semgrep/v2/__init__.py +17 -17
  39. scanoss/api/semgrep/v2/scanoss_semgrep_pb2.py +50 -23
  40. scanoss/api/semgrep/v2/scanoss_semgrep_pb2_grpc.py +151 -16
  41. scanoss/api/vulnerabilities/__init__.py +17 -17
  42. scanoss/api/vulnerabilities/v2/__init__.py +17 -17
  43. scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2.py +78 -31
  44. scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2_grpc.py +282 -18
  45. scanoss/cli.py +2359 -370
  46. scanoss/components.py +187 -94
  47. scanoss/constants.py +22 -0
  48. scanoss/cryptography.py +308 -0
  49. scanoss/csvoutput.py +91 -58
  50. scanoss/cyclonedx.py +221 -63
  51. scanoss/data/build_date.txt +1 -1
  52. scanoss/data/osadl-copyleft.json +133 -0
  53. scanoss/data/scanoss-settings-schema.json +254 -0
  54. scanoss/delta.py +197 -0
  55. scanoss/export/__init__.py +23 -0
  56. scanoss/export/dependency_track.py +227 -0
  57. scanoss/file_filters.py +582 -0
  58. scanoss/filecount.py +75 -69
  59. scanoss/gitlabqualityreport.py +214 -0
  60. scanoss/header_filter.py +563 -0
  61. scanoss/inspection/__init__.py +23 -0
  62. scanoss/inspection/policy_check/__init__.py +0 -0
  63. scanoss/inspection/policy_check/dependency_track/__init__.py +0 -0
  64. scanoss/inspection/policy_check/dependency_track/project_violation.py +479 -0
  65. scanoss/inspection/policy_check/policy_check.py +222 -0
  66. scanoss/inspection/policy_check/scanoss/__init__.py +0 -0
  67. scanoss/inspection/policy_check/scanoss/copyleft.py +243 -0
  68. scanoss/inspection/policy_check/scanoss/undeclared_component.py +309 -0
  69. scanoss/inspection/summary/__init__.py +0 -0
  70. scanoss/inspection/summary/component_summary.py +170 -0
  71. scanoss/inspection/summary/license_summary.py +191 -0
  72. scanoss/inspection/summary/match_summary.py +341 -0
  73. scanoss/inspection/utils/file_utils.py +44 -0
  74. scanoss/inspection/utils/license_utils.py +123 -0
  75. scanoss/inspection/utils/markdown_utils.py +63 -0
  76. scanoss/inspection/utils/scan_result_processor.py +417 -0
  77. scanoss/osadl.py +125 -0
  78. scanoss/results.py +275 -0
  79. scanoss/scancodedeps.py +87 -38
  80. scanoss/scanner.py +431 -539
  81. scanoss/scanners/__init__.py +23 -0
  82. scanoss/scanners/container_scanner.py +476 -0
  83. scanoss/scanners/folder_hasher.py +358 -0
  84. scanoss/scanners/scanner_config.py +73 -0
  85. scanoss/scanners/scanner_hfh.py +252 -0
  86. scanoss/scanoss_settings.py +337 -0
  87. scanoss/scanossapi.py +140 -101
  88. scanoss/scanossbase.py +59 -22
  89. scanoss/scanossgrpc.py +799 -251
  90. scanoss/scanpostprocessor.py +294 -0
  91. scanoss/scantype.py +22 -21
  92. scanoss/services/dependency_track_service.py +132 -0
  93. scanoss/spdxlite.py +532 -174
  94. scanoss/threadeddependencies.py +148 -47
  95. scanoss/threadedscanning.py +53 -37
  96. scanoss/utils/__init__.py +23 -0
  97. scanoss/utils/abstract_presenter.py +103 -0
  98. scanoss/utils/crc64.py +96 -0
  99. scanoss/utils/file.py +84 -0
  100. scanoss/utils/scanoss_scan_results_utils.py +41 -0
  101. scanoss/utils/simhash.py +198 -0
  102. scanoss/winnowing.py +241 -63
  103. {scanoss-1.12.2.dist-info → scanoss-1.43.1.dist-info}/METADATA +18 -9
  104. scanoss-1.43.1.dist-info/RECORD +110 -0
  105. {scanoss-1.12.2.dist-info → scanoss-1.43.1.dist-info}/WHEEL +1 -1
  106. scanoss-1.12.2.dist-info/RECORD +0 -58
  107. {scanoss-1.12.2.dist-info → scanoss-1.43.1.dist-info}/entry_points.txt +0 -0
  108. {scanoss-1.12.2.dist-info → scanoss-1.43.1.dist-info/licenses}/LICENSE +0 -0
  109. {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
- 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.
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: json):
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: json - JSON object
57
+ :param data: dict - JSON object
52
58
  :return: CycloneDX dictionary, and vulnerability dictionary
53
59
  """
54
- if not data:
60
+ if data is None:
55
61
  self.print_stderr('ERROR: No JSON data provided to parse.')
56
62
  return None, None
57
- self.print_debug(f'Processing raw results into CycloneDX format...')
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("id")
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("dependencies")
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("purl")
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
- dc = []
87
- for lic in licenses:
88
- name = lic.get("name")
89
- if name not in dc: # Only save the license name once
90
- fdl.append({'id': name})
91
- dc.append(name)
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("vulnerabilities")
117
+ vulnerabilities = d.get('vulnerabilities')
108
118
  if vulnerabilities:
109
119
  for vuln in vulnerabilities:
110
- vuln_id = vuln.get("ID")
120
+ vuln_id = vuln.get('ID')
111
121
  if vuln_id == '':
112
- vuln_id = vuln.get("id")
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("CVE", '')
125
+ vuln_cve = vuln.get('CVE', '')
116
126
  if vuln_cve == '':
117
- vuln_cve = vuln.get("cve", '')
118
- if vuln_id.upper().startswith("CPE:"):
119
- fd['cpe'] = vuln_id # Save the component CPE if we have one
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 = {'cve': vuln_cve,
126
- 'source': 'NVD' if vuln_source == 'nvd' else 'GitHub Advisories',
127
- 'url': f'https://nvd.nist.gov/vuln/detail/{vuln_cve}' if vuln_source == 'nvd' else f'https://github.com/advisories/{vuln_id}',
128
- 'severity': self._sev_lookup(vuln.get('severity', 'unknown').lower()),
129
- 'affects': set()
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
- for lic in licenses:
141
- fdl.append({'id': lic.get("name")})
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: json, output_file: str = None) -> bool:
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
- :param data: JSON object
169
- :param output_file: Output file (optional)
170
- :return: True if successful, False otherwise
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 not cdx:
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
- return self.produce_from_json(data, output_file)
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
- }.get(value, 'unknown')
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
  #
@@ -1 +1 @@
1
- date: 20240417094300, utime: 1713346980
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
+ }