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/results.py
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
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 typing import Any, Dict, List
|
|
27
|
+
|
|
28
|
+
from scanoss.utils.abstract_presenter import AbstractPresenter
|
|
29
|
+
|
|
30
|
+
from .scanossbase import ScanossBase
|
|
31
|
+
|
|
32
|
+
MATCH_TYPES = ['file', 'snippet']
|
|
33
|
+
STATUSES = ['pending', 'identified']
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
AVAILABLE_FILTER_VALUES = {
|
|
37
|
+
'match_type': [e for e in MATCH_TYPES],
|
|
38
|
+
'status': [e for e in STATUSES],
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
ARG_TO_FILTER_MAP = {
|
|
43
|
+
'match_type': 'id',
|
|
44
|
+
'status': 'status',
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
PENDING_IDENTIFICATION_FILTERS = {
|
|
48
|
+
'match_type': ['file', 'snippet'],
|
|
49
|
+
'status': ['pending'],
|
|
50
|
+
}
|
|
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
|
|
107
|
+
|
|
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:
|
|
129
|
+
"""
|
|
130
|
+
SCANOSS Results class \n
|
|
131
|
+
Handles the parsing and filtering of the scan results
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
def __init__( # noqa: PLR0913
|
|
135
|
+
self,
|
|
136
|
+
debug: bool = False,
|
|
137
|
+
trace: bool = False,
|
|
138
|
+
quiet: bool = False,
|
|
139
|
+
filepath: str = None,
|
|
140
|
+
match_type: str = None,
|
|
141
|
+
status: str = None,
|
|
142
|
+
output_file: str = None,
|
|
143
|
+
output_format: str = None,
|
|
144
|
+
):
|
|
145
|
+
"""Initialise the Results class
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
debug (bool, optional): Debug. Defaults to False.
|
|
149
|
+
trace (bool, optional): Trace. Defaults to False.
|
|
150
|
+
quiet (bool, optional): Quiet. Defaults to False.
|
|
151
|
+
filepath (str, optional): Path to the scan results file. Defaults to None.
|
|
152
|
+
match_type (str, optional): Comma separated match type filters. Defaults to None.
|
|
153
|
+
status (str, optional): Comma separated status filters. Defaults to None.
|
|
154
|
+
output_file (str, optional): Path to the output file. Defaults to None.
|
|
155
|
+
output_format (str, optional): Output format. Defaults to None.
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
self.base = ScanossBase(debug, trace, quiet)
|
|
159
|
+
self.data = self._load_and_transform(filepath)
|
|
160
|
+
self.filters = self._load_filters(match_type=match_type, status=status)
|
|
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
|
+
)
|
|
169
|
+
|
|
170
|
+
def load_file(self, file: str) -> Dict[str, Any]:
|
|
171
|
+
"""Load the JSON file
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
file (str): Path to the JSON file
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
Dict[str, Any]: The parsed JSON data
|
|
178
|
+
"""
|
|
179
|
+
with open(file, 'r') as jsonfile:
|
|
180
|
+
try:
|
|
181
|
+
return json.load(jsonfile)
|
|
182
|
+
except Exception as e:
|
|
183
|
+
self.base.print_stderr(f'ERROR: Problem parsing input JSON: {e}')
|
|
184
|
+
|
|
185
|
+
def _load_and_transform(self, file: str) -> List[Dict[str, Any]]:
|
|
186
|
+
"""
|
|
187
|
+
Load the file and transform the data into a list of dictionaries with the filename and the file data
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
raw_data = self.load_file(file)
|
|
191
|
+
return self._transform_data(raw_data)
|
|
192
|
+
|
|
193
|
+
@staticmethod
|
|
194
|
+
def _transform_data(data: dict) -> list:
|
|
195
|
+
"""Transform the data into a list of dictionaries with the filename and the file data
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
data (dict): The raw data
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
list: The transformed data
|
|
202
|
+
"""
|
|
203
|
+
result = []
|
|
204
|
+
for filename, file_data in data.items():
|
|
205
|
+
if file_data:
|
|
206
|
+
file_obj = {'filename': filename}
|
|
207
|
+
file_obj.update(file_data[0])
|
|
208
|
+
result.append(file_obj)
|
|
209
|
+
return result
|
|
210
|
+
|
|
211
|
+
def _load_filters(self, **kwargs):
|
|
212
|
+
"""Extract and parse the filters
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
dict: Parsed filters
|
|
216
|
+
"""
|
|
217
|
+
filters = {}
|
|
218
|
+
|
|
219
|
+
for key, value in kwargs.items():
|
|
220
|
+
if value:
|
|
221
|
+
filters[key] = self._extract_comma_separated_values(value)
|
|
222
|
+
|
|
223
|
+
return filters
|
|
224
|
+
|
|
225
|
+
@staticmethod
|
|
226
|
+
def _extract_comma_separated_values(values: str):
|
|
227
|
+
return [value.strip() for value in values.split(',')]
|
|
228
|
+
|
|
229
|
+
def apply_filters(self):
|
|
230
|
+
"""Apply the filters to the data"""
|
|
231
|
+
filtered_data = []
|
|
232
|
+
for item in self.data:
|
|
233
|
+
if self._item_matches_filters(item):
|
|
234
|
+
filtered_data.append(item)
|
|
235
|
+
self.data = filtered_data
|
|
236
|
+
|
|
237
|
+
return self
|
|
238
|
+
|
|
239
|
+
def _item_matches_filters(self, item):
|
|
240
|
+
for filter_key, filter_values in self.filters.items():
|
|
241
|
+
if not filter_values:
|
|
242
|
+
continue
|
|
243
|
+
|
|
244
|
+
self._validate_filter_values(filter_key, filter_values)
|
|
245
|
+
|
|
246
|
+
item_value = item.get(ARG_TO_FILTER_MAP[filter_key])
|
|
247
|
+
if isinstance(filter_values, list):
|
|
248
|
+
if item_value not in filter_values:
|
|
249
|
+
return False
|
|
250
|
+
elif item_value != filter_values:
|
|
251
|
+
return False
|
|
252
|
+
return True
|
|
253
|
+
|
|
254
|
+
@staticmethod
|
|
255
|
+
def _validate_filter_values(filter_key: str, filter_value: List[str]):
|
|
256
|
+
if any(value not in AVAILABLE_FILTER_VALUES.get(filter_key, []) for value in filter_value):
|
|
257
|
+
valid_values = ', '.join(AVAILABLE_FILTER_VALUES.get(filter_key, []))
|
|
258
|
+
raise ValueError(
|
|
259
|
+
f"ERROR: Invalid filter value '{filter_value}' for filter '{filter_key}'. "
|
|
260
|
+
f'Valid values are: {valid_values}'
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
def get_pending_identifications(self):
|
|
264
|
+
"""Get files with 'pending' status and 'file' or 'snippet' match type"""
|
|
265
|
+
self.filters = PENDING_IDENTIFICATION_FILTERS
|
|
266
|
+
self.apply_filters()
|
|
267
|
+
|
|
268
|
+
return self
|
|
269
|
+
|
|
270
|
+
def has_results(self):
|
|
271
|
+
return bool(self.data)
|
|
272
|
+
|
|
273
|
+
def present(self, output_format: str = None, output_file: str = None):
|
|
274
|
+
"""Present the results in the selected format"""
|
|
275
|
+
self.presenter.present(output_format=output_format, output_file=output_file)
|
scanoss/scancodedeps.py
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
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
24
|
|
|
25
25
|
import json
|
|
@@ -33,8 +33,17 @@ class ScancodeDeps(ScanossBase):
|
|
|
33
33
|
"""
|
|
34
34
|
SCANOSS dependency scanning class
|
|
35
35
|
"""
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
|
|
37
|
+
def __init__(
|
|
38
|
+
self,
|
|
39
|
+
debug: bool = False,
|
|
40
|
+
quiet: bool = False,
|
|
41
|
+
trace: bool = False,
|
|
42
|
+
output_file: str = None,
|
|
43
|
+
scan_output: str = None,
|
|
44
|
+
timeout: int = 600,
|
|
45
|
+
sc_command: str = None,
|
|
46
|
+
):
|
|
38
47
|
"""
|
|
39
48
|
Initialise ScancodeDeps class
|
|
40
49
|
"""
|
|
@@ -54,7 +63,7 @@ class ScancodeDeps(ScanossBase):
|
|
|
54
63
|
if not outfile and self.scan_output:
|
|
55
64
|
outfile = self.scan_output
|
|
56
65
|
if outfile:
|
|
57
|
-
with open(outfile,
|
|
66
|
+
with open(outfile, 'a') as rf:
|
|
58
67
|
rf.write(string + '\n')
|
|
59
68
|
else:
|
|
60
69
|
print(string)
|
|
@@ -85,7 +94,7 @@ class ScancodeDeps(ScanossBase):
|
|
|
85
94
|
self.print_debug(f'Processing Scancode results into Dependency data...')
|
|
86
95
|
files = []
|
|
87
96
|
for t in data:
|
|
88
|
-
if t == 'files':
|
|
97
|
+
if t == 'files': # Only interested in 'files' details
|
|
89
98
|
files_details = data.get(t)
|
|
90
99
|
if not files_details or files_details == '':
|
|
91
100
|
continue
|
|
@@ -105,32 +114,40 @@ class ScancodeDeps(ScanossBase):
|
|
|
105
114
|
continue
|
|
106
115
|
self.print_debug(f'Path: {f_path}, Packages: {len(f_packages)}')
|
|
107
116
|
purls = []
|
|
117
|
+
scopes = []
|
|
108
118
|
for pkgs in f_packages:
|
|
109
119
|
pk_deps = pkgs.get('dependencies')
|
|
120
|
+
|
|
110
121
|
if not pk_deps or pk_deps == '':
|
|
111
122
|
continue
|
|
112
|
-
self.print_debug(f'Path: {f_path}, Dependencies: {len(pk_deps)}')
|
|
113
123
|
for d in pk_deps:
|
|
114
124
|
dp = d.get('purl')
|
|
115
125
|
if not dp or dp == '':
|
|
116
126
|
continue
|
|
127
|
+
|
|
117
128
|
dp = dp.replace('"', '').replace('%22', '') # remove unwanted quotes on purls
|
|
118
129
|
dp_data = {'purl': dp}
|
|
119
130
|
rq = d.get('extracted_requirement') # scancode format 2.0
|
|
120
131
|
if not rq or rq == '':
|
|
121
|
-
rq = d.get('requirement')
|
|
132
|
+
rq = d.get('requirement') # scancode format 1.0
|
|
122
133
|
# skip requirement if it ends with the purl (i.e. exact version) or if it's local (file)
|
|
123
134
|
if rq and rq != '' and not dp.endswith(rq) and not rq.startswith('file:'):
|
|
124
135
|
dp_data['requirement'] = rq
|
|
136
|
+
|
|
137
|
+
# Gets dependency scope
|
|
138
|
+
scope = d.get('scope')
|
|
139
|
+
if scope and scope != '':
|
|
140
|
+
dp_data['scope'] = scope
|
|
141
|
+
|
|
125
142
|
purls.append(dp_data)
|
|
126
|
-
#
|
|
143
|
+
# end for loop
|
|
144
|
+
|
|
127
145
|
if len(purls) > 0:
|
|
128
146
|
files.append({'file': f_path, 'purls': purls})
|
|
129
147
|
# End packages
|
|
130
148
|
# End file details
|
|
131
149
|
# End dependencies json
|
|
132
150
|
deps = {'files': files}
|
|
133
|
-
# self.print_debug(f'Dep Data: {deps}')
|
|
134
151
|
return deps
|
|
135
152
|
|
|
136
153
|
def produce_from_file(self, json_file: str = None) -> json:
|
|
@@ -179,6 +196,7 @@ class ScancodeDeps(ScanossBase):
|
|
|
179
196
|
return False
|
|
180
197
|
self.print_msg('Producing summary...')
|
|
181
198
|
deps = self.produce_from_file(output_file)
|
|
199
|
+
deps = self.__remove_dep_scope(deps)
|
|
182
200
|
self.remove_interim_file(output_file)
|
|
183
201
|
if not deps:
|
|
184
202
|
return False
|
|
@@ -196,17 +214,32 @@ class ScancodeDeps(ScanossBase):
|
|
|
196
214
|
output_file = self.output_file
|
|
197
215
|
try:
|
|
198
216
|
open(output_file, 'w').close()
|
|
199
|
-
self.print_trace(
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
217
|
+
self.print_trace(
|
|
218
|
+
f'About to execute {self.sc_command} -p --only-findings --quiet --json {output_file} {what_to_scan}'
|
|
219
|
+
)
|
|
220
|
+
result = subprocess.run(
|
|
221
|
+
[
|
|
222
|
+
self.sc_command,
|
|
223
|
+
'-p',
|
|
224
|
+
'--only-findings',
|
|
225
|
+
'--quiet',
|
|
226
|
+
'--strip-root',
|
|
227
|
+
'--json',
|
|
228
|
+
output_file,
|
|
229
|
+
what_to_scan,
|
|
230
|
+
],
|
|
231
|
+
cwd=os.getcwd(),
|
|
232
|
+
stdout=subprocess.PIPE,
|
|
233
|
+
stderr=subprocess.STDOUT,
|
|
234
|
+
text=True,
|
|
235
|
+
timeout=self.timeout,
|
|
236
|
+
)
|
|
206
237
|
self.print_trace(f'Subprocess return: {result}')
|
|
207
238
|
if result.returncode:
|
|
208
|
-
self.print_stderr(
|
|
209
|
-
|
|
239
|
+
self.print_stderr(
|
|
240
|
+
f'ERROR: Scancode dependency scan of {what_to_scan} failed with exit code'
|
|
241
|
+
f' {result.returncode}:\n{result.stdout}'
|
|
242
|
+
)
|
|
210
243
|
return False
|
|
211
244
|
except subprocess.TimeoutExpired as e:
|
|
212
245
|
self.print_stderr(f'ERROR: Timed out attempting to run scancode dependency scan on {what_to_scan}: {e}')
|
|
@@ -235,6 +268,22 @@ class ScancodeDeps(ScanossBase):
|
|
|
235
268
|
self.print_stderr(f'ERROR: Problem loading input JSON: {e}')
|
|
236
269
|
return None
|
|
237
270
|
|
|
271
|
+
@staticmethod
|
|
272
|
+
def __remove_dep_scope(deps: json) -> json:
|
|
273
|
+
"""
|
|
274
|
+
:param deps: dependencies with scopes
|
|
275
|
+
:return dependencies without scopes
|
|
276
|
+
"""
|
|
277
|
+
files = deps.get('files')
|
|
278
|
+
for file in files:
|
|
279
|
+
if 'purls' in file:
|
|
280
|
+
purls = file.get('purls')
|
|
281
|
+
for purl in purls:
|
|
282
|
+
purl.pop('scope', None)
|
|
283
|
+
|
|
284
|
+
return {'files': files}
|
|
285
|
+
|
|
286
|
+
|
|
238
287
|
#
|
|
239
288
|
# End of ScancodeDeps Class
|
|
240
289
|
#
|