scanoss 1.18.0__py3-none-any.whl → 1.19.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.
- scanoss/__init__.py +1 -1
- scanoss/cli.py +63 -79
- scanoss/cyclonedx.py +5 -0
- scanoss/data/build_date.txt +1 -1
- scanoss/data/scanoss-settings-schema.json +254 -0
- scanoss/file_filters.py +525 -0
- scanoss/scanner.py +163 -266
- scanoss/scanoss_settings.py +105 -40
- scanoss/scanpostprocessor.py +89 -81
- scanoss/utils/__init__.py +23 -0
- scanoss/utils/file.py +57 -0
- {scanoss-1.18.0.dist-info → scanoss-1.19.0.dist-info}/METADATA +6 -4
- {scanoss-1.18.0.dist-info → scanoss-1.19.0.dist-info}/RECORD +17 -13
- {scanoss-1.18.0.dist-info → scanoss-1.19.0.dist-info}/WHEEL +1 -1
- {scanoss-1.18.0.dist-info → scanoss-1.19.0.dist-info}/LICENSE +0 -0
- {scanoss-1.18.0.dist-info → scanoss-1.19.0.dist-info}/entry_points.txt +0 -0
- {scanoss-1.18.0.dist-info → scanoss-1.19.0.dist-info}/top_level.txt +0 -0
scanoss/scanoss_settings.py
CHANGED
|
@@ -26,7 +26,13 @@ import json
|
|
|
26
26
|
from pathlib import Path
|
|
27
27
|
from typing import List, TypedDict
|
|
28
28
|
|
|
29
|
+
import importlib_resources
|
|
30
|
+
from jsonschema import validate
|
|
31
|
+
|
|
29
32
|
from .scanossbase import ScanossBase
|
|
33
|
+
from .utils.file import validate_json_file
|
|
34
|
+
|
|
35
|
+
DEFAULT_SCANOSS_JSON_FILE = 'scanoss.json'
|
|
30
36
|
|
|
31
37
|
|
|
32
38
|
class BomEntry(TypedDict, total=False):
|
|
@@ -34,15 +40,46 @@ class BomEntry(TypedDict, total=False):
|
|
|
34
40
|
path: str
|
|
35
41
|
|
|
36
42
|
|
|
43
|
+
class SizeFilter(TypedDict, total=False):
|
|
44
|
+
patterns: List[str]
|
|
45
|
+
min: int
|
|
46
|
+
max: int
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ScanossSettingsError(Exception):
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _load_settings_schema() -> dict:
|
|
54
|
+
"""
|
|
55
|
+
Load the SCANOSS settings schema from a JSON file.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
dict: The parsed JSON content of the SCANOSS settings schema.
|
|
59
|
+
|
|
60
|
+
Raises:
|
|
61
|
+
ScanossSettingsError: If there is any issue in locating, reading, or parsing the JSON file
|
|
62
|
+
"""
|
|
63
|
+
try:
|
|
64
|
+
schema_path = importlib_resources.files(__name__) / 'data' / 'scanoss-settings-schema.json'
|
|
65
|
+
with importlib_resources.as_file(schema_path) as f:
|
|
66
|
+
with open(f, 'r', encoding='utf-8') as file:
|
|
67
|
+
return json.load(file)
|
|
68
|
+
except Exception as e:
|
|
69
|
+
raise ScanossSettingsError(f'ERROR: Problem parsing Scanoss Settings Schema JSON file: {e}') from e
|
|
70
|
+
|
|
71
|
+
|
|
37
72
|
class ScanossSettings(ScanossBase):
|
|
38
|
-
"""
|
|
73
|
+
"""
|
|
74
|
+
Handles the loading and parsing of the SCANOSS settings file
|
|
75
|
+
"""
|
|
39
76
|
|
|
40
77
|
def __init__(
|
|
41
78
|
self,
|
|
42
79
|
debug: bool = False,
|
|
43
80
|
trace: bool = False,
|
|
44
81
|
quiet: bool = False,
|
|
45
|
-
filepath: str = None,
|
|
82
|
+
filepath: 'str | None' = None,
|
|
46
83
|
):
|
|
47
84
|
"""
|
|
48
85
|
Args:
|
|
@@ -51,38 +88,44 @@ class ScanossSettings(ScanossBase):
|
|
|
51
88
|
quiet (bool, optional): Quiet. Defaults to False.
|
|
52
89
|
filepath (str, optional): Path to settings file. Defaults to None.
|
|
53
90
|
"""
|
|
54
|
-
|
|
55
91
|
super().__init__(debug, trace, quiet)
|
|
56
92
|
self.data = {}
|
|
57
93
|
self.settings_file_type = None
|
|
58
94
|
self.scan_type = None
|
|
59
|
-
|
|
95
|
+
self.schema = _load_settings_schema()
|
|
60
96
|
if filepath:
|
|
61
97
|
self.load_json_file(filepath)
|
|
62
98
|
|
|
63
|
-
def load_json_file(self, filepath: str):
|
|
64
|
-
"""
|
|
99
|
+
def load_json_file(self, filepath: 'str | None' = None) -> 'ScanossSettings':
|
|
100
|
+
"""
|
|
101
|
+
Load the scan settings file. If no filepath is provided, scanoss.json will be used as default.
|
|
65
102
|
|
|
66
103
|
Args:
|
|
67
104
|
filepath (str): Path to the SCANOSS settings file
|
|
68
105
|
"""
|
|
106
|
+
if not filepath:
|
|
107
|
+
filepath = DEFAULT_SCANOSS_JSON_FILE
|
|
69
108
|
json_file = Path(filepath).resolve()
|
|
70
109
|
|
|
71
|
-
if not json_file.exists():
|
|
72
|
-
self.
|
|
73
|
-
self
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
110
|
+
if filepath == DEFAULT_SCANOSS_JSON_FILE and not json_file.exists():
|
|
111
|
+
self.print_debug(f'Default settings file "{filepath}" not found. Skipping...')
|
|
112
|
+
return self
|
|
113
|
+
self.print_msg(f'Loading settings file {filepath}...')
|
|
114
|
+
|
|
115
|
+
result = validate_json_file(json_file)
|
|
116
|
+
if not result.is_valid:
|
|
117
|
+
raise ScanossSettingsError(f'Problem with settings file. {result.error}')
|
|
118
|
+
try:
|
|
119
|
+
validate(result.data, self.schema)
|
|
120
|
+
except Exception as e:
|
|
121
|
+
raise ScanossSettingsError(f'Invalid settings file. {e}') from e
|
|
122
|
+
self.data = result.data
|
|
123
|
+
self.print_debug(f'Loading scan settings from: {filepath}')
|
|
81
124
|
return self
|
|
82
125
|
|
|
83
126
|
def set_file_type(self, file_type: str):
|
|
84
|
-
"""
|
|
85
|
-
|
|
127
|
+
"""
|
|
128
|
+
Set the file type in order to support both legacy SBOM.json and new scanoss.json files
|
|
86
129
|
Args:
|
|
87
130
|
file_type (str): 'legacy' or 'new'
|
|
88
131
|
|
|
@@ -95,8 +138,8 @@ class ScanossSettings(ScanossBase):
|
|
|
95
138
|
return self
|
|
96
139
|
|
|
97
140
|
def set_scan_type(self, scan_type: str):
|
|
98
|
-
"""
|
|
99
|
-
|
|
141
|
+
"""
|
|
142
|
+
Set the scan type to support legacy SBOM.json files
|
|
100
143
|
Args:
|
|
101
144
|
scan_type (str): 'identify' or 'exclude'
|
|
102
145
|
"""
|
|
@@ -104,8 +147,8 @@ class ScanossSettings(ScanossBase):
|
|
|
104
147
|
return self
|
|
105
148
|
|
|
106
149
|
def _is_valid_sbom_file(self):
|
|
107
|
-
"""
|
|
108
|
-
|
|
150
|
+
"""
|
|
151
|
+
Check if the scan settings file is valid
|
|
109
152
|
Returns:
|
|
110
153
|
bool: True if the file is valid, False otherwise
|
|
111
154
|
"""
|
|
@@ -114,8 +157,8 @@ class ScanossSettings(ScanossBase):
|
|
|
114
157
|
return True
|
|
115
158
|
|
|
116
159
|
def _get_bom(self):
|
|
117
|
-
"""
|
|
118
|
-
|
|
160
|
+
"""
|
|
161
|
+
Get the Billing of Materials from the settings file
|
|
119
162
|
Returns:
|
|
120
163
|
dict: If using scanoss.json
|
|
121
164
|
list: If using SBOM.json
|
|
@@ -130,8 +173,8 @@ class ScanossSettings(ScanossBase):
|
|
|
130
173
|
return self.data.get('bom', {})
|
|
131
174
|
|
|
132
175
|
def get_bom_include(self) -> List[BomEntry]:
|
|
133
|
-
"""
|
|
134
|
-
|
|
176
|
+
"""
|
|
177
|
+
Get the list of components to include in the scan
|
|
135
178
|
Returns:
|
|
136
179
|
list: List of components to include in the scan
|
|
137
180
|
"""
|
|
@@ -140,8 +183,8 @@ class ScanossSettings(ScanossBase):
|
|
|
140
183
|
return self._get_bom().get('include', [])
|
|
141
184
|
|
|
142
185
|
def get_bom_remove(self) -> List[BomEntry]:
|
|
143
|
-
"""
|
|
144
|
-
|
|
186
|
+
"""
|
|
187
|
+
Get the list of components to remove from the scan
|
|
145
188
|
Returns:
|
|
146
189
|
list: List of components to remove from the scan
|
|
147
190
|
"""
|
|
@@ -150,8 +193,8 @@ class ScanossSettings(ScanossBase):
|
|
|
150
193
|
return self._get_bom().get('remove', [])
|
|
151
194
|
|
|
152
195
|
def get_bom_replace(self) -> List[BomEntry]:
|
|
153
|
-
"""
|
|
154
|
-
|
|
196
|
+
"""
|
|
197
|
+
Get the list of components to replace in the scan
|
|
155
198
|
Returns:
|
|
156
199
|
list: List of components to replace in the scan
|
|
157
200
|
"""
|
|
@@ -160,8 +203,8 @@ class ScanossSettings(ScanossBase):
|
|
|
160
203
|
return self._get_bom().get('replace', [])
|
|
161
204
|
|
|
162
205
|
def get_sbom(self):
|
|
163
|
-
"""
|
|
164
|
-
|
|
206
|
+
"""
|
|
207
|
+
Get the SBOM to be sent to the SCANOSS API
|
|
165
208
|
Returns:
|
|
166
209
|
dict: SBOM request payload
|
|
167
210
|
"""
|
|
@@ -173,8 +216,8 @@ class ScanossSettings(ScanossBase):
|
|
|
173
216
|
}
|
|
174
217
|
|
|
175
218
|
def _get_sbom_assets(self):
|
|
176
|
-
"""
|
|
177
|
-
|
|
219
|
+
"""
|
|
220
|
+
Get the SBOM assets
|
|
178
221
|
Returns:
|
|
179
222
|
List: List of SBOM assets
|
|
180
223
|
"""
|
|
@@ -191,11 +234,10 @@ class ScanossSettings(ScanossBase):
|
|
|
191
234
|
|
|
192
235
|
@staticmethod
|
|
193
236
|
def normalize_bom_entries(bom_entries) -> List[BomEntry]:
|
|
194
|
-
"""
|
|
195
|
-
|
|
237
|
+
"""
|
|
238
|
+
Normalize the BOM entries
|
|
196
239
|
Args:
|
|
197
240
|
bom_entries (List[Dict]): List of BOM entries
|
|
198
|
-
|
|
199
241
|
Returns:
|
|
200
242
|
List: Normalized BOM entries
|
|
201
243
|
"""
|
|
@@ -210,11 +252,10 @@ class ScanossSettings(ScanossBase):
|
|
|
210
252
|
|
|
211
253
|
@staticmethod
|
|
212
254
|
def _remove_duplicates(bom_entries: List[BomEntry]) -> List[BomEntry]:
|
|
213
|
-
"""
|
|
214
|
-
|
|
255
|
+
"""
|
|
256
|
+
Remove duplicate BOM entries
|
|
215
257
|
Args:
|
|
216
258
|
bom_entries (List[Dict]): List of BOM entries
|
|
217
|
-
|
|
218
259
|
Returns:
|
|
219
260
|
List: List of unique BOM entries
|
|
220
261
|
"""
|
|
@@ -226,3 +267,27 @@ class ScanossSettings(ScanossBase):
|
|
|
226
267
|
already_added.add(entry_tuple)
|
|
227
268
|
unique_entries.append(entry)
|
|
228
269
|
return unique_entries
|
|
270
|
+
|
|
271
|
+
def is_legacy(self):
|
|
272
|
+
"""Check if the settings file is legacy"""
|
|
273
|
+
return self.settings_file_type == 'legacy'
|
|
274
|
+
|
|
275
|
+
def get_skip_patterns(self, operation_type: str) -> List[str]:
|
|
276
|
+
"""
|
|
277
|
+
Get the list of patterns to skip based on the operation type
|
|
278
|
+
Args:
|
|
279
|
+
operation_type (str): Operation type
|
|
280
|
+
Returns:
|
|
281
|
+
List: List of patterns to skip
|
|
282
|
+
"""
|
|
283
|
+
return self.data.get('settings', {}).get('skip', {}).get('patterns', {}).get(operation_type, [])
|
|
284
|
+
|
|
285
|
+
def get_skip_sizes(self, operation_type: str) -> List[SizeFilter]:
|
|
286
|
+
"""
|
|
287
|
+
Get the min and max sizes to skip based on the operation type
|
|
288
|
+
Args:
|
|
289
|
+
operation_type (str): Operation type
|
|
290
|
+
Returns:
|
|
291
|
+
List: Min and max sizes to skip
|
|
292
|
+
"""
|
|
293
|
+
return self.data.get('settings', {}).get('skip', {}).get('sizes', {}).get(operation_type, [])
|
scanoss/scanpostprocessor.py
CHANGED
|
@@ -31,8 +31,52 @@ from .scanoss_settings import BomEntry, ScanossSettings
|
|
|
31
31
|
from .scanossbase import ScanossBase
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
def _get_match_type_message(result_path: str, bom_entry: BomEntry, action: str) -> str:
|
|
35
|
+
"""
|
|
36
|
+
Compose message based on match type
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
result_path (str): Path of the scan result
|
|
40
|
+
bom_entry (BomEntry): BOM entry to compare with
|
|
41
|
+
action (str): Post processing action being performed
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
str: The message to be printed
|
|
45
|
+
"""
|
|
46
|
+
if bom_entry.get('path') and bom_entry.get('purl'):
|
|
47
|
+
message = f"{action} '{result_path}'. Full match found."
|
|
48
|
+
elif bom_entry.get('purl'):
|
|
49
|
+
message = f"{action} '{result_path}'. Found PURL match."
|
|
50
|
+
else:
|
|
51
|
+
message = f"{action} '{result_path}'. Found path match."
|
|
52
|
+
return message
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _is_full_match(result_path: str, result_purls: List[str], bom_entry: BomEntry) -> bool:
|
|
56
|
+
"""
|
|
57
|
+
Check if path and purl matches fully with the bom entry
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
result_path (str): Scan result path
|
|
61
|
+
result_purls (List[str]): Scan result purls
|
|
62
|
+
bom_entry (BomEntry): BOM entry to compare with
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
bool: True if the path and purl match, False otherwise
|
|
66
|
+
"""
|
|
67
|
+
if not result_purls:
|
|
68
|
+
return False
|
|
69
|
+
return bool(
|
|
70
|
+
(bom_entry.get('purl') and bom_entry.get('path'))
|
|
71
|
+
and (bom_entry.get('path') == result_path)
|
|
72
|
+
and (bom_entry.get('purl') in result_purls)
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
34
76
|
class ScanPostProcessor(ScanossBase):
|
|
35
|
-
"""
|
|
77
|
+
"""
|
|
78
|
+
Handles post-processing of the scan results
|
|
79
|
+
"""
|
|
36
80
|
|
|
37
81
|
def __init__(
|
|
38
82
|
self,
|
|
@@ -76,21 +120,28 @@ class ScanPostProcessor(ScanossBase):
|
|
|
76
120
|
self.component_info_map[purl] = result
|
|
77
121
|
|
|
78
122
|
def post_process(self):
|
|
79
|
-
"""
|
|
123
|
+
"""
|
|
124
|
+
Post-process the scan results
|
|
80
125
|
|
|
81
126
|
Returns:
|
|
82
127
|
dict: Processed results
|
|
83
128
|
"""
|
|
129
|
+
if self.scan_settings.is_legacy():
|
|
130
|
+
self.print_stderr(
|
|
131
|
+
'Legacy settings file detected. Post-processing is not supported for legacy settings file.'
|
|
132
|
+
)
|
|
133
|
+
return self.results
|
|
84
134
|
self._remove_dismissed_files()
|
|
85
135
|
self._replace_purls()
|
|
86
136
|
return self.results
|
|
87
137
|
|
|
88
138
|
def _remove_dismissed_files(self):
|
|
89
|
-
"""
|
|
139
|
+
"""
|
|
140
|
+
Remove entries from the results based on files and/or purls specified in the SCANOSS settings file
|
|
141
|
+
"""
|
|
90
142
|
to_remove_entries = self.scan_settings.get_bom_remove()
|
|
91
143
|
if not to_remove_entries:
|
|
92
144
|
return
|
|
93
|
-
|
|
94
145
|
self.results = {
|
|
95
146
|
result_path: result
|
|
96
147
|
for result_path, result in self.results.items()
|
|
@@ -98,7 +149,9 @@ class ScanPostProcessor(ScanossBase):
|
|
|
98
149
|
}
|
|
99
150
|
|
|
100
151
|
def _replace_purls(self):
|
|
101
|
-
"""
|
|
152
|
+
"""
|
|
153
|
+
Replace purls in the results based on the SCANOSS settings file
|
|
154
|
+
"""
|
|
102
155
|
to_replace_entries = self.scan_settings.get_bom_replace()
|
|
103
156
|
if not to_replace_entries:
|
|
104
157
|
return
|
|
@@ -128,9 +181,9 @@ class ScanPostProcessor(ScanossBase):
|
|
|
128
181
|
try:
|
|
129
182
|
new_component = PackageURL.from_string(to_replace_with_purl).to_dict()
|
|
130
183
|
new_component_url = purl2url.get_repo_url(to_replace_with_purl)
|
|
131
|
-
except
|
|
184
|
+
except RuntimeError:
|
|
132
185
|
self.print_stderr(
|
|
133
|
-
f"
|
|
186
|
+
f"ERROR: Issue while replacing: Invalid PURL '{to_replace_with_purl}' in settings file. Skipping."
|
|
134
187
|
)
|
|
135
188
|
return result
|
|
136
189
|
|
|
@@ -154,13 +207,13 @@ class ScanPostProcessor(ScanossBase):
|
|
|
154
207
|
|
|
155
208
|
return result
|
|
156
209
|
|
|
157
|
-
def _should_replace_result(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
210
|
+
def _should_replace_result(self, result_path: str, result: dict, to_replace_entries: List[BomEntry]
|
|
211
|
+
) -> Tuple[bool, str]:
|
|
212
|
+
"""
|
|
213
|
+
Check if a result should be replaced based on the SCANOSS settings
|
|
161
214
|
|
|
162
215
|
Args:
|
|
163
|
-
result_path (str): Path of the result
|
|
216
|
+
result_path (str): Path of the result data
|
|
164
217
|
result (dict): Result to check
|
|
165
218
|
to_replace_entries (List[BomEntry]): BOM entries to replace from the settings file
|
|
166
219
|
|
|
@@ -176,9 +229,8 @@ class ScanPostProcessor(ScanossBase):
|
|
|
176
229
|
|
|
177
230
|
if not to_replace_path and not to_replace_purl or not to_replace_with:
|
|
178
231
|
continue
|
|
179
|
-
|
|
180
232
|
if (
|
|
181
|
-
|
|
233
|
+
_is_full_match(result_path, result_purls, to_replace_entry)
|
|
182
234
|
or (not to_replace_path and to_replace_purl in result_purls)
|
|
183
235
|
or (not to_replace_purl and to_replace_path == result_path)
|
|
184
236
|
):
|
|
@@ -188,7 +240,14 @@ class ScanPostProcessor(ScanossBase):
|
|
|
188
240
|
return False, None
|
|
189
241
|
|
|
190
242
|
def _should_remove_result(self, result_path: str, result: dict, to_remove_entries: List[BomEntry]) -> bool:
|
|
191
|
-
"""
|
|
243
|
+
"""
|
|
244
|
+
Check if a result should be removed based on the SCANOSS settings
|
|
245
|
+
|
|
246
|
+
:param result_path: path of the result data
|
|
247
|
+
:param result: result to check
|
|
248
|
+
:param to_remove_entries: BOM entries to remove from the result
|
|
249
|
+
:return:
|
|
250
|
+
"""
|
|
192
251
|
result = result[0] if isinstance(result, list) else result
|
|
193
252
|
result_purls = result.get('purl', [])
|
|
194
253
|
|
|
@@ -198,9 +257,8 @@ class ScanPostProcessor(ScanossBase):
|
|
|
198
257
|
|
|
199
258
|
if not to_remove_path and not to_remove_purl:
|
|
200
259
|
continue
|
|
201
|
-
|
|
202
260
|
if (
|
|
203
|
-
|
|
261
|
+
_is_full_match(result_path, result_purls, to_remove_entry)
|
|
204
262
|
or (not to_remove_path and to_remove_purl in result_purls)
|
|
205
263
|
or (not to_remove_purl and to_remove_path == result_path)
|
|
206
264
|
):
|
|
@@ -209,75 +267,25 @@ class ScanPostProcessor(ScanossBase):
|
|
|
209
267
|
|
|
210
268
|
return False
|
|
211
269
|
|
|
212
|
-
def _print_message(
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
270
|
+
def _print_message(self, result_path: str, result_purls: List[str], bom_entry: BomEntry, action: str) -> None:
|
|
271
|
+
"""
|
|
272
|
+
Print a message about replacing or removing a result
|
|
273
|
+
|
|
274
|
+
:param result_path:
|
|
275
|
+
:param result_purls:
|
|
276
|
+
:param bom_entry:
|
|
277
|
+
:param action:
|
|
278
|
+
:return:
|
|
279
|
+
"""
|
|
220
280
|
message = (
|
|
221
|
-
f"{
|
|
281
|
+
f"{_get_match_type_message(result_path, bom_entry, action)} \n"
|
|
222
282
|
f"Details:\n"
|
|
223
283
|
f" - PURLs: {', '.join(result_purls)}\n"
|
|
224
284
|
f" - Path: '{result_path}'\n"
|
|
225
285
|
)
|
|
226
|
-
|
|
227
286
|
if action == 'Replacing':
|
|
228
287
|
message += f" - {action} with '{bom_entry.get('replace_with')}'"
|
|
229
|
-
|
|
230
288
|
self.print_debug(message)
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
result_path: str,
|
|
235
|
-
result_purls: List[str],
|
|
236
|
-
bom_entry: BomEntry,
|
|
237
|
-
action: str,
|
|
238
|
-
) -> str:
|
|
239
|
-
"""Compose message based on match type
|
|
240
|
-
|
|
241
|
-
Args:
|
|
242
|
-
result_path (str): Path of the scan result
|
|
243
|
-
result_purls (List[str]): Purls of the scan result
|
|
244
|
-
bom_entry (BomEntry): BOM entry to compare with
|
|
245
|
-
action (str): Post processing action being performed
|
|
246
|
-
|
|
247
|
-
Returns:
|
|
248
|
-
str: The message to be printed
|
|
249
|
-
"""
|
|
250
|
-
if bom_entry.get('path') and bom_entry.get('purl'):
|
|
251
|
-
message = f"{action} '{result_path}'. Full match found."
|
|
252
|
-
elif bom_entry.get('purl'):
|
|
253
|
-
message = f"{action} '{result_path}'. Found PURL match."
|
|
254
|
-
else:
|
|
255
|
-
message = f"{action} '{result_path}'. Found path match."
|
|
256
|
-
|
|
257
|
-
return message
|
|
258
|
-
|
|
259
|
-
def _is_full_match(
|
|
260
|
-
self,
|
|
261
|
-
result_path: str,
|
|
262
|
-
result_purls: List[str],
|
|
263
|
-
bom_entry: BomEntry,
|
|
264
|
-
) -> bool:
|
|
265
|
-
"""Check if path and purl matches fully with the bom entry
|
|
266
|
-
|
|
267
|
-
Args:
|
|
268
|
-
result_path (str): Scan result path
|
|
269
|
-
result_purls (List[str]): Scan result purls
|
|
270
|
-
bom_entry (BomEntry): BOM entry to compare with
|
|
271
|
-
|
|
272
|
-
Returns:
|
|
273
|
-
bool: True if the path and purl match, False otherwise
|
|
274
|
-
"""
|
|
275
|
-
|
|
276
|
-
if not result_purls:
|
|
277
|
-
return False
|
|
278
|
-
|
|
279
|
-
return bool(
|
|
280
|
-
(bom_entry.get('purl') and bom_entry.get('path'))
|
|
281
|
-
and (bom_entry.get('path') == result_path)
|
|
282
|
-
and (bom_entry.get('purl') in result_purls)
|
|
283
|
-
)
|
|
289
|
+
#
|
|
290
|
+
# End of ScanPostProcessor Class
|
|
291
|
+
#
|
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
"""
|
scanoss/utils/file.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
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
|
+
import json
|
|
25
|
+
import os
|
|
26
|
+
import sys
|
|
27
|
+
from dataclasses import dataclass
|
|
28
|
+
from typing import Optional
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class JsonValidation:
|
|
33
|
+
is_valid: bool
|
|
34
|
+
data: Optional[dict] = None
|
|
35
|
+
error: Optional[str] = None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def validate_json_file(json_file_path: str) -> JsonValidation:
|
|
39
|
+
"""
|
|
40
|
+
Validate if the specified file is indeed a valid JSON file
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
json_file_path (str): The JSON file to validate
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Tuple[bool, str]: A tuple containing a boolean indicating if the file is valid and a message
|
|
47
|
+
"""
|
|
48
|
+
if not json_file_path:
|
|
49
|
+
return JsonValidation(is_valid=False, error='No JSON file specified')
|
|
50
|
+
if not os.path.isfile(json_file_path):
|
|
51
|
+
return JsonValidation(is_valid=False, error=f'File not found: {json_file_path}')
|
|
52
|
+
try:
|
|
53
|
+
with open(json_file_path) as f:
|
|
54
|
+
data = json.load(f)
|
|
55
|
+
return JsonValidation(is_valid=True, data=data)
|
|
56
|
+
except json.JSONDecodeError as e:
|
|
57
|
+
return JsonValidation(is_valid=False, error=f'Problem parsing JSON file: "{json_file_path}": {e}')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: scanoss
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.19.0
|
|
4
4
|
Summary: Simple Python library to leverage the SCANOSS APIs
|
|
5
5
|
Home-page: https://scanoss.com
|
|
6
6
|
Author: SCANOSS
|
|
@@ -25,10 +25,12 @@ Requires-Dist: protobuf>3.19.1
|
|
|
25
25
|
Requires-Dist: pypac
|
|
26
26
|
Requires-Dist: pyOpenSSL
|
|
27
27
|
Requires-Dist: google-api-core
|
|
28
|
-
Requires-Dist:
|
|
28
|
+
Requires-Dist: importlib_resources
|
|
29
29
|
Requires-Dist: packageurl-python
|
|
30
|
-
|
|
31
|
-
Requires-Dist:
|
|
30
|
+
Requires-Dist: pathspec
|
|
31
|
+
Requires-Dist: jsonschema
|
|
32
|
+
Provides-Extra: fast-winnowing
|
|
33
|
+
Requires-Dist: scanoss_winnowing>=0.5.0; extra == "fast-winnowing"
|
|
32
34
|
|
|
33
35
|
# SCANOSS Python Package
|
|
34
36
|
The SCANOSS python package provides a simple easy to consume library for interacting with SCANOSS APIs/Engine.
|
|
@@ -4,20 +4,21 @@ protoc_gen_swagger/options/annotations_pb2.py,sha256=b25EDD6gssUWnFby9gxgcpLIROT
|
|
|
4
4
|
protoc_gen_swagger/options/annotations_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
|
|
5
5
|
protoc_gen_swagger/options/openapiv2_pb2.py,sha256=vYElGp8E1vGHszvWqX97zNG9GFJ7u2QcdK9ouq0XdyI,14939
|
|
6
6
|
protoc_gen_swagger/options/openapiv2_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
|
|
7
|
-
scanoss/__init__.py,sha256=
|
|
8
|
-
scanoss/cli.py,sha256=
|
|
7
|
+
scanoss/__init__.py,sha256=QII--iMwPZhf6bpdwYDt75o9BhE45V7pn0t7vHxYOK4,1163
|
|
8
|
+
scanoss/cli.py,sha256=8uvO6VFDqGTw4ZLySr0xhS9KTm-gdwq9K3mYv_LdxW8,51498
|
|
9
9
|
scanoss/components.py,sha256=ZHZ1KA69shxOASZK7USD9yPTITpAc_RXL5q5zpDK23o,12590
|
|
10
10
|
scanoss/csvoutput.py,sha256=hBwr_Fc6mBdOdXgyQcdFrockYH-PJ0jblowlExJ6OPg,9925
|
|
11
|
-
scanoss/cyclonedx.py,sha256=
|
|
11
|
+
scanoss/cyclonedx.py,sha256=ZbFNinMz_R68ko4-ZPo2w4bBORpASh271VCmYYRTUCg,12746
|
|
12
|
+
scanoss/file_filters.py,sha256=HGGECJ-P0LL8pg8YVO4BP8ql24tCnanx0_S2wA8EWis,16642
|
|
12
13
|
scanoss/filecount.py,sha256=o7xb6m387ucnsU4H1OXGzf_AdWsudhAHe49T8uX4Ieo,6660
|
|
13
14
|
scanoss/results.py,sha256=7G33QAYYI9qI61TCzXjSLYXMmg5CDtZS5e2QhnQfE74,9883
|
|
14
15
|
scanoss/scancodedeps.py,sha256=_9d7MAV20-FrET7mF7gW-BZiz2eHrtwudgrEcSX0oZQ,11321
|
|
15
|
-
scanoss/scanner.py,sha256=
|
|
16
|
-
scanoss/scanoss_settings.py,sha256=
|
|
16
|
+
scanoss/scanner.py,sha256=hF8N8XXSwYw93htpqv0h0t-IqswG_bcxH7awe4JA5Aw,45374
|
|
17
|
+
scanoss/scanoss_settings.py,sha256=AJKMJeLoPVLO15vb9Pd12b5tVCa3ALP5sOXeWR6KCb0,10144
|
|
17
18
|
scanoss/scanossapi.py,sha256=TJxPctr-0DTn_26LfM__OAMfntaXzvheFTbdmU-5pnM,11953
|
|
18
19
|
scanoss/scanossbase.py,sha256=zMDRCLbrcoRvYEKQRuZXnBiVY4_Vsplmg_APbB65oaU,3084
|
|
19
20
|
scanoss/scanossgrpc.py,sha256=ythZkr6F0P0hl_KPYoHkos_IL97TxLKeYfAouX_CUnM,20491
|
|
20
|
-
scanoss/scanpostprocessor.py,sha256=
|
|
21
|
+
scanoss/scanpostprocessor.py,sha256=OY75PLUMRas7hyjrHTCKiu4X-N_Z5t710wc9f3HPHqw,11074
|
|
21
22
|
scanoss/scantype.py,sha256=R2-ExLGOrYxaJFtIK2AEo2caD0XrN1zpF5q1qT9Zsyc,1326
|
|
22
23
|
scanoss/spdxlite.py,sha256=REChAWV-6qhp16jc4X2lMb1v7VvYiDH5nN9VDV3fDaQ,15828
|
|
23
24
|
scanoss/threadeddependencies.py,sha256=sOIAjiPTmxybKz2yhT4-ixXBeC4K8UQVq6JQj4e8mLc,9906
|
|
@@ -50,7 +51,8 @@ scanoss/api/vulnerabilities/__init__.py,sha256=FLQtiDiv85Q1Chk-sJ9ky9WOV1mulZhEK
|
|
|
50
51
|
scanoss/api/vulnerabilities/v2/__init__.py,sha256=FLQtiDiv85Q1Chk-sJ9ky9WOV1mulZhEKjiBihlwiaM,1139
|
|
51
52
|
scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2.py,sha256=CFhF80av8tenGvn9AIsGEtRJPuV2dC_syA5JLZb2lDw,5464
|
|
52
53
|
scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2_grpc.py,sha256=HlS4k4Zmx6RIAqaO9I96jD-eyF5yU6Xx04pVm7pdqOg,6864
|
|
53
|
-
scanoss/data/build_date.txt,sha256=
|
|
54
|
+
scanoss/data/build_date.txt,sha256=EgEEayPVruney0IcvaC1mlb6hMwjLliofFDgvTJTtfI,40
|
|
55
|
+
scanoss/data/scanoss-settings-schema.json,sha256=ClkRYAkjAN0Sk704G8BE_Ok006oQ6YnIGmX84CF8h9w,8798
|
|
54
56
|
scanoss/data/spdx-exceptions.json,sha256=s7UTYxC7jqQXr11YBlIWYCNwN6lRDFTR33Y8rpN_dA4,17953
|
|
55
57
|
scanoss/data/spdx-licenses.json,sha256=A6Z0q82gaTLtnopBfzeIVZjJFxkdRW1g2TuumQc-lII,228794
|
|
56
58
|
scanoss/inspection/__init__.py,sha256=z62680zKq4OmBOugSODvgpSwdsloZL7bvcaMbnx3xgU,1139
|
|
@@ -58,9 +60,11 @@ scanoss/inspection/copyleft.py,sha256=dkiLkgNYz7cbIQZCzy6zThiIyHkqrper_xruZ9PQhA
|
|
|
58
60
|
scanoss/inspection/policy_check.py,sha256=eo5VfEBwKoDSqIwRi0xwaVLy6EUR29HlH5Bl0Kpvx7I,14752
|
|
59
61
|
scanoss/inspection/undeclared_component.py,sha256=c2xwWyrPtQbdl0MgHTN_CpaJwC1PAofqyfYBSuWt_yI,7965
|
|
60
62
|
scanoss/inspection/utils/license_utils.py,sha256=mIaoVWXMA6shkRQmgmiP2mWchjxX4ex8LWs91Nf6rq4,5093
|
|
61
|
-
scanoss
|
|
62
|
-
scanoss
|
|
63
|
-
scanoss-1.
|
|
64
|
-
scanoss-1.
|
|
65
|
-
scanoss-1.
|
|
66
|
-
scanoss-1.
|
|
63
|
+
scanoss/utils/__init__.py,sha256=0hjb5ktavp7utJzFhGMPImPaZiHWgilM2HwvTp5lXJE,1122
|
|
64
|
+
scanoss/utils/file.py,sha256=W-XFLgaTM_td31Y5rzd5DimO4_qXafQhBtKtY_p3JIQ,2184
|
|
65
|
+
scanoss-1.19.0.dist-info/LICENSE,sha256=LLUaXoiyOroIbr5ubAyrxBOwSRLTm35ETO2FmLpy8QQ,1074
|
|
66
|
+
scanoss-1.19.0.dist-info/METADATA,sha256=ElWs851Eb75AWscKw-8hKuZ4V68DgkMCaW8cBZ_qmyM,6019
|
|
67
|
+
scanoss-1.19.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
68
|
+
scanoss-1.19.0.dist-info/entry_points.txt,sha256=Uy28xnaDL5KQ7V77sZD5VLDXPNxYYzSr5tsqtiXVzAs,48
|
|
69
|
+
scanoss-1.19.0.dist-info/top_level.txt,sha256=V11PrQ6Pnrc-nDF9xnisnJ8e6-i7HqSIKVNqduRWcL8,27
|
|
70
|
+
scanoss-1.19.0.dist-info/RECORD,,
|