scanoss 1.19.6__py3-none-any.whl → 1.20.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 (71) 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 +12 -9
  4. protoc_gen_swagger/options/annotations_pb2_grpc.py +1 -1
  5. protoc_gen_swagger/options/openapiv2_pb2.py +98 -96
  6. protoc_gen_swagger/options/openapiv2_pb2_grpc.py +1 -1
  7. scanoss/__init__.py +18 -18
  8. scanoss/api/__init__.py +17 -17
  9. scanoss/api/common/__init__.py +17 -17
  10. scanoss/api/common/v2/__init__.py +17 -17
  11. scanoss/api/common/v2/scanoss_common_pb2.py +18 -18
  12. scanoss/api/common/v2/scanoss_common_pb2_grpc.py +1 -1
  13. scanoss/api/components/__init__.py +17 -17
  14. scanoss/api/components/v2/__init__.py +17 -17
  15. scanoss/api/components/v2/scanoss_components_pb2.py +48 -38
  16. scanoss/api/components/v2/scanoss_components_pb2_grpc.py +142 -96
  17. scanoss/api/cryptography/v2/scanoss_cryptography_pb2.py +22 -16
  18. scanoss/api/cryptography/v2/scanoss_cryptography_pb2_grpc.py +75 -49
  19. scanoss/api/dependencies/__init__.py +17 -17
  20. scanoss/api/dependencies/v2/__init__.py +17 -17
  21. scanoss/api/dependencies/v2/scanoss_dependencies_pb2.py +30 -24
  22. scanoss/api/dependencies/v2/scanoss_dependencies_pb2_grpc.py +75 -49
  23. scanoss/api/provenance/__init__.py +23 -0
  24. scanoss/api/provenance/v2/__init__.py +23 -0
  25. scanoss/api/provenance/v2/scanoss_provenance_pb2.py +42 -0
  26. scanoss/api/provenance/v2/scanoss_provenance_pb2_grpc.py +108 -0
  27. scanoss/api/scanning/__init__.py +17 -17
  28. scanoss/api/scanning/v2/__init__.py +17 -17
  29. scanoss/api/scanning/v2/scanoss_scanning_pb2.py +10 -8
  30. scanoss/api/scanning/v2/scanoss_scanning_pb2_grpc.py +40 -32
  31. scanoss/api/semgrep/__init__.py +17 -17
  32. scanoss/api/semgrep/v2/__init__.py +17 -17
  33. scanoss/api/semgrep/v2/scanoss_semgrep_pb2.py +22 -18
  34. scanoss/api/semgrep/v2/scanoss_semgrep_pb2_grpc.py +71 -49
  35. scanoss/api/vulnerabilities/__init__.py +17 -17
  36. scanoss/api/vulnerabilities/v2/__init__.py +17 -17
  37. scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2.py +37 -27
  38. scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2_grpc.py +109 -72
  39. scanoss/cli.py +578 -264
  40. scanoss/components.py +99 -48
  41. scanoss/csvoutput.py +83 -56
  42. scanoss/cyclonedx.py +48 -46
  43. scanoss/data/build_date.txt +1 -1
  44. scanoss/file_filters.py +13 -15
  45. scanoss/filecount.py +43 -36
  46. scanoss/inspection/__init__.py +17 -17
  47. scanoss/inspection/copyleft.py +71 -58
  48. scanoss/inspection/policy_check.py +76 -53
  49. scanoss/inspection/undeclared_component.py +98 -75
  50. scanoss/inspection/utils/license_utils.py +66 -44
  51. scanoss/results.py +51 -60
  52. scanoss/scancodedeps.py +61 -38
  53. scanoss/scanner.py +203 -135
  54. scanoss/scanoss_settings.py +5 -3
  55. scanoss/scanossapi.py +98 -69
  56. scanoss/scanossbase.py +19 -19
  57. scanoss/scanossgrpc.py +107 -51
  58. scanoss/scanpostprocessor.py +9 -6
  59. scanoss/scantype.py +22 -21
  60. scanoss/spdxlite.py +265 -171
  61. scanoss/threadeddependencies.py +91 -61
  62. scanoss/threadedscanning.py +37 -31
  63. scanoss/utils/file.py +4 -4
  64. scanoss/winnowing.py +111 -47
  65. {scanoss-1.19.6.dist-info → scanoss-1.20.1.dist-info}/METADATA +1 -1
  66. scanoss-1.20.1.dist-info/RECORD +74 -0
  67. scanoss-1.19.6.dist-info/RECORD +0 -70
  68. {scanoss-1.19.6.dist-info → scanoss-1.20.1.dist-info}/LICENSE +0 -0
  69. {scanoss-1.19.6.dist-info → scanoss-1.20.1.dist-info}/WHEEL +0 -0
  70. {scanoss-1.19.6.dist-info → scanoss-1.20.1.dist-info}/entry_points.txt +0 -0
  71. {scanoss-1.19.6.dist-info → scanoss-1.20.1.dist-info}/top_level.txt +0 -0
scanoss/components.py CHANGED
@@ -1,30 +1,31 @@
1
1
  """
2
- SPDX-License-Identifier: MIT
3
-
4
- Copyright (c) 2023, 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) 2023, 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
+
24
25
  import json
25
26
  import os
26
27
  import sys
27
- from typing import TextIO
28
+ from typing import TextIO, Optional, List
28
29
 
29
30
  from pypac.parser import PACFile
30
31
 
@@ -38,10 +39,19 @@ class Components(ScanossBase):
38
39
  Class for Component functionality
39
40
  """
40
41
 
41
- def __init__(self, debug: bool = False, trace: bool = False, quiet: bool = False,
42
- grpc_url: str = None, api_key: str = None, timeout: int = 600,
43
- proxy: str = None, grpc_proxy: str = None, ca_cert: str = None, pac: PACFile = None
44
- ):
42
+ def __init__(
43
+ self,
44
+ debug: bool = False,
45
+ trace: bool = False,
46
+ quiet: bool = False,
47
+ grpc_url: str = None,
48
+ api_key: str = None,
49
+ timeout: int = 600,
50
+ proxy: str = None,
51
+ grpc_proxy: str = None,
52
+ ca_cert: str = None,
53
+ pac: PACFile = None,
54
+ ):
45
55
  """
46
56
  Handle all component style requests
47
57
 
@@ -58,17 +68,27 @@ class Components(ScanossBase):
58
68
  """
59
69
  super().__init__(debug, trace, quiet)
60
70
  ver_details = Scanner.version_details()
61
- self.grpc_api = ScanossGrpc(url=grpc_url, debug=debug, quiet=quiet, trace=trace, api_key=api_key,
62
- ver_details=ver_details, ca_cert=ca_cert, proxy=proxy, pac=pac,
63
- grpc_proxy=grpc_proxy, timeout=timeout)
71
+ self.grpc_api = ScanossGrpc(
72
+ url=grpc_url,
73
+ debug=debug,
74
+ quiet=quiet,
75
+ trace=trace,
76
+ api_key=api_key,
77
+ ver_details=ver_details,
78
+ ca_cert=ca_cert,
79
+ proxy=proxy,
80
+ pac=pac,
81
+ grpc_proxy=grpc_proxy,
82
+ timeout=timeout,
83
+ )
64
84
 
65
- def load_purls(self, json_file: str = None, purls: [] = None) -> dict:
85
+ def load_purls(self, json_file: Optional[str] = None, purls: Optional[List[str]] = None) -> Optional[dict]:
66
86
  """
67
87
  Load the specified purls and return a dictionary
68
88
 
69
89
  :param json_file: JSON PURL file (optional)
70
90
  :param purls: list of PURLs (optional)
71
- :return: PURL Request dictionary
91
+ :return: PURL Request dictionary or None
72
92
  """
73
93
  if json_file:
74
94
  if not os.path.isfile(json_file) or not os.access(json_file, os.R_OK):
@@ -81,6 +101,9 @@ class Components(ScanossBase):
81
101
  self.print_stderr(f'ERROR: Problem parsing input JSON: {e}')
82
102
  return None
83
103
  elif purls:
104
+ if not all(isinstance(purl, str) for purl in purls):
105
+ self.print_stderr('ERROR: PURLs must be a list of strings.')
106
+ return None
84
107
  parsed_purls = []
85
108
  for p in purls:
86
109
  parsed_purls.append({'purl': p})
@@ -219,9 +242,17 @@ class Components(ScanossBase):
219
242
  self._close_file(output_file, file)
220
243
  return success
221
244
 
222
- def search_components(self, output_file: str = None, json_file: str = None,
223
- search: str = None, vendor: str = None, comp: str = None, package: str = None,
224
- limit: int = None, offset: int = None) -> bool:
245
+ def search_components(
246
+ self,
247
+ output_file: str = None,
248
+ json_file: str = None,
249
+ search: str = None,
250
+ vendor: str = None,
251
+ comp: str = None,
252
+ package: str = None,
253
+ limit: int = None,
254
+ offset: int = None,
255
+ ) -> bool:
225
256
  """
226
257
  Search for a component based on the given search criteria
227
258
 
@@ -242,16 +273,11 @@ class Components(ScanossBase):
242
273
  if request is None:
243
274
  return False
244
275
  else: # Construct a query dictionary from parameters
245
- request = {
246
- "search": search,
247
- "vendor": vendor,
248
- "component": comp,
249
- "package": package
250
- }
276
+ request = {'search': search, 'vendor': vendor, 'component': comp, 'package': package}
251
277
  if limit is not None and limit > 0:
252
- request["limit"] = limit
278
+ request['limit'] = limit
253
279
  if offset is not None and offset > 0:
254
- request["offset"] = offset
280
+ request['offset'] = offset
255
281
 
256
282
  file = self._open_file_or_sdtout(output_file)
257
283
  if file is None:
@@ -266,8 +292,9 @@ class Components(ScanossBase):
266
292
  self._close_file(output_file, file)
267
293
  return success
268
294
 
269
- def get_component_versions(self, output_file: str = None, json_file: str = None,
270
- purl: str = None, limit: int = None) -> bool:
295
+ def get_component_versions(
296
+ self, output_file: str = None, json_file: str = None, purl: str = None, limit: int = None
297
+ ) -> bool:
271
298
  """
272
299
  Search for a component versions based on the given search criteria
273
300
 
@@ -284,11 +311,9 @@ class Components(ScanossBase):
284
311
  if request is None:
285
312
  return False
286
313
  else: # Construct a query dictionary from parameters
287
- request = {
288
- "purl": purl
289
- }
314
+ request = {'purl': purl}
290
315
  if limit is not None and limit > 0:
291
- request["limit"] = limit
316
+ request['limit'] = limit
292
317
 
293
318
  file = self._open_file_or_sdtout(output_file)
294
319
  if file is None:
@@ -302,3 +327,29 @@ class Components(ScanossBase):
302
327
  self.print_msg(f'Results written to: {output_file}')
303
328
  self._close_file(output_file, file)
304
329
  return success
330
+
331
+ def get_provenance_details(self, json_file: str = None, purls: [] = None, output_file: str = None) -> bool:
332
+ """
333
+ Retrieve the semgrep details for the supplied PURLs
334
+
335
+ :param json_file: PURL JSON request file (optional)
336
+ :param purls: PURL request array (optional)
337
+ :param output_file: output filename (optional). Default: STDOUT
338
+ :return: True on success, False otherwise
339
+ """
340
+ success = False
341
+ purls_request = self.load_purls(json_file, purls)
342
+ if purls_request is None or len(purls_request) == 0:
343
+ return False
344
+ file = self._open_file_or_sdtout(output_file)
345
+ if file is None:
346
+ return False
347
+ self.print_msg('Sending PURLs to Provenance API for decoration...')
348
+ response = self.grpc_api.get_provenance_json(purls_request)
349
+ if response:
350
+ print(json.dumps(response, indent=2, sort_keys=True), file=file)
351
+ success = True
352
+ if output_file:
353
+ self.print_msg(f'Results written to: {output_file}')
354
+ self._close_file(output_file, file)
355
+ return success
scanoss/csvoutput.py CHANGED
@@ -1,26 +1,27 @@
1
1
  """
2
- SPDX-License-Identifier: MIT
3
-
4
- Copyright (c) 2022, 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) 2022, 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
+
24
25
  import json
25
26
  import os.path
26
27
  import sys
@@ -59,21 +60,21 @@ class CsvOutput(ScanossBase):
59
60
  file_details = data.get(f)
60
61
  # print(f'File: {f}: {file_details}')
61
62
  for d in file_details:
62
- id_details = d.get("id")
63
+ id_details = d.get('id')
63
64
  if not id_details or id_details == 'none':
64
65
  continue
65
- matched = d.get("matched", '')
66
- lines = d.get("lines", '').replace(',', ';') # swap comma with semicolon to help basic parsers
67
- oss_lines = d.get("oss_lines", '').replace(',', ';')
66
+ matched = d.get('matched', '')
67
+ lines = d.get('lines', '').replace(',', ';') # swap comma with semicolon to help basic parsers
68
+ oss_lines = d.get('oss_lines', '').replace(',', ';')
68
69
  detected = {}
69
70
  if id_details == 'dependency':
70
- dependencies = d.get("dependencies")
71
+ dependencies = d.get('dependencies')
71
72
  if not dependencies:
72
73
  self.print_stderr(f'Warning: No Dependencies found for {f}: {file_details}')
73
74
  continue
74
75
  for deps in dependencies:
75
76
  detected = {}
76
- purl = deps.get("purl")
77
+ purl = deps.get('purl')
77
78
  if not purl:
78
79
  self.print_stderr(f'Warning: No PURL found for {f}: {deps}')
79
80
  continue
@@ -84,7 +85,7 @@ class CsvOutput(ScanossBase):
84
85
  dc = []
85
86
  if licenses:
86
87
  for lic in licenses:
87
- name = lic.get("name")
88
+ name = lic.get('name')
88
89
  if name and name not in dc: # Only save the license name once
89
90
  dc.append(name)
90
91
  if not dc or len(dc) == 0:
@@ -92,17 +93,23 @@ class CsvOutput(ScanossBase):
92
93
  else:
93
94
  detected['licenses'] = ';'.join(dc)
94
95
  # inventory_id,path,usage,detected_component,detected_license,detected_version,detected_latest,purl
95
- csv_dict.append({'inventory_id': row_id, 'path': f, 'detected_usage': id_details,
96
- 'detected_component': detected.get('component'),
97
- 'detected_license': detected.get('licenses'),
98
- 'detected_version': detected.get('version'),
99
- 'detected_latest': detected.get('latest'),
100
- 'detected_purls': detected.get('purls'),
101
- 'detected_url': detected.get('url'),
102
- 'detected_path': detected.get('file', ''),
103
- 'detected_match': matched, 'detected_lines': lines,
104
- 'detected_oss_lines': oss_lines
105
- })
96
+ csv_dict.append(
97
+ {
98
+ 'inventory_id': row_id,
99
+ 'path': f,
100
+ 'detected_usage': id_details,
101
+ 'detected_component': detected.get('component'),
102
+ 'detected_license': detected.get('licenses'),
103
+ 'detected_version': detected.get('version'),
104
+ 'detected_latest': detected.get('latest'),
105
+ 'detected_purls': detected.get('purls'),
106
+ 'detected_url': detected.get('url'),
107
+ 'detected_path': detected.get('file', ''),
108
+ 'detected_match': matched,
109
+ 'detected_lines': lines,
110
+ 'detected_oss_lines': oss_lines,
111
+ }
112
+ )
106
113
  row_id = row_id + 1
107
114
  else:
108
115
  purls = d.get('purl')
@@ -123,25 +130,31 @@ class CsvOutput(ScanossBase):
123
130
  dc = []
124
131
  if licenses:
125
132
  for lic in licenses:
126
- name = lic.get("name")
133
+ name = lic.get('name')
127
134
  if name and name not in dc: # Only save the license name once
128
- dc.append(lic.get("name"))
135
+ dc.append(lic.get('name'))
129
136
  if not dc or len(dc) == 0:
130
137
  detected['licenses'] = ''
131
138
  else:
132
139
  detected['licenses'] = ';'.join(dc)
133
140
  # inventory_id,path,usage,detected_component,detected_license,detected_version,detected_latest,purl
134
- csv_dict.append({'inventory_id': row_id, 'path': f, 'detected_usage': id_details,
135
- 'detected_component': detected.get('component'),
136
- 'detected_license': detected.get('licenses'),
137
- 'detected_version': detected.get('version'),
138
- 'detected_latest': detected.get('latest'),
139
- 'detected_purls': detected.get('purls'),
140
- 'detected_url': detected.get('url'),
141
- 'detected_path': detected.get('file', ''),
142
- 'detected_match': matched, 'detected_lines': lines,
143
- 'detected_oss_lines': oss_lines
144
- })
141
+ csv_dict.append(
142
+ {
143
+ 'inventory_id': row_id,
144
+ 'path': f,
145
+ 'detected_usage': id_details,
146
+ 'detected_component': detected.get('component'),
147
+ 'detected_license': detected.get('licenses'),
148
+ 'detected_version': detected.get('version'),
149
+ 'detected_latest': detected.get('latest'),
150
+ 'detected_purls': detected.get('purls'),
151
+ 'detected_url': detected.get('url'),
152
+ 'detected_path': detected.get('file', ''),
153
+ 'detected_match': matched,
154
+ 'detected_lines': lines,
155
+ 'detected_oss_lines': oss_lines,
156
+ }
157
+ )
145
158
  row_id = row_id + 1
146
159
  return csv_dict
147
160
 
@@ -174,16 +187,28 @@ class CsvOutput(ScanossBase):
174
187
  self.print_stderr('ERROR: No CSV data returned for the JSON string provided.')
175
188
  return False
176
189
  # Header row/column details
177
- fields = ['inventory_id', 'path', 'detected_usage', 'detected_component', 'detected_license',
178
- 'detected_version', 'detected_latest', 'detected_purls', 'detected_url', 'detected_match',
179
- 'detected_lines', 'detected_oss_lines', 'detected_path']
190
+ fields = [
191
+ 'inventory_id',
192
+ 'path',
193
+ 'detected_usage',
194
+ 'detected_component',
195
+ 'detected_license',
196
+ 'detected_version',
197
+ 'detected_latest',
198
+ 'detected_purls',
199
+ 'detected_url',
200
+ 'detected_match',
201
+ 'detected_lines',
202
+ 'detected_oss_lines',
203
+ 'detected_path',
204
+ ]
180
205
  file = sys.stdout
181
206
  if not output_file and self.output_file:
182
207
  output_file = self.output_file
183
208
  if output_file:
184
209
  file = open(output_file, 'w')
185
210
  writer = csv.DictWriter(file, fieldnames=fields)
186
- writer.writeheader() # writing headers (field names)
211
+ writer.writeheader() # writing headers (field names)
187
212
  writer.writerows(csv_data) # writing data rows
188
213
  if output_file:
189
214
  file.close()
@@ -206,6 +231,8 @@ class CsvOutput(ScanossBase):
206
231
  self.print_stderr(f'ERROR: Problem parsing input JSON: {e}')
207
232
  return False
208
233
  return self.produce_from_json(data, output_file)
234
+
235
+
209
236
  #
210
237
  # End of CsvOutput Class
211
238
  #
scanoss/cyclonedx.py CHANGED
@@ -1,26 +1,27 @@
1
1
  """
2
- SPDX-License-Identifier: MIT
2
+ SPDX-License-Identifier: MIT
3
3
 
4
- Copyright (c) 2021, SCANOSS
4
+ Copyright (c) 2021, SCANOSS
5
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:
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
12
 
13
- The above copyright notice and this permission notice shall be included in
14
- all copies or substantial portions of the Software.
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
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.
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
+
24
25
  import json
25
26
  import os.path
26
27
  import sys
@@ -64,17 +65,17 @@ class CycloneDx(ScanossBase):
64
65
  file_details = data.get(f)
65
66
  # print(f'File: {f}: {file_details}')
66
67
  for d in file_details:
67
- id_details = d.get("id")
68
+ id_details = d.get('id')
68
69
  if not id_details or id_details == 'none':
69
70
  continue
70
71
  purl = None
71
72
  if id_details == 'dependency':
72
- dependencies = d.get("dependencies")
73
+ dependencies = d.get('dependencies')
73
74
  if not dependencies:
74
75
  self.print_stderr(f'Warning: No Dependencies found for {f}: {file_details}')
75
76
  continue
76
77
  for deps in dependencies:
77
- purl = deps.get("purl")
78
+ purl = deps.get('purl')
78
79
  if not purl:
79
80
  self.print_stderr(f'Warning: No PURL found for {f}: {deps}')
80
81
  continue
@@ -89,7 +90,7 @@ class CycloneDx(ScanossBase):
89
90
  if licenses:
90
91
  dc = []
91
92
  for lic in licenses:
92
- name = lic.get("name")
93
+ name = lic.get('name')
93
94
  if name not in dc: # Only save the license name once
94
95
  fdl.append({'id': name})
95
96
  dc.append(name)
@@ -108,30 +109,33 @@ class CycloneDx(ScanossBase):
108
109
  self.print_stderr(f'Warning: No PURL found for {f}: {file_details}')
109
110
  continue
110
111
  fd = {}
111
- vulnerabilities = d.get("vulnerabilities")
112
+ vulnerabilities = d.get('vulnerabilities')
112
113
  if vulnerabilities:
113
114
  for vuln in vulnerabilities:
114
- vuln_id = vuln.get("ID")
115
+ vuln_id = vuln.get('ID')
115
116
  if vuln_id == '':
116
- vuln_id = vuln.get("id")
117
+ vuln_id = vuln.get('id')
117
118
  if not vuln_id or vuln_id == '': # Skip empty ids
118
119
  continue
119
- vuln_cve = vuln.get("CVE", '')
120
+ vuln_cve = vuln.get('CVE', '')
120
121
  if vuln_cve == '':
121
- vuln_cve = vuln.get("cve", '')
122
- if vuln_id.upper().startswith("CPE:"):
123
- fd['cpe'] = vuln_id # Save the component CPE if we have one
122
+ vuln_cve = vuln.get('cve', '')
123
+ if vuln_id.upper().startswith('CPE:'):
124
+ fd['cpe'] = vuln_id # Save the component CPE if we have one
124
125
  if vuln_cve != '':
125
126
  vuln_id = vuln_cve
126
127
  vd = vdx.get(vuln_id) # Check if we've already encountered this vulnerability
127
128
  if not vd:
128
129
  vuln_source = vuln.get('source', '').lower()
129
- vd = {'cve': vuln_cve,
130
- 'source': 'NVD' if vuln_source == 'nvd' else 'GitHub Advisories',
131
- 'url': f'https://nvd.nist.gov/vuln/detail/{vuln_cve}' if vuln_source == 'nvd' else f'https://github.com/advisories/{vuln_id}',
132
- 'severity': self._sev_lookup(vuln.get('severity', 'unknown').lower()),
133
- 'affects': set()
134
- }
130
+ vd = {
131
+ 'cve': vuln_cve,
132
+ 'source': 'NVD' if vuln_source == 'nvd' else 'GitHub Advisories',
133
+ 'url': f'https://nvd.nist.gov/vuln/detail/{vuln_cve}'
134
+ if vuln_source == 'nvd'
135
+ else f'https://github.com/advisories/{vuln_id}',
136
+ 'severity': self._sev_lookup(vuln.get('severity', 'unknown').lower()),
137
+ 'affects': set(),
138
+ }
135
139
  vd.get('affects').add(purl)
136
140
  vdx[vuln_id] = vd
137
141
  if cdx.get(purl):
@@ -143,7 +147,7 @@ class CycloneDx(ScanossBase):
143
147
  fdl = []
144
148
  if licenses:
145
149
  for lic in licenses:
146
- fdl.append({'id': lic.get("name")})
150
+ fdl.append({'id': lic.get('name')})
147
151
  fd['licenses'] = fdl
148
152
  cdx[purl] = fd
149
153
  # self.print_stderr(f'VD: {vdx}')
@@ -190,7 +194,7 @@ class CycloneDx(ScanossBase):
190
194
  'serialNumber': f'urn:uuid:{uuid.uuid4()}',
191
195
  'version': 1,
192
196
  'metadata': {
193
- 'timestamp': datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
197
+ 'timestamp': datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ'),
194
198
  'tools': [
195
199
  {
196
200
  'vendor': 'SCANOSS',
@@ -198,14 +202,10 @@ class CycloneDx(ScanossBase):
198
202
  'version': __version__,
199
203
  }
200
204
  ],
201
- 'component': {
202
- 'type': 'application',
203
- 'name': 'NOASSERTION',
204
- 'version': 'NOASSERTION'
205
- }
205
+ 'component': {'type': 'application', 'name': 'NOASSERTION', 'version': 'NOASSERTION'},
206
206
  },
207
207
  'components': [],
208
- 'vulnerabilities': []
208
+ 'vulnerabilities': [],
209
209
  }
210
210
  for purl in cdx:
211
211
  comp = cdx.get(purl)
@@ -230,7 +230,7 @@ class CycloneDx(ScanossBase):
230
230
  'version': comp.get('version'),
231
231
  'purl': purl,
232
232
  'bom-ref': purl,
233
- 'licenses': lic_text
233
+ 'licenses': lic_text,
234
234
  }
235
235
  cpe = comp.get('cpe', '')
236
236
  if cpe and cpe != '':
@@ -250,7 +250,7 @@ class CycloneDx(ScanossBase):
250
250
  'id': vuln_id,
251
251
  'source': {'name': v_source, 'url': vulns.get('url')},
252
252
  'ratings': [{'severity': vulns.get('severity', 'unknown')}],
253
- 'affects': affects
253
+ 'affects': affects,
254
254
  }
255
255
  data['vulnerabilities'].append(vd)
256
256
  # End for loop
@@ -298,8 +298,10 @@ class CycloneDx(ScanossBase):
298
298
  'low': 'low',
299
299
  'info': 'info',
300
300
  'none': 'none',
301
- 'unknown': 'unknown'
302
- }.get(value, 'unknown')
301
+ 'unknown': 'unknown',
302
+ }.get(value, 'unknown')
303
+
304
+
303
305
  #
304
306
  # End of CycloneDX Class
305
307
  #
@@ -1 +1 @@
1
- date: 20250130190618, utime: 1738263978
1
+ date: 20250219134430, utime: 1739972670