scanoss 1.20.6__py3-none-any.whl → 1.23.0__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/options/annotations_pb2.py +9 -12
- protoc_gen_swagger/options/annotations_pb2_grpc.py +1 -1
- protoc_gen_swagger/options/openapiv2_pb2.py +96 -98
- protoc_gen_swagger/options/openapiv2_pb2_grpc.py +1 -1
- scanoss/__init__.py +1 -1
- scanoss/api/common/v2/scanoss_common_pb2.py +20 -18
- scanoss/api/common/v2/scanoss_common_pb2_grpc.py +1 -1
- scanoss/api/components/v2/scanoss_components_pb2.py +38 -48
- scanoss/api/components/v2/scanoss_components_pb2_grpc.py +96 -142
- scanoss/api/cryptography/v2/scanoss_cryptography_pb2.py +42 -22
- scanoss/api/cryptography/v2/scanoss_cryptography_pb2_grpc.py +185 -75
- scanoss/api/dependencies/v2/scanoss_dependencies_pb2.py +32 -30
- scanoss/api/dependencies/v2/scanoss_dependencies_pb2_grpc.py +83 -75
- scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2.py +49 -0
- scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2_grpc.py +142 -0
- scanoss/api/scanning/v2/scanoss_scanning_pb2.py +20 -10
- scanoss/api/scanning/v2/scanoss_scanning_pb2_grpc.py +70 -40
- scanoss/api/semgrep/v2/scanoss_semgrep_pb2.py +18 -22
- scanoss/api/semgrep/v2/scanoss_semgrep_pb2_grpc.py +49 -71
- scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2.py +27 -37
- scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2_grpc.py +72 -109
- scanoss/cli.py +393 -84
- scanoss/components.py +21 -11
- scanoss/constants.py +12 -0
- scanoss/data/build_date.txt +1 -1
- scanoss/file_filters.py +272 -57
- scanoss/results.py +92 -109
- scanoss/scanners/__init__.py +23 -0
- scanoss/scanners/container_scanner.py +474 -0
- scanoss/scanners/folder_hasher.py +302 -0
- scanoss/scanners/scanner_config.py +73 -0
- scanoss/scanners/scanner_hfh.py +173 -0
- scanoss/scanoss_settings.py +9 -5
- scanoss/scanossbase.py +9 -3
- scanoss/scanossgrpc.py +143 -18
- scanoss/threadedscanning.py +6 -6
- scanoss/utils/abstract_presenter.py +103 -0
- scanoss/utils/crc64.py +96 -0
- scanoss/utils/simhash.py +198 -0
- {scanoss-1.20.6.dist-info → scanoss-1.23.0.dist-info}/METADATA +2 -1
- scanoss-1.23.0.dist-info/RECORD +83 -0
- {scanoss-1.20.6.dist-info → scanoss-1.23.0.dist-info}/WHEEL +1 -1
- scanoss/api/provenance/v2/scanoss_provenance_pb2.py +0 -42
- scanoss/api/provenance/v2/scanoss_provenance_pb2_grpc.py +0 -108
- scanoss-1.20.6.dist-info/RECORD +0 -74
- /scanoss/api/{provenance → geoprovenance}/__init__.py +0 -0
- /scanoss/api/{provenance → geoprovenance}/v2/__init__.py +0 -0
- {scanoss-1.20.6.dist-info → scanoss-1.23.0.dist-info}/entry_points.txt +0 -0
- {scanoss-1.20.6.dist-info → scanoss-1.23.0.dist-info}/licenses/LICENSE +0 -0
- {scanoss-1.20.6.dist-info → scanoss-1.23.0.dist-info}/top_level.txt +0 -0
scanoss/results.py
CHANGED
|
@@ -25,6 +25,8 @@ SPDX-License-Identifier: MIT
|
|
|
25
25
|
import json
|
|
26
26
|
from typing import Any, Dict, List
|
|
27
27
|
|
|
28
|
+
from scanoss.utils.abstract_presenter import AbstractPresenter
|
|
29
|
+
|
|
28
30
|
from .scanossbase import ScanossBase
|
|
29
31
|
|
|
30
32
|
MATCH_TYPES = ['file', 'snippet']
|
|
@@ -47,16 +49,89 @@ PENDING_IDENTIFICATION_FILTERS = {
|
|
|
47
49
|
'status': ['pending'],
|
|
48
50
|
}
|
|
49
51
|
|
|
50
|
-
AVAILABLE_OUTPUT_FORMATS = ['json', 'plain']
|
|
51
52
|
|
|
53
|
+
class ResultsPresenter(AbstractPresenter):
|
|
54
|
+
"""
|
|
55
|
+
SCANOSS Results presenter class
|
|
56
|
+
Handles the presentation of the scan results
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(self, results_instance, **kwargs):
|
|
60
|
+
super().__init__(**kwargs)
|
|
61
|
+
self.results = results_instance
|
|
62
|
+
|
|
63
|
+
def _format_json_output(self) -> str:
|
|
64
|
+
"""
|
|
65
|
+
Format the output data into a JSON object
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
formatted_data = []
|
|
69
|
+
for item in self.results.data:
|
|
70
|
+
formatted_data.append(
|
|
71
|
+
{
|
|
72
|
+
'file': item.get('filename'),
|
|
73
|
+
'status': item.get('status', 'N/A'),
|
|
74
|
+
'match_type': item['id'],
|
|
75
|
+
'matched': item.get('matched', 'N/A'),
|
|
76
|
+
'purl': (item.get('purl')[0] if item.get('purl') else 'N/A'),
|
|
77
|
+
'license': (item.get('licenses')[0].get('name', 'N/A') if item.get('licenses') else 'N/A'),
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
try:
|
|
81
|
+
return json.dumps({'results': formatted_data, 'total': len(formatted_data)}, indent=2)
|
|
82
|
+
except Exception as e:
|
|
83
|
+
self.base.print_stderr(f'ERROR: Problem formatting JSON output: {e}')
|
|
84
|
+
return ''
|
|
85
|
+
|
|
86
|
+
def _format_cyclonedx_output(self) -> str:
|
|
87
|
+
raise NotImplementedError('CycloneDX output is not implemented')
|
|
88
|
+
|
|
89
|
+
def _format_spdxlite_output(self) -> str:
|
|
90
|
+
raise NotImplementedError('SPDXlite output is not implemented')
|
|
91
|
+
|
|
92
|
+
def _format_csv_output(self) -> str:
|
|
93
|
+
raise NotImplementedError('CSV output is not implemented')
|
|
94
|
+
|
|
95
|
+
def _format_raw_output(self) -> str:
|
|
96
|
+
raise NotImplementedError('Raw output is not implemented')
|
|
97
|
+
|
|
98
|
+
def _format_plain_output(self) -> str:
|
|
99
|
+
"""Format the output data into a plain text string
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
str: The formatted output data
|
|
103
|
+
"""
|
|
104
|
+
if not self.results.data:
|
|
105
|
+
msg = 'No results to present'
|
|
106
|
+
return msg
|
|
52
107
|
|
|
53
|
-
|
|
108
|
+
formatted = ''
|
|
109
|
+
for item in self.results.data:
|
|
110
|
+
formatted += f'{self._format_plain_output_item(item)}\n'
|
|
111
|
+
return formatted
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def _format_plain_output_item(item):
|
|
115
|
+
purls = item.get('purl', [])
|
|
116
|
+
licenses = item.get('licenses', [])
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
f'File: {item.get("filename")}\n'
|
|
120
|
+
f'Match type: {item.get("id")}\n'
|
|
121
|
+
f'Status: {item.get("status", "N/A")}\n'
|
|
122
|
+
f'Matched: {item.get("matched", "N/A")}\n'
|
|
123
|
+
f'Purl: {purls[0] if purls else "N/A"}\n'
|
|
124
|
+
f'License: {licenses[0].get("name", "N/A") if licenses else "N/A"}\n'
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class Results:
|
|
54
129
|
"""
|
|
55
130
|
SCANOSS Results class \n
|
|
56
131
|
Handles the parsing and filtering of the scan results
|
|
57
132
|
"""
|
|
58
133
|
|
|
59
|
-
def __init__(
|
|
134
|
+
def __init__( # noqa: PLR0913
|
|
60
135
|
self,
|
|
61
136
|
debug: bool = False,
|
|
62
137
|
trace: bool = False,
|
|
@@ -80,11 +155,17 @@ class Results(ScanossBase):
|
|
|
80
155
|
output_format (str, optional): Output format. Defaults to None.
|
|
81
156
|
"""
|
|
82
157
|
|
|
83
|
-
|
|
158
|
+
self.base = ScanossBase(debug, trace, quiet)
|
|
84
159
|
self.data = self._load_and_transform(filepath)
|
|
85
160
|
self.filters = self._load_filters(match_type=match_type, status=status)
|
|
86
|
-
self.
|
|
87
|
-
|
|
161
|
+
self.presenter = ResultsPresenter(
|
|
162
|
+
self,
|
|
163
|
+
debug=debug,
|
|
164
|
+
trace=trace,
|
|
165
|
+
quiet=quiet,
|
|
166
|
+
output_file=output_file,
|
|
167
|
+
output_format=output_format,
|
|
168
|
+
)
|
|
88
169
|
|
|
89
170
|
def load_file(self, file: str) -> Dict[str, Any]:
|
|
90
171
|
"""Load the JSON file
|
|
@@ -99,7 +180,7 @@ class Results(ScanossBase):
|
|
|
99
180
|
try:
|
|
100
181
|
return json.load(jsonfile)
|
|
101
182
|
except Exception as e:
|
|
102
|
-
self.print_stderr(f'ERROR: Problem parsing input JSON: {e}')
|
|
183
|
+
self.base.print_stderr(f'ERROR: Problem parsing input JSON: {e}')
|
|
103
184
|
|
|
104
185
|
def _load_and_transform(self, file: str) -> List[Dict[str, Any]]:
|
|
105
186
|
"""
|
|
@@ -174,8 +255,8 @@ class Results(ScanossBase):
|
|
|
174
255
|
def _validate_filter_values(filter_key: str, filter_value: List[str]):
|
|
175
256
|
if any(value not in AVAILABLE_FILTER_VALUES.get(filter_key, []) for value in filter_value):
|
|
176
257
|
valid_values = ', '.join(AVAILABLE_FILTER_VALUES.get(filter_key, []))
|
|
177
|
-
raise
|
|
178
|
-
f"ERROR: Invalid filter value '{filter_value}' for filter '{filter_key
|
|
258
|
+
raise ValueError(
|
|
259
|
+
f"ERROR: Invalid filter value '{filter_value}' for filter '{filter_key}'. "
|
|
179
260
|
f'Valid values are: {valid_values}'
|
|
180
261
|
)
|
|
181
262
|
|
|
@@ -190,103 +271,5 @@ class Results(ScanossBase):
|
|
|
190
271
|
return bool(self.data)
|
|
191
272
|
|
|
192
273
|
def present(self, output_format: str = None, output_file: str = None):
|
|
193
|
-
"""
|
|
194
|
-
|
|
195
|
-
Args:
|
|
196
|
-
output_format (str, optional): Output format. Defaults to None.
|
|
197
|
-
output_file (str, optional): Output file. Defaults to None.
|
|
198
|
-
|
|
199
|
-
Raises:
|
|
200
|
-
Exception: Invalid output format
|
|
201
|
-
|
|
202
|
-
Returns:
|
|
203
|
-
None
|
|
204
|
-
"""
|
|
205
|
-
file_path = output_file or self.output_file
|
|
206
|
-
fmt = output_format or self.output_format
|
|
207
|
-
|
|
208
|
-
if fmt and fmt not in AVAILABLE_OUTPUT_FORMATS:
|
|
209
|
-
raise Exception(
|
|
210
|
-
f"ERROR: Invalid output format '{output_format}'. Valid values are: {', '.join(AVAILABLE_OUTPUT_FORMATS)}"
|
|
211
|
-
)
|
|
212
|
-
|
|
213
|
-
if fmt == 'json':
|
|
214
|
-
return self._present_json(file_path)
|
|
215
|
-
elif fmt == 'plain':
|
|
216
|
-
return self._present_plain(file_path)
|
|
217
|
-
else:
|
|
218
|
-
return self._present_stdout()
|
|
219
|
-
|
|
220
|
-
def _present_json(self, file: str = None):
|
|
221
|
-
"""Present the results in JSON format
|
|
222
|
-
|
|
223
|
-
Args:
|
|
224
|
-
file (str, optional): Output file. Defaults to None.
|
|
225
|
-
"""
|
|
226
|
-
self.print_to_file_or_stdout(json.dumps(self._format_json_output(), indent=2), file)
|
|
227
|
-
|
|
228
|
-
def _format_json_output(self):
|
|
229
|
-
"""
|
|
230
|
-
Format the output data into a JSON object
|
|
231
|
-
"""
|
|
232
|
-
|
|
233
|
-
formatted_data = []
|
|
234
|
-
for item in self.data:
|
|
235
|
-
formatted_data.append(
|
|
236
|
-
{
|
|
237
|
-
'file': item.get('filename'),
|
|
238
|
-
'status': item.get('status', 'N/A'),
|
|
239
|
-
'match_type': item['id'],
|
|
240
|
-
'matched': item.get('matched', 'N/A'),
|
|
241
|
-
'purl': (item.get('purl')[0] if item.get('purl') else 'N/A'),
|
|
242
|
-
'license': (item.get('licenses')[0].get('name', 'N/A') if item.get('licenses') else 'N/A'),
|
|
243
|
-
}
|
|
244
|
-
)
|
|
245
|
-
return {'results': formatted_data, 'total': len(formatted_data)}
|
|
246
|
-
|
|
247
|
-
def _present_plain(self, file: str = None):
|
|
248
|
-
"""Present the results in plain text format
|
|
249
|
-
|
|
250
|
-
Args:
|
|
251
|
-
file (str, optional): Output file. Defaults to None.
|
|
252
|
-
|
|
253
|
-
Returns:
|
|
254
|
-
None
|
|
255
|
-
"""
|
|
256
|
-
if not self.data:
|
|
257
|
-
return self.print_stderr('No results to present')
|
|
258
|
-
self.print_to_file_or_stdout(self._format_plain_output(), file)
|
|
259
|
-
|
|
260
|
-
def _present_stdout(self):
|
|
261
|
-
"""Present the results to stdout
|
|
262
|
-
|
|
263
|
-
Returns:
|
|
264
|
-
None
|
|
265
|
-
"""
|
|
266
|
-
if not self.data:
|
|
267
|
-
return self.print_stderr('No results to present')
|
|
268
|
-
self.print_to_file_or_stdout(self._format_plain_output())
|
|
269
|
-
|
|
270
|
-
def _format_plain_output(self):
|
|
271
|
-
"""
|
|
272
|
-
Format the output data into a plain text string
|
|
273
|
-
"""
|
|
274
|
-
|
|
275
|
-
formatted = ''
|
|
276
|
-
for item in self.data:
|
|
277
|
-
formatted += f'{self._format_plain_output_item(item)} \n'
|
|
278
|
-
return formatted
|
|
279
|
-
|
|
280
|
-
@staticmethod
|
|
281
|
-
def _format_plain_output_item(item):
|
|
282
|
-
purls = item.get('purl', [])
|
|
283
|
-
licenses = item.get('licenses', [])
|
|
284
|
-
|
|
285
|
-
return (
|
|
286
|
-
f'File: {item.get("filename")}\n'
|
|
287
|
-
f'Match type: {item.get("id")}\n'
|
|
288
|
-
f'Status: {item.get("status", "N/A")}\n'
|
|
289
|
-
f'Matched: {item.get("matched", "N/A")}\n'
|
|
290
|
-
f'Purl: {purls[0] if purls else "N/A"}\n'
|
|
291
|
-
f'License: {licenses[0].get("name", "N/A") if licenses else "N/A"}\n'
|
|
292
|
-
)
|
|
274
|
+
"""Present the results in the selected format"""
|
|
275
|
+
self.presenter.present(output_format=output_format, output_file=output_file)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2025, 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
|
+
"""
|