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
@@ -0,0 +1,337 @@
1
+ """
2
+ SPDX-License-Identifier: MIT
3
+
4
+ Copyright (c) 2024, 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
+ """
24
+
25
+ import json
26
+ from pathlib import Path
27
+ from typing import List, Optional, TypedDict
28
+
29
+ import importlib_resources
30
+ from jsonschema import validate
31
+
32
+ from .scanossbase import ScanossBase
33
+ from .utils.file import (
34
+ JSON_ERROR_FILE_EMPTY,
35
+ JSON_ERROR_FILE_NOT_FOUND,
36
+ validate_json_file,
37
+ )
38
+
39
+ DEFAULT_SCANOSS_JSON_FILE = Path('scanoss.json')
40
+
41
+
42
+ class BomEntry(TypedDict, total=False):
43
+ purl: str
44
+ path: str
45
+
46
+
47
+ class SizeFilter(TypedDict, total=False):
48
+ patterns: List[str]
49
+ min: int
50
+ max: int
51
+
52
+
53
+ class ScanossSettingsError(Exception):
54
+ pass
55
+
56
+
57
+ def _load_settings_schema() -> dict:
58
+ """
59
+ Load the SCANOSS settings schema from a JSON file.
60
+
61
+ Returns:
62
+ dict: The parsed JSON content of the SCANOSS settings schema.
63
+
64
+ Raises:
65
+ ScanossSettingsError: If there is any issue in locating, reading, or parsing the JSON file
66
+ """
67
+ try:
68
+ schema_path = importlib_resources.files(__name__) / 'data' / 'scanoss-settings-schema.json'
69
+ with importlib_resources.as_file(schema_path) as f:
70
+ with open(f, 'r', encoding='utf-8') as file:
71
+ return json.load(file)
72
+ except Exception as e:
73
+ raise ScanossSettingsError(f'ERROR: Problem parsing Scanoss Settings Schema JSON file: {e}') from e
74
+
75
+
76
+ class ScanossSettings(ScanossBase):
77
+ """
78
+ Handles the loading and parsing of the SCANOSS settings file
79
+ """
80
+
81
+ def __init__(
82
+ self,
83
+ debug: bool = False,
84
+ trace: bool = False,
85
+ quiet: bool = False,
86
+ filepath: 'str | None' = None,
87
+ ):
88
+ """
89
+ Args:
90
+ debug (bool, optional): Debug. Defaults to False.
91
+ trace (bool, optional): Trace. Defaults to False.
92
+ quiet (bool, optional): Quiet. Defaults to False.
93
+ filepath (str, optional): Path to settings file. Defaults to None.
94
+ """
95
+ super().__init__(debug, trace, quiet)
96
+ self.data = {}
97
+ self.settings_file_type = None
98
+ self.scan_type = None
99
+ self.schema = _load_settings_schema()
100
+ if filepath:
101
+ self.load_json_file(filepath)
102
+
103
+ def load_json_file(self, filepath: Optional[str] = None, scan_root: Optional[str] = None) -> 'ScanossSettings':
104
+ """
105
+ Load the scan settings file. If no filepath is provided, scanoss.json will be used as default.
106
+
107
+ Args:
108
+ filepath (str): Path to the SCANOSS settings file
109
+ """
110
+
111
+ if not filepath:
112
+ filepath = DEFAULT_SCANOSS_JSON_FILE
113
+
114
+ filepath = Path(scan_root) / filepath if scan_root else Path(filepath)
115
+
116
+ json_file = filepath.resolve()
117
+
118
+ if filepath == DEFAULT_SCANOSS_JSON_FILE and not json_file.exists():
119
+ self.print_debug(f'Default settings file "{filepath}" not found. Skipping...')
120
+ return self
121
+ self.print_msg(f'Loading settings file {filepath}...')
122
+
123
+ result = validate_json_file(json_file)
124
+ if not result.is_valid:
125
+ if result.error_code in (JSON_ERROR_FILE_NOT_FOUND, JSON_ERROR_FILE_EMPTY):
126
+ self.print_msg(
127
+ f'WARNING: The supplied settings file "{filepath}" was not found or is empty. Skipping...'
128
+ )
129
+ return self
130
+ else:
131
+ raise ScanossSettingsError(f'Problem with settings file. {result.error}')
132
+ try:
133
+ validate(result.data, self.schema)
134
+ except Exception as e:
135
+ raise ScanossSettingsError(f'Invalid settings file. {e}') from e
136
+ self.data = result.data
137
+ self.print_debug(f'Loading scan settings from: {filepath}')
138
+ return self
139
+
140
+ def set_file_type(self, file_type: str):
141
+ """
142
+ Set the file type in order to support both legacy SBOM.json and new scanoss.json files
143
+ Args:
144
+ file_type (str): 'legacy' or 'new'
145
+
146
+ Raises:
147
+ Exception: Invalid scan settings file, missing "components" or "bom"
148
+ """
149
+ self.settings_file_type = file_type
150
+ if not self._is_valid_sbom_file:
151
+ raise Exception('Invalid scan settings file, missing "components" or "bom")')
152
+ return self
153
+
154
+ def set_scan_type(self, scan_type: str):
155
+ """
156
+ Set the scan type to support legacy SBOM.json files
157
+ Args:
158
+ scan_type (str): 'identify' or 'exclude'
159
+ """
160
+ self.scan_type = scan_type
161
+ return self
162
+
163
+ def _is_valid_sbom_file(self):
164
+ """
165
+ Check if the scan settings file is valid
166
+ Returns:
167
+ bool: True if the file is valid, False otherwise
168
+ """
169
+ if not self.data.get('components') or not self.data.get('bom'):
170
+ return False
171
+ return True
172
+
173
+ def _get_bom(self):
174
+ """
175
+ Get the Bill of Materials from the settings file
176
+ Returns:
177
+ dict: If using scanoss.json
178
+ list: If using SBOM.json
179
+ """
180
+ if self.settings_file_type == 'legacy':
181
+ if isinstance(self.data, list):
182
+ return self.data
183
+ elif isinstance(self.data, dict) and self.data.get('components'):
184
+ return self.data.get('components')
185
+ else:
186
+ return []
187
+ return self.data.get('bom', {})
188
+
189
+ def get_bom_include(self) -> List[BomEntry]:
190
+ """
191
+ Get the list of components to include in the scan
192
+ Returns:
193
+ list: List of components to include in the scan
194
+ """
195
+ if self.settings_file_type == 'legacy':
196
+ return self._get_bom()
197
+ return self._get_bom().get('include', [])
198
+
199
+
200
+ def get_bom_exclude(self) -> List[BomEntry]:
201
+ """
202
+ Get the list of components to exclude from the scan
203
+ Returns:
204
+ list: List of components to exclude from the scan
205
+ """
206
+ if self.settings_file_type == 'legacy':
207
+ return self._get_bom()
208
+ return self._get_bom().get('exclude', [])
209
+
210
+ def get_bom_remove(self) -> List[BomEntry]:
211
+ """
212
+ Get the list of components to remove from the scan
213
+ Returns:
214
+ list: List of components to remove from the scan
215
+ """
216
+ if self.settings_file_type == 'legacy':
217
+ return self._get_bom()
218
+ return self._get_bom().get('remove', [])
219
+
220
+ def get_bom_replace(self) -> List[BomEntry]:
221
+ """
222
+ Get the list of components to replace in the scan
223
+ Returns:
224
+ list: List of components to replace in the scan
225
+ """
226
+ if self.settings_file_type == 'legacy':
227
+ return []
228
+ return self._get_bom().get('replace', [])
229
+
230
+ def get_sbom(self):
231
+ """
232
+ Get the SBOM to be sent to the SCANOSS API
233
+ Returns:
234
+ dict: SBOM request payload
235
+ """
236
+ if not self.data:
237
+ return None
238
+ return {
239
+ 'assets': json.dumps(self._get_sbom_assets()),
240
+ 'scan_type': self.scan_type,
241
+ }
242
+
243
+ def _get_sbom_assets(self):
244
+ """
245
+ Get the SBOM assets
246
+ Returns:
247
+ List: List of SBOM assets
248
+ """
249
+
250
+ if self.settings_file_type == 'new':
251
+ if len(self.get_bom_include()):
252
+ self.scan_type = 'identify'
253
+ include_bom_entries = self._remove_duplicates(self.normalize_bom_entries(self.get_bom_include()))
254
+ return {"components": include_bom_entries}
255
+ elif len(self.get_bom_exclude()):
256
+ self.scan_type = 'blacklist'
257
+ exclude_bom_entries = self._remove_duplicates(self.normalize_bom_entries(self.get_bom_exclude()))
258
+ return {"components": exclude_bom_entries}
259
+
260
+ if self.settings_file_type == 'legacy' and self.scan_type == 'identify': # sbom-identify.json
261
+ include_bom_entries = self._remove_duplicates(self.normalize_bom_entries(self.get_bom_include()))
262
+ replace_bom_entries = self._remove_duplicates(self.normalize_bom_entries(self.get_bom_replace()))
263
+ self.print_debug(
264
+ f"Scan type set to 'identify'. Adding {len(include_bom_entries) + len(replace_bom_entries)} components as context to the scan. \n" # noqa: E501
265
+ f'From Include list: {[entry["purl"] for entry in include_bom_entries]} \n'
266
+ f'From Replace list: {[entry["purl"] for entry in replace_bom_entries]} \n'
267
+ )
268
+ return include_bom_entries + replace_bom_entries
269
+
270
+ if self.settings_file_type == 'legacy' and self.scan_type == 'blacklist': # sbom-identify.json
271
+ exclude_bom_entries = self._remove_duplicates(self.normalize_bom_entries(self.get_bom_exclude()))
272
+ self.print_debug(
273
+ f"Scan type set to 'blacklist'. Adding {len(exclude_bom_entries)} components as context to the scan. \n" # noqa: E501
274
+ f'From Exclude list: {[entry["purl"] for entry in exclude_bom_entries]} \n')
275
+ return exclude_bom_entries
276
+
277
+ return self.normalize_bom_entries(self.get_bom_remove())
278
+
279
+ @staticmethod
280
+ def normalize_bom_entries(bom_entries) -> List[BomEntry]:
281
+ """
282
+ Normalize the BOM entries
283
+ Args:
284
+ bom_entries (List[Dict]): List of BOM entries
285
+ Returns:
286
+ List: Normalized BOM entries
287
+ """
288
+ normalized_bom_entries = []
289
+ for entry in bom_entries:
290
+ normalized_bom_entries.append(
291
+ {
292
+ 'purl': entry.get('purl', ''),
293
+ }
294
+ )
295
+ return normalized_bom_entries
296
+
297
+ @staticmethod
298
+ def _remove_duplicates(bom_entries: List[BomEntry]) -> List[BomEntry]:
299
+ """
300
+ Remove duplicate BOM entries
301
+ Args:
302
+ bom_entries (List[Dict]): List of BOM entries
303
+ Returns:
304
+ List: List of unique BOM entries
305
+ """
306
+ already_added = set()
307
+ unique_entries = []
308
+ for entry in bom_entries:
309
+ entry_tuple = tuple(entry.items())
310
+ if entry_tuple not in already_added:
311
+ already_added.add(entry_tuple)
312
+ unique_entries.append(entry)
313
+ return unique_entries
314
+
315
+ def is_legacy(self):
316
+ """Check if the settings file is legacy"""
317
+ return self.settings_file_type == 'legacy'
318
+
319
+ def get_skip_patterns(self, operation_type: str) -> List[str]:
320
+ """
321
+ Get the list of patterns to skip based on the operation type
322
+ Args:
323
+ operation_type (str): Operation type
324
+ Returns:
325
+ List: List of patterns to skip
326
+ """
327
+ return self.data.get('settings', {}).get('skip', {}).get('patterns', {}).get(operation_type, [])
328
+
329
+ def get_skip_sizes(self, operation_type: str) -> List[SizeFilter]:
330
+ """
331
+ Get the min and max sizes to skip based on the operation type
332
+ Args:
333
+ operation_type (str): Operation type
334
+ Returns:
335
+ List: Min and max sizes to skip
336
+ """
337
+ return self.data.get('settings', {}).get('skip', {}).get('sizes', {}).get(operation_type, [])