scanoss 1.31.5__tar.gz → 1.32.0__tar.gz
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-1.31.5/src/scanoss.egg-info → scanoss-1.32.0}/PKG-INFO +1 -1
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/__init__.py +1 -1
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/cli.py +6 -1
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/components.py +27 -8
- scanoss-1.32.0/src/scanoss/data/build_date.txt +1 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanner.py +3 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanossapi.py +22 -24
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanossgrpc.py +196 -64
- {scanoss-1.31.5 → scanoss-1.32.0/src/scanoss.egg-info}/PKG-INFO +1 -1
- scanoss-1.31.5/src/scanoss/data/build_date.txt +0 -1
- {scanoss-1.31.5 → scanoss-1.32.0}/LICENSE +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/PACKAGE.md +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/README.md +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/pyproject.toml +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/setup.cfg +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/protoc_gen_swagger/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/protoc_gen_swagger/options/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/protoc_gen_swagger/options/annotations_pb2.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/protoc_gen_swagger/options/annotations_pb2_grpc.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/protoc_gen_swagger/options/openapiv2_pb2.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/protoc_gen_swagger/options/openapiv2_pb2_grpc.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/common/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/common/v2/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/common/v2/scanoss_common_pb2.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/common/v2/scanoss_common_pb2_grpc.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/components/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/components/v2/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/components/v2/scanoss_components_pb2.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/components/v2/scanoss_components_pb2_grpc.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/cryptography/v2/scanoss_cryptography_pb2.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/cryptography/v2/scanoss_cryptography_pb2_grpc.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/dependencies/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/dependencies/v2/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/dependencies/v2/scanoss_dependencies_pb2.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/dependencies/v2/scanoss_dependencies_pb2_grpc.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/geoprovenance/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/geoprovenance/v2/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2_grpc.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/licenses/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/licenses/v2/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/licenses/v2/scanoss_licenses_pb2.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/licenses/v2/scanoss_licenses_pb2_grpc.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/scanning/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/scanning/v2/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/scanning/v2/scanoss_scanning_pb2.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/scanning/v2/scanoss_scanning_pb2_grpc.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/semgrep/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/semgrep/v2/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/semgrep/v2/scanoss_semgrep_pb2.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/semgrep/v2/scanoss_semgrep_pb2_grpc.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/vulnerabilities/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/vulnerabilities/v2/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2_grpc.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/constants.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/cryptography.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/csvoutput.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/cyclonedx.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/data/scanoss-settings-schema.json +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/data/spdx-exceptions.json +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/data/spdx-licenses.json +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/export/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/export/dependency_track.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/file_filters.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/filecount.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/inspection/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/inspection/dependency_track/project_violation.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/inspection/policy_check.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/inspection/raw/component_summary.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/inspection/raw/copyleft.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/inspection/raw/license_summary.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/inspection/raw/raw_base.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/inspection/raw/undeclared_component.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/inspection/utils/license_utils.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/results.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scancodedeps.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanners/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanners/container_scanner.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanners/folder_hasher.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanners/scanner_config.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanners/scanner_hfh.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanoss_settings.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanossbase.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanpostprocessor.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scantype.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/services/dependency_track_service.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/spdxlite.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/threadeddependencies.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/threadedscanning.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/utils/__init__.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/utils/abstract_presenter.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/utils/crc64.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/utils/file.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/utils/simhash.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/winnowing.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss.egg-info/SOURCES.txt +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss.egg-info/dependency_links.txt +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss.egg-info/entry_points.txt +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss.egg-info/requires.txt +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss.egg-info/top_level.txt +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/tests/test_csv_output.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/tests/test_file_filters.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/tests/test_policy_inspect.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/tests/test_scan_post_processor.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/tests/test_spdxlite.py +0 -0
- {scanoss-1.31.5 → scanoss-1.32.0}/tests/test_winnowing.py +0 -0
|
@@ -308,6 +308,7 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
|
|
|
308
308
|
help='Retrieve vulnerabilities for the given components',
|
|
309
309
|
)
|
|
310
310
|
c_vulns.set_defaults(func=comp_vulns)
|
|
311
|
+
c_vulns.add_argument('--grpc', action='store_true', help='Enable gRPC support')
|
|
311
312
|
|
|
312
313
|
# Component Sub-command: component semgrep
|
|
313
314
|
c_semgrep = comp_sub.add_parser(
|
|
@@ -964,7 +965,7 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
|
|
|
964
965
|
p.add_argument(
|
|
965
966
|
'--apiurl', type=str, help='SCANOSS API URL (optional - default: https://api.osskb.org/scan/direct)'
|
|
966
967
|
)
|
|
967
|
-
p.add_argument('--
|
|
968
|
+
p.add_argument('--grpc', action='store_true', help='Enable gRPC support')
|
|
968
969
|
|
|
969
970
|
# Global Scan/Fingerprint filter options
|
|
970
971
|
for p in [p_scan, p_wfp]:
|
|
@@ -1055,6 +1056,7 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
|
|
|
1055
1056
|
type=str,
|
|
1056
1057
|
help='Headers to be sent on request (e.g., -hdr "Name: Value") - can be used multiple times',
|
|
1057
1058
|
)
|
|
1059
|
+
p.add_argument('--ignore-cert-errors', action='store_true', help='Ignore certificate errors')
|
|
1058
1060
|
|
|
1059
1061
|
# Syft options
|
|
1060
1062
|
for p in [p_cs, p_dep]:
|
|
@@ -1418,6 +1420,7 @@ def scan(parser, args): # noqa: PLR0912, PLR0915
|
|
|
1418
1420
|
strip_snippet_ids=args.strip_snippet,
|
|
1419
1421
|
scan_settings=scan_settings,
|
|
1420
1422
|
req_headers=process_req_headers(args.header),
|
|
1423
|
+
use_grpc=args.grpc
|
|
1421
1424
|
)
|
|
1422
1425
|
if args.wfp:
|
|
1423
1426
|
if not scanner.is_file_or_snippet_scan():
|
|
@@ -2144,6 +2147,8 @@ def comp_vulns(parser, args):
|
|
|
2144
2147
|
pac=pac_file,
|
|
2145
2148
|
timeout=args.timeout,
|
|
2146
2149
|
req_headers=process_req_headers(args.header),
|
|
2150
|
+
ignore_cert_errors=args.ignore_cert_errors,
|
|
2151
|
+
use_grpc=args.grpc,
|
|
2147
2152
|
)
|
|
2148
2153
|
if not comps.get_vulnerabilities(args.input, args.purl, args.output):
|
|
2149
2154
|
sys.exit(1)
|
|
@@ -52,6 +52,8 @@ class Components(ScanossBase):
|
|
|
52
52
|
ca_cert: str = None,
|
|
53
53
|
pac: PACFile = None,
|
|
54
54
|
req_headers: dict = None,
|
|
55
|
+
ignore_cert_errors: bool = False,
|
|
56
|
+
use_grpc: bool = False,
|
|
55
57
|
):
|
|
56
58
|
"""
|
|
57
59
|
Handle all component style requests
|
|
@@ -66,6 +68,9 @@ class Components(ScanossBase):
|
|
|
66
68
|
:param grpc_proxy: Specific gRPC proxy (optional)
|
|
67
69
|
:param ca_cert: TLS client certificate (optional)
|
|
68
70
|
:param pac: Proxy Auto-Config file (optional)
|
|
71
|
+
:param req_headers: Additional headers to send with requests (optional)
|
|
72
|
+
:param ignore_cert_errors: Ignore TLS certificate errors (optional)
|
|
73
|
+
:param use_grpc: Use gRPC instead of HTTP (optional)
|
|
69
74
|
"""
|
|
70
75
|
super().__init__(debug, trace, quiet)
|
|
71
76
|
ver_details = Scanner.version_details()
|
|
@@ -82,14 +87,28 @@ class Components(ScanossBase):
|
|
|
82
87
|
grpc_proxy=grpc_proxy,
|
|
83
88
|
timeout=timeout,
|
|
84
89
|
req_headers=req_headers,
|
|
90
|
+
ignore_cert_errors=ignore_cert_errors,
|
|
91
|
+
use_grpc=use_grpc,
|
|
85
92
|
)
|
|
86
93
|
|
|
87
|
-
def
|
|
94
|
+
def load_comps(self, json_file: Optional[str] = None, purls: Optional[List[str]] = None)-> Optional[dict]:
|
|
95
|
+
"""
|
|
96
|
+
Load the specified components and return a dictionary
|
|
97
|
+
|
|
98
|
+
:param json_file: JSON Components file (optional)
|
|
99
|
+
:param purls: list pf PURLs (optional)
|
|
100
|
+
:return: Components Request dictionary or None
|
|
101
|
+
"""
|
|
102
|
+
return self.load_purls(json_file, purls, 'components')
|
|
103
|
+
|
|
104
|
+
def load_purls(self, json_file: Optional[str] = None, purls: Optional[List[str]] = None, field:str = 'purls'
|
|
105
|
+
) -> Optional[dict]:
|
|
88
106
|
"""
|
|
89
107
|
Load the specified purls and return a dictionary
|
|
90
108
|
|
|
91
109
|
:param json_file: JSON PURL file (optional)
|
|
92
110
|
:param purls: list of PURLs (optional)
|
|
111
|
+
:param field: Name of the dictionary field to store the purls in (default: 'purls')
|
|
93
112
|
:return: PURL Request dictionary or None
|
|
94
113
|
"""
|
|
95
114
|
if json_file:
|
|
@@ -109,14 +128,14 @@ class Components(ScanossBase):
|
|
|
109
128
|
parsed_purls = []
|
|
110
129
|
for p in purls:
|
|
111
130
|
parsed_purls.append({'purl': p})
|
|
112
|
-
purl_request = {
|
|
131
|
+
purl_request = {field: parsed_purls}
|
|
113
132
|
else:
|
|
114
133
|
self.print_stderr('ERROR: No purls specified to process.')
|
|
115
134
|
return None
|
|
116
|
-
purl_count = len(purl_request.get(
|
|
117
|
-
self.print_debug(f'Parsed
|
|
135
|
+
purl_count = len(purl_request.get(field, []))
|
|
136
|
+
self.print_debug(f'Parsed {field} ({purl_count}): {purl_request}')
|
|
118
137
|
if purl_count == 0:
|
|
119
|
-
self.print_stderr('ERROR: No
|
|
138
|
+
self.print_stderr(f'ERROR: No {field} parsed from request.')
|
|
120
139
|
return None
|
|
121
140
|
return purl_request
|
|
122
141
|
|
|
@@ -142,8 +161,8 @@ class Components(ScanossBase):
|
|
|
142
161
|
"""
|
|
143
162
|
Open the given filename if requested, otherwise return STDOUT
|
|
144
163
|
|
|
145
|
-
:param filename:
|
|
146
|
-
:return:
|
|
164
|
+
:param filename: filename to open or None to return STDOUT
|
|
165
|
+
:return: file descriptor or None
|
|
147
166
|
"""
|
|
148
167
|
file = sys.stdout
|
|
149
168
|
if filename:
|
|
@@ -202,7 +221,7 @@ class Components(ScanossBase):
|
|
|
202
221
|
:return: True on success, False otherwise
|
|
203
222
|
"""
|
|
204
223
|
success = False
|
|
205
|
-
purls_request = self.
|
|
224
|
+
purls_request = self.load_comps(json_file, purls)
|
|
206
225
|
if purls_request is None or len(purls_request) == 0:
|
|
207
226
|
return False
|
|
208
227
|
file = self._open_file_or_sdtout(output_file)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
date: 20250901122016, utime: 1756729216
|
|
@@ -107,6 +107,7 @@ class Scanner(ScanossBase):
|
|
|
107
107
|
skip_md5_ids=None,
|
|
108
108
|
scan_settings: 'ScanossSettings | None' = None,
|
|
109
109
|
req_headers: dict = None,
|
|
110
|
+
use_grpc: bool = False,
|
|
110
111
|
):
|
|
111
112
|
"""
|
|
112
113
|
Initialise scanning class, including Winnowing, ScanossApi, ThreadedScanning
|
|
@@ -173,6 +174,8 @@ class Scanner(ScanossBase):
|
|
|
173
174
|
pac=pac,
|
|
174
175
|
grpc_proxy=grpc_proxy,
|
|
175
176
|
req_headers=self.req_headers,
|
|
177
|
+
ignore_cert_errors=ignore_cert_errors,
|
|
178
|
+
use_grpc=use_grpc
|
|
176
179
|
)
|
|
177
180
|
self.threaded_deps = ThreadedDependencies(sc_deps, grpc_api, debug=debug, quiet=quiet, trace=trace)
|
|
178
181
|
self.nb_threads = nb_threads
|
|
@@ -22,23 +22,23 @@ SPDX-License-Identifier: MIT
|
|
|
22
22
|
THE SOFTWARE.
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
|
+
import http.client as http_client
|
|
25
26
|
import logging
|
|
26
27
|
import os
|
|
27
28
|
import sys
|
|
28
29
|
import time
|
|
30
|
+
import uuid
|
|
29
31
|
from json.decoder import JSONDecodeError
|
|
32
|
+
|
|
30
33
|
import requests
|
|
31
|
-
import uuid
|
|
32
|
-
import http.client as http_client
|
|
33
34
|
import urllib3
|
|
34
|
-
|
|
35
35
|
from pypac import PACSession
|
|
36
36
|
from pypac.parser import PACFile
|
|
37
37
|
from urllib3.exceptions import InsecureRequestWarning
|
|
38
38
|
|
|
39
|
-
from .scanossbase import ScanossBase
|
|
40
39
|
from . import __version__
|
|
41
|
-
|
|
40
|
+
from .constants import DEFAULT_TIMEOUT, MIN_TIMEOUT
|
|
41
|
+
from .scanossbase import ScanossBase
|
|
42
42
|
|
|
43
43
|
DEFAULT_URL = 'https://api.osskb.org/scan/direct' # default free service URL
|
|
44
44
|
DEFAULT_URL2 = 'https://api.scanoss.com/scan/direct' # default premium service URL
|
|
@@ -52,7 +52,7 @@ class ScanossApi(ScanossBase):
|
|
|
52
52
|
Currently support posting scan requests to the SCANOSS streaming API
|
|
53
53
|
"""
|
|
54
54
|
|
|
55
|
-
def __init__( # noqa: PLR0913, PLR0915
|
|
55
|
+
def __init__( # noqa: PLR0912, PLR0913, PLR0915
|
|
56
56
|
self,
|
|
57
57
|
scan_format: str = None,
|
|
58
58
|
flags: str = None,
|
|
@@ -61,7 +61,7 @@ class ScanossApi(ScanossBase):
|
|
|
61
61
|
debug: bool = False,
|
|
62
62
|
trace: bool = False,
|
|
63
63
|
quiet: bool = False,
|
|
64
|
-
timeout: int =
|
|
64
|
+
timeout: int = DEFAULT_TIMEOUT,
|
|
65
65
|
ver_details: str = None,
|
|
66
66
|
ignore_cert_errors: bool = False,
|
|
67
67
|
proxy: str = None,
|
|
@@ -87,30 +87,28 @@ class ScanossApi(ScanossBase):
|
|
|
87
87
|
HTTPS_PROXY='http://<ip>:<port>'
|
|
88
88
|
"""
|
|
89
89
|
super().__init__(debug, trace, quiet)
|
|
90
|
-
self.url = url
|
|
91
|
-
self.api_key = api_key
|
|
92
90
|
self.sbom = None
|
|
93
91
|
self.scan_format = scan_format if scan_format else 'plain'
|
|
94
92
|
self.flags = flags
|
|
95
|
-
self.timeout = timeout if timeout >
|
|
93
|
+
self.timeout = timeout if timeout > MIN_TIMEOUT else DEFAULT_TIMEOUT
|
|
96
94
|
self.retry_limit = retry if retry >= 0 else 5
|
|
97
95
|
self.ignore_cert_errors = ignore_cert_errors
|
|
98
96
|
self.req_headers = req_headers if req_headers else {}
|
|
99
97
|
self.headers = {}
|
|
100
|
-
|
|
98
|
+
# Set the correct URL/API key combination
|
|
99
|
+
self.url = url if url else SCANOSS_SCAN_URL
|
|
100
|
+
self.api_key = api_key if api_key else SCANOSS_API_KEY
|
|
101
|
+
if self.api_key and not url and not os.environ.get('SCANOSS_SCAN_URL'):
|
|
102
|
+
self.url = DEFAULT_URL2 # API key specific and no alternative URL, so use the default premium
|
|
101
103
|
if ver_details:
|
|
102
104
|
self.headers['x-scanoss-client'] = ver_details
|
|
103
105
|
if self.api_key:
|
|
104
106
|
self.headers['X-Session'] = self.api_key
|
|
105
107
|
self.headers['x-api-key'] = self.api_key
|
|
106
|
-
|
|
107
|
-
self.headers['
|
|
108
|
-
self.
|
|
109
|
-
|
|
110
|
-
self.url = url if url else SCANOSS_SCAN_URL
|
|
111
|
-
self.api_key = api_key if api_key else SCANOSS_API_KEY
|
|
112
|
-
if self.api_key and not url and not os.environ.get('SCANOSS_SCAN_URL'):
|
|
113
|
-
self.url = DEFAULT_URL2 # API key specific and no alternative URL, so use the default premium
|
|
108
|
+
user_agent = f'scanoss-py/{__version__}'
|
|
109
|
+
self.headers['User-Agent'] = user_agent
|
|
110
|
+
self.headers['user-agent'] = user_agent
|
|
111
|
+
self.load_generic_headers(url)
|
|
114
112
|
|
|
115
113
|
if self.trace:
|
|
116
114
|
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
|
|
@@ -133,7 +131,7 @@ class ScanossApi(ScanossBase):
|
|
|
133
131
|
if self.proxies:
|
|
134
132
|
self.session.proxies = self.proxies
|
|
135
133
|
|
|
136
|
-
def scan(self, wfp: str, context: str = None, scan_id: int = None):
|
|
134
|
+
def scan(self, wfp: str, context: str = None, scan_id: int = None): # noqa: PLR0912, PLR0915
|
|
137
135
|
"""
|
|
138
136
|
Scan the specified WFP and return the JSON object
|
|
139
137
|
:param wfp: WFP to scan
|
|
@@ -192,7 +190,7 @@ class ScanossApi(ScanossBase):
|
|
|
192
190
|
else:
|
|
193
191
|
self.print_stderr(f'Warning: No response received from {self.url}. Retrying...')
|
|
194
192
|
time.sleep(5)
|
|
195
|
-
elif r.status_code ==
|
|
193
|
+
elif r.status_code == requests.codes.service_unavailable: # Service limits most likely reached
|
|
196
194
|
self.print_stderr(
|
|
197
195
|
f'ERROR: SCANOSS API rejected the scan request ({request_id}) due to '
|
|
198
196
|
f'service limits being exceeded'
|
|
@@ -202,7 +200,7 @@ class ScanossApi(ScanossBase):
|
|
|
202
200
|
f'ERROR: {r.status_code} - The SCANOSS API request ({request_id}) rejected '
|
|
203
201
|
f'for {self.url} due to service limits being exceeded.'
|
|
204
202
|
)
|
|
205
|
-
elif r.status_code >=
|
|
203
|
+
elif r.status_code >= requests.codes.bad_request:
|
|
206
204
|
if retry > self.retry_limit: # No response retry_limit or more times, fail
|
|
207
205
|
self.save_bad_req_wfp(scan_files, request_id, scan_id)
|
|
208
206
|
raise Exception(
|
|
@@ -269,7 +267,7 @@ class ScanossApi(ScanossBase):
|
|
|
269
267
|
self.sbom = sbom
|
|
270
268
|
return self
|
|
271
269
|
|
|
272
|
-
def load_generic_headers(self):
|
|
270
|
+
def load_generic_headers(self, url):
|
|
273
271
|
"""
|
|
274
272
|
Adds custom headers from req_headers to the headers collection.
|
|
275
273
|
|
|
@@ -279,7 +277,7 @@ class ScanossApi(ScanossBase):
|
|
|
279
277
|
if self.req_headers: # Load generic headers
|
|
280
278
|
for key, value in self.req_headers.items():
|
|
281
279
|
if key == 'x-api-key': # Set premium URL if x-api-key header is set
|
|
282
|
-
if not
|
|
280
|
+
if not url and not os.environ.get('SCANOSS_SCAN_URL'):
|
|
283
281
|
self.url = DEFAULT_URL2 # API key specific and no alternative URL, so use the default premium
|
|
284
282
|
self.api_key = value
|
|
285
283
|
self.headers[key] = value
|
|
@@ -23,8 +23,12 @@ SPDX-License-Identifier: MIT
|
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
25
|
import concurrent.futures
|
|
26
|
+
import http.client as http_client
|
|
26
27
|
import json
|
|
28
|
+
import logging
|
|
27
29
|
import os
|
|
30
|
+
import sys
|
|
31
|
+
import time
|
|
28
32
|
import uuid
|
|
29
33
|
from dataclasses import dataclass
|
|
30
34
|
from enum import IntEnum
|
|
@@ -32,15 +36,20 @@ from typing import Dict, Optional
|
|
|
32
36
|
from urllib.parse import urlparse
|
|
33
37
|
|
|
34
38
|
import grpc
|
|
39
|
+
import requests
|
|
40
|
+
import urllib3
|
|
35
41
|
from google.protobuf.json_format import MessageToDict, ParseDict
|
|
42
|
+
from pypac import PACSession
|
|
36
43
|
from pypac.parser import PACFile
|
|
37
44
|
from pypac.resolver import ProxyResolver
|
|
45
|
+
from urllib3.exceptions import InsecureRequestWarning
|
|
38
46
|
|
|
39
47
|
from scanoss.api.scanning.v2.scanoss_scanning_pb2_grpc import ScanningStub
|
|
40
48
|
from scanoss.constants import DEFAULT_TIMEOUT
|
|
41
49
|
|
|
42
50
|
from . import __version__
|
|
43
51
|
from .api.common.v2.scanoss_common_pb2 import (
|
|
52
|
+
ComponentsRequest,
|
|
44
53
|
EchoRequest,
|
|
45
54
|
EchoResponse,
|
|
46
55
|
PurlRequest,
|
|
@@ -62,7 +71,7 @@ from .api.geoprovenance.v2.scanoss_geoprovenance_pb2_grpc import GeoProvenanceSt
|
|
|
62
71
|
from .api.scanning.v2.scanoss_scanning_pb2 import HFHRequest
|
|
63
72
|
from .api.semgrep.v2.scanoss_semgrep_pb2 import SemgrepResponse
|
|
64
73
|
from .api.semgrep.v2.scanoss_semgrep_pb2_grpc import SemgrepStub
|
|
65
|
-
from .api.vulnerabilities.v2.scanoss_vulnerabilities_pb2 import
|
|
74
|
+
from .api.vulnerabilities.v2.scanoss_vulnerabilities_pb2 import ComponentsVulnerabilityResponse
|
|
66
75
|
from .api.vulnerabilities.v2.scanoss_vulnerabilities_pb2_grpc import VulnerabilitiesStub
|
|
67
76
|
from .scanossbase import ScanossBase
|
|
68
77
|
|
|
@@ -70,21 +79,20 @@ DEFAULT_URL = 'https://api.osskb.org' # default free service URL
|
|
|
70
79
|
DEFAULT_URL2 = 'https://api.scanoss.com' # default premium service URL
|
|
71
80
|
SCANOSS_GRPC_URL = os.environ.get('SCANOSS_GRPC_URL') if os.environ.get('SCANOSS_GRPC_URL') else DEFAULT_URL
|
|
72
81
|
SCANOSS_API_KEY = os.environ.get('SCANOSS_API_KEY') if os.environ.get('SCANOSS_API_KEY') else ''
|
|
82
|
+
DEFAULT_URI_PREFIX = '/v2'
|
|
73
83
|
|
|
74
|
-
MAX_CONCURRENT_REQUESTS = 5
|
|
84
|
+
MAX_CONCURRENT_REQUESTS = 5 # Maximum number of concurrent requests to make
|
|
75
85
|
|
|
76
86
|
|
|
77
87
|
class ScanossGrpcError(Exception):
|
|
78
88
|
"""
|
|
79
89
|
Custom exception for SCANOSS gRPC errors
|
|
80
90
|
"""
|
|
81
|
-
|
|
82
91
|
pass
|
|
83
92
|
|
|
84
93
|
|
|
85
94
|
class ScanossGrpcStatusCode(IntEnum):
|
|
86
95
|
"""Status codes for SCANOSS gRPC responses"""
|
|
87
|
-
|
|
88
96
|
SUCCESS = 1
|
|
89
97
|
SUCCESS_WITH_WARNINGS = 2
|
|
90
98
|
FAILED_WITH_WARNINGS = 3
|
|
@@ -96,7 +104,7 @@ class ScanossGrpc(ScanossBase):
|
|
|
96
104
|
Client for gRPC functionality
|
|
97
105
|
"""
|
|
98
106
|
|
|
99
|
-
def __init__( # noqa: PLR0913, PLR0915
|
|
107
|
+
def __init__( # noqa: PLR0912, PLR0913, PLR0915
|
|
100
108
|
self,
|
|
101
109
|
url: str = None,
|
|
102
110
|
debug: bool = False,
|
|
@@ -110,6 +118,8 @@ class ScanossGrpc(ScanossBase):
|
|
|
110
118
|
grpc_proxy: str = None,
|
|
111
119
|
pac: PACFile = None,
|
|
112
120
|
req_headers: dict = None,
|
|
121
|
+
ignore_cert_errors: bool = False,
|
|
122
|
+
use_grpc: bool = False,
|
|
113
123
|
):
|
|
114
124
|
"""
|
|
115
125
|
|
|
@@ -127,27 +137,55 @@ class ScanossGrpc(ScanossBase):
|
|
|
127
137
|
grpc_proxy='http://<ip>:<port>'
|
|
128
138
|
"""
|
|
129
139
|
super().__init__(debug, trace, quiet)
|
|
130
|
-
self.url = url
|
|
131
140
|
self.api_key = api_key if api_key else SCANOSS_API_KEY
|
|
132
141
|
self.timeout = timeout
|
|
133
142
|
self.proxy = proxy
|
|
134
143
|
self.grpc_proxy = grpc_proxy
|
|
135
144
|
self.pac = pac
|
|
136
|
-
self.req_headers = req_headers
|
|
137
145
|
self.metadata = []
|
|
146
|
+
self.ignore_cert_errors = ignore_cert_errors
|
|
147
|
+
self.use_grpc = use_grpc
|
|
148
|
+
self.req_headers = req_headers if req_headers else {}
|
|
149
|
+
self.headers = {}
|
|
150
|
+
self.retry_limit = 2 # default retry limit
|
|
138
151
|
|
|
139
152
|
if self.api_key:
|
|
140
153
|
self.metadata.append(('x-api-key', api_key)) # Set API key if we have one
|
|
154
|
+
self.headers['X-Session'] = self.api_key
|
|
155
|
+
self.headers['x-api-key'] = self.api_key
|
|
141
156
|
if ver_details:
|
|
142
157
|
self.metadata.append(('x-scanoss-client', ver_details))
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
158
|
+
self.headers['x-scanoss-client'] = ver_details
|
|
159
|
+
user_agent = f'scanoss-py/{__version__}'
|
|
160
|
+
self.metadata.append(('user-agent', user_agent))
|
|
161
|
+
self.headers['User-Agent'] = user_agent
|
|
162
|
+
self.headers['user-agent'] = user_agent
|
|
163
|
+
self.headers['Content-Type'] = 'application/json'
|
|
164
|
+
# Set the correct URL/API key combination
|
|
146
165
|
self.url = url if url else SCANOSS_GRPC_URL
|
|
147
166
|
if self.api_key and not url and not os.environ.get('SCANOSS_GRPC_URL'):
|
|
148
167
|
self.url = DEFAULT_URL2 # API key specific and no alternative URL, so use the default premium
|
|
168
|
+
self.load_generic_headers(url)
|
|
149
169
|
self.url = self.url.lower()
|
|
150
|
-
self.orig_url = self.url # Used for proxy lookup
|
|
170
|
+
self.orig_url = self.url.strip().rstrip('/') # Used for proxy lookup
|
|
171
|
+
# REST setup
|
|
172
|
+
if self.trace:
|
|
173
|
+
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
|
|
174
|
+
http_client.HTTPConnection.debuglevel = 1
|
|
175
|
+
if pac and not proxy: # Set up a PAC session if requested (and no proxy has been explicitly set)
|
|
176
|
+
self.print_debug('Setting up PAC session...')
|
|
177
|
+
self.session = PACSession(pac=pac)
|
|
178
|
+
else:
|
|
179
|
+
self.session = requests.sessions.Session()
|
|
180
|
+
if self.ignore_cert_errors:
|
|
181
|
+
self.print_debug('Ignoring cert errors...')
|
|
182
|
+
urllib3.disable_warnings(InsecureRequestWarning)
|
|
183
|
+
self.session.verify = False
|
|
184
|
+
elif ca_cert:
|
|
185
|
+
self.session.verify = ca_cert
|
|
186
|
+
self.proxies = {'https': proxy, 'http': proxy} if proxy else None
|
|
187
|
+
if self.proxies:
|
|
188
|
+
self.session.proxies = self.proxies
|
|
151
189
|
|
|
152
190
|
secure = True if self.url.startswith('https:') else False # Is it a secure connection?
|
|
153
191
|
if self.url.startswith('http'):
|
|
@@ -162,7 +200,7 @@ class ScanossGrpc(ScanossBase):
|
|
|
162
200
|
cert_data = ScanossGrpc._load_cert(ca_cert)
|
|
163
201
|
self.print_debug(f'Setting up (secure: {secure}) connection to {self.url}...')
|
|
164
202
|
self._get_proxy_config()
|
|
165
|
-
if secure
|
|
203
|
+
if not secure: # insecure connection
|
|
166
204
|
self.comp_search_stub = ComponentsStub(grpc.insecure_channel(self.url))
|
|
167
205
|
self.crypto_stub = CryptographyStub(grpc.insecure_channel(self.url))
|
|
168
206
|
self.dependencies_stub = DependenciesStub(grpc.insecure_channel(self.url))
|
|
@@ -206,17 +244,6 @@ class ScanossGrpc(ScanossBase):
|
|
|
206
244
|
f'ERROR: {e.__class__.__name__} Problem encountered sending gRPC message (rqId: {request_id}): {e}'
|
|
207
245
|
)
|
|
208
246
|
else:
|
|
209
|
-
# self.print_stderr(f'resp: {resp} - call: {call}')
|
|
210
|
-
# response_id = ""
|
|
211
|
-
# if not call:
|
|
212
|
-
# self.print_stderr(f'No call to leverage.')
|
|
213
|
-
# for key, value in call.trailing_metadata():
|
|
214
|
-
# print('Greeter client received trailing metadata: key=%s value=%s' % (key, value))
|
|
215
|
-
#
|
|
216
|
-
# for key, value in call.trailing_metadata():
|
|
217
|
-
# if key == 'x-response-id':
|
|
218
|
-
# response_id = value
|
|
219
|
-
# self.print_stderr(f'Response ID: {response_id}. Metadata: {call.trailing_metadata()}')
|
|
220
247
|
if resp:
|
|
221
248
|
return resp.message
|
|
222
249
|
self.print_stderr(f'ERROR: Problem sending Echo request ({message}) to {self.url}. rqId: {request_id}')
|
|
@@ -264,54 +291,70 @@ class ScanossGrpc(ScanossBase):
|
|
|
264
291
|
if not dependencies:
|
|
265
292
|
self.print_stderr('ERROR: No message supplied to send to gRPC service.')
|
|
266
293
|
return None
|
|
267
|
-
|
|
268
294
|
files_json = dependencies.get('files')
|
|
269
|
-
|
|
270
295
|
if files_json is None or len(files_json) == 0:
|
|
271
|
-
self.print_stderr('ERROR: No dependency data supplied to send to
|
|
296
|
+
self.print_stderr('ERROR: No dependency data supplied to send to decoration service.')
|
|
272
297
|
return None
|
|
273
|
-
|
|
274
|
-
def process_file(file):
|
|
275
|
-
request_id = str(uuid.uuid4())
|
|
276
|
-
try:
|
|
277
|
-
file_request = {'files': [file]}
|
|
278
|
-
|
|
279
|
-
request = ParseDict(file_request, DependencyRequest())
|
|
280
|
-
request.depth = depth
|
|
281
|
-
metadata = self.metadata[:]
|
|
282
|
-
metadata.append(('x-request-id', request_id))
|
|
283
|
-
self.print_debug(f'Sending dependency data for decoration (rqId: {request_id})...')
|
|
284
|
-
resp = self.dependencies_stub.GetDependencies(request, metadata=metadata, timeout=self.timeout)
|
|
285
|
-
|
|
286
|
-
return MessageToDict(resp, preserving_proto_field_name=True)
|
|
287
|
-
except Exception as e:
|
|
288
|
-
self.print_stderr(
|
|
289
|
-
f'ERROR: {e.__class__.__name__} Problem encountered sending gRPC message (rqId: {request_id}): {e}'
|
|
290
|
-
)
|
|
291
|
-
return None
|
|
292
|
-
|
|
293
298
|
all_responses = []
|
|
299
|
+
# determine if we are using gRPC or REST based on the use_grpc flag
|
|
300
|
+
process_file = self._process_dep_file_grpc if self.use_grpc else self._process_dep_file_rest
|
|
301
|
+
# Process the dependency files in parallel
|
|
294
302
|
with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_CONCURRENT_REQUESTS) as executor:
|
|
295
|
-
future_to_file = {executor.submit(process_file, file): file for file in files_json}
|
|
296
|
-
|
|
303
|
+
future_to_file = {executor.submit(process_file, file, depth): file for file in files_json}
|
|
297
304
|
for future in concurrent.futures.as_completed(future_to_file):
|
|
298
305
|
response = future.result()
|
|
299
306
|
if response:
|
|
300
307
|
all_responses.append(response)
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
308
|
+
# End of concurrent processing
|
|
309
|
+
success_status = 'SUCCESS'
|
|
310
|
+
merged_response = {'files': [], 'status': {'status': success_status, 'message': 'Success'}}
|
|
311
|
+
# Merge the responses
|
|
305
312
|
for response in all_responses:
|
|
306
313
|
if response:
|
|
307
314
|
if 'files' in response and len(response['files']) > 0:
|
|
308
315
|
merged_response['files'].append(response['files'][0])
|
|
309
|
-
# Overwrite the status if
|
|
310
|
-
if 'status' in response and response['status']['status'] !=
|
|
316
|
+
# Overwrite the status if any of the responses was not successful
|
|
317
|
+
if 'status' in response and response['status']['status'] != success_status:
|
|
311
318
|
merged_response['status'] = response['status']
|
|
312
319
|
return merged_response
|
|
313
320
|
|
|
321
|
+
def _process_dep_file_grpc(self, file, depth: int = 1) -> dict:
|
|
322
|
+
"""
|
|
323
|
+
Process a single file using gRPC
|
|
324
|
+
|
|
325
|
+
:param file: dependency file purls
|
|
326
|
+
:param depth: depth to search (default: 1)
|
|
327
|
+
:return: response JSON or None
|
|
328
|
+
"""
|
|
329
|
+
request_id = str(uuid.uuid4())
|
|
330
|
+
try:
|
|
331
|
+
file_request = {'files': [file]}
|
|
332
|
+
request = ParseDict(file_request, DependencyRequest())
|
|
333
|
+
request.depth = depth
|
|
334
|
+
metadata = self.metadata[:]
|
|
335
|
+
metadata.append(('x-request-id', request_id))
|
|
336
|
+
self.print_debug(f'Sending dependency data via gRPC for decoration (rqId: {request_id})...')
|
|
337
|
+
resp = self.dependencies_stub.GetDependencies(request, metadata=metadata, timeout=self.timeout)
|
|
338
|
+
return MessageToDict(resp, preserving_proto_field_name=True)
|
|
339
|
+
except Exception as e:
|
|
340
|
+
self.print_stderr(
|
|
341
|
+
f'ERROR: {e.__class__.__name__} Problem encountered sending gRPC message (rqId: {request_id}): {e}'
|
|
342
|
+
)
|
|
343
|
+
return None
|
|
344
|
+
|
|
314
345
|
def get_vulnerabilities_json(self, purls: dict) -> dict:
|
|
346
|
+
"""
|
|
347
|
+
Client function to call the rpc for Vulnerability GetVulnerabilities
|
|
348
|
+
It will either use REST (default) or gRPC depending on the use_grpc flag
|
|
349
|
+
:param purls: Message to send to the service
|
|
350
|
+
:return: Server response or None
|
|
351
|
+
"""
|
|
352
|
+
if self.use_grpc:
|
|
353
|
+
return self._get_vulnerabilities_grpc(purls)
|
|
354
|
+
else:
|
|
355
|
+
return self._get_vulnerabilities_rest(purls)
|
|
356
|
+
|
|
357
|
+
def _get_vulnerabilities_grpc(self, purls: dict) -> dict:
|
|
315
358
|
"""
|
|
316
359
|
Client function to call the rpc for Vulnerability GetVulnerabilities
|
|
317
360
|
:param purls: Message to send to the service
|
|
@@ -321,13 +364,13 @@ class ScanossGrpc(ScanossBase):
|
|
|
321
364
|
self.print_stderr('ERROR: No message supplied to send to gRPC service.')
|
|
322
365
|
return None
|
|
323
366
|
request_id = str(uuid.uuid4())
|
|
324
|
-
resp:
|
|
367
|
+
resp: ComponentsVulnerabilityResponse
|
|
325
368
|
try:
|
|
326
|
-
request = ParseDict(purls,
|
|
369
|
+
request = ParseDict(purls, ComponentsRequest()) # Parse the JSON/Dict into the purl request object
|
|
327
370
|
metadata = self.metadata[:]
|
|
328
371
|
metadata.append(('x-request-id', request_id)) # Set a Request ID
|
|
329
372
|
self.print_debug(f'Sending vulnerability data for decoration (rqId: {request_id})...')
|
|
330
|
-
resp = self.vuln_stub.
|
|
373
|
+
resp = self.vuln_stub.GetComponentsVulnerabilities(request, metadata=metadata, timeout=self.timeout)
|
|
331
374
|
except Exception as e:
|
|
332
375
|
self.print_stderr(
|
|
333
376
|
f'ERROR: {e.__class__.__name__} Problem encountered sending gRPC message (rqId: {request_id}): {e}'
|
|
@@ -462,14 +505,11 @@ class ScanossGrpc(ScanossBase):
|
|
|
462
505
|
dict: The parsed gRPC response as a dictionary, or None if something went wrong
|
|
463
506
|
"""
|
|
464
507
|
request_id = str(uuid.uuid4())
|
|
465
|
-
|
|
466
508
|
if isinstance(request_input, dict):
|
|
467
509
|
request_obj = ParseDict(request_input, request_type())
|
|
468
510
|
else:
|
|
469
511
|
request_obj = request_input
|
|
470
|
-
|
|
471
512
|
metadata = self.metadata[:] + [('x-request-id', request_id)]
|
|
472
|
-
|
|
473
513
|
self.print_debug(debug_msg.format(rqId=request_id))
|
|
474
514
|
try:
|
|
475
515
|
resp = rpc_method(request_obj, metadata=metadata, timeout=self.timeout)
|
|
@@ -477,7 +517,6 @@ class ScanossGrpc(ScanossBase):
|
|
|
477
517
|
raise ScanossGrpcError(
|
|
478
518
|
f'{e.__class__.__name__} while sending gRPC message (rqId: {request_id}): {e.details()}'
|
|
479
519
|
)
|
|
480
|
-
|
|
481
520
|
if resp and not self._check_status_response(resp.status, request_id):
|
|
482
521
|
return None
|
|
483
522
|
|
|
@@ -661,7 +700,7 @@ class ScanossGrpc(ScanossBase):
|
|
|
661
700
|
'Sending data for cryptographic versions in range decoration (rqId: {rqId})...',
|
|
662
701
|
)
|
|
663
702
|
|
|
664
|
-
def load_generic_headers(self):
|
|
703
|
+
def load_generic_headers(self, url):
|
|
665
704
|
"""
|
|
666
705
|
Adds custom headers from req_headers to metadata.
|
|
667
706
|
|
|
@@ -671,17 +710,106 @@ class ScanossGrpc(ScanossBase):
|
|
|
671
710
|
if self.req_headers: # Load generic headers
|
|
672
711
|
for key, value in self.req_headers.items():
|
|
673
712
|
if key == 'x-api-key': # Set premium URL if x-api-key header is set
|
|
674
|
-
if not
|
|
713
|
+
if not url and not os.environ.get('SCANOSS_GRPC_URL'):
|
|
675
714
|
self.url = DEFAULT_URL2 # API key specific and no alternative URL, so use the default premium
|
|
676
715
|
self.api_key = value
|
|
677
716
|
self.metadata.append((key, value))
|
|
717
|
+
self.headers[key] = value
|
|
718
|
+
|
|
719
|
+
#
|
|
720
|
+
# End of gRPC Client Functions
|
|
721
|
+
#
|
|
722
|
+
# Start of REST Client Functions
|
|
723
|
+
#
|
|
678
724
|
|
|
725
|
+
def rest_post(self, uri: str, request_id: str, data: dict) -> dict:
|
|
726
|
+
"""
|
|
727
|
+
Post the specified data to the given URI.
|
|
728
|
+
:param request_id: request id
|
|
729
|
+
:param uri: URI to post to
|
|
730
|
+
:param data: json data to post
|
|
731
|
+
:return: JSON response or None
|
|
732
|
+
"""
|
|
733
|
+
if not uri:
|
|
734
|
+
self.print_stderr('Error: Missing URI. Cannot search for project.')
|
|
735
|
+
return None
|
|
736
|
+
self.print_trace(f'Sending REST POST data to {uri}...')
|
|
737
|
+
headers = self.headers
|
|
738
|
+
headers['x-request-id'] = request_id # send a unique request id for each post
|
|
739
|
+
retry = 0 # Add some retry logic to cater for timeouts, etc.
|
|
740
|
+
while retry <= self.retry_limit:
|
|
741
|
+
retry += 1
|
|
742
|
+
try:
|
|
743
|
+
response = self.session.post(uri, headers=headers, json=data, timeout=self.timeout)
|
|
744
|
+
response.raise_for_status() # Raises an HTTPError for bad responses
|
|
745
|
+
return response.json()
|
|
746
|
+
except requests.exceptions.RequestException as e:
|
|
747
|
+
self.print_stderr(f'Error: Problem posting data to {uri}: {e}')
|
|
748
|
+
except (requests.exceptions.SSLError, requests.exceptions.ProxyError) as e:
|
|
749
|
+
self.print_stderr(f'ERROR: Exception ({e.__class__.__name__}) POSTing data - {e}.')
|
|
750
|
+
raise Exception(f'ERROR: The SCANOSS Decoration API request failed for {uri}') from e
|
|
751
|
+
except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
|
|
752
|
+
if retry > self.retry_limit: # Timed out retry_limit or more times, fail
|
|
753
|
+
self.print_stderr(f'ERROR: {e.__class__.__name__} POSTing decoration data ({request_id}): {e}')
|
|
754
|
+
raise Exception(
|
|
755
|
+
f'ERROR: The SCANOSS Decoration API request timed out ({e.__class__.__name__}) for {uri}'
|
|
756
|
+
) from e
|
|
757
|
+
else:
|
|
758
|
+
self.print_stderr(f'Warning: {e.__class__.__name__} communicating with {self.url}. Retrying...')
|
|
759
|
+
time.sleep(5)
|
|
760
|
+
except Exception as e:
|
|
761
|
+
self.print_stderr(
|
|
762
|
+
f'ERROR: Exception ({e.__class__.__name__}) POSTing data ({request_id}) to {uri}: {e}'
|
|
763
|
+
)
|
|
764
|
+
raise Exception(f'ERROR: The SCANOSS Decoration API request failed for {uri}') from e
|
|
765
|
+
return None
|
|
679
766
|
|
|
767
|
+
def _get_vulnerabilities_rest(self, purls: dict):
|
|
768
|
+
"""
|
|
769
|
+
Get the vulnerabilities for the given purls using REST API
|
|
770
|
+
:param purls: Purl Request dictionary
|
|
771
|
+
:return: Vulnerability Response, or None if the request was unsuccessful
|
|
772
|
+
"""
|
|
773
|
+
if not purls:
|
|
774
|
+
self.print_stderr('ERROR: No message supplied to send to REST decoration service.')
|
|
775
|
+
return None
|
|
776
|
+
request_id = str(uuid.uuid4())
|
|
777
|
+
self.print_debug(f'Sending data for Vulnerabilities via REST (request id: {request_id})...')
|
|
778
|
+
response = self.rest_post(f'{self.orig_url}{DEFAULT_URI_PREFIX}/vulnerabilities/components', request_id, purls)
|
|
779
|
+
self.print_trace(f'Received response for Vulnerabilities via REST (request id: {request_id}): {response}')
|
|
780
|
+
if response:
|
|
781
|
+
# Parse the JSON/Dict into the purl response
|
|
782
|
+
resp_obj = ParseDict(response, ComponentsVulnerabilityResponse(), True)
|
|
783
|
+
if resp_obj:
|
|
784
|
+
self.print_debug(f'Vulnerability Response: {resp_obj}')
|
|
785
|
+
if not self._check_status_response(resp_obj.status, request_id):
|
|
786
|
+
return None
|
|
787
|
+
del response['status']
|
|
788
|
+
return response
|
|
789
|
+
return None
|
|
790
|
+
|
|
791
|
+
def _process_dep_file_rest(self, file, depth: int = 1) -> dict:
|
|
792
|
+
"""
|
|
793
|
+
Porcess a single dependency file using REST
|
|
794
|
+
|
|
795
|
+
:param file: dependency file purls
|
|
796
|
+
:param depth: depth to search (default: 1)
|
|
797
|
+
:return: response JSON or None
|
|
798
|
+
"""
|
|
799
|
+
request_id = str(uuid.uuid4())
|
|
800
|
+
self.print_debug(f'Sending data for Dependencies via REST (request id: {request_id})...')
|
|
801
|
+
file_request = {'files': [file], 'depth': depth}
|
|
802
|
+
response = self.rest_post(f'{self.orig_url}{DEFAULT_URI_PREFIX}/dependencies/dependencies',
|
|
803
|
+
request_id, file_request
|
|
804
|
+
)
|
|
805
|
+
self.print_trace(f'Received response for Dependencies via REST (request id: {request_id}): {response}')
|
|
806
|
+
if response:
|
|
807
|
+
return response
|
|
808
|
+
return None
|
|
680
809
|
#
|
|
681
810
|
# End of ScanossGrpc Class
|
|
682
811
|
#
|
|
683
812
|
|
|
684
|
-
|
|
685
813
|
@dataclass
|
|
686
814
|
class GrpcConfig:
|
|
687
815
|
url: str = DEFAULT_URL
|
|
@@ -711,3 +839,7 @@ def create_grpc_config_from_args(args) -> GrpcConfig:
|
|
|
711
839
|
proxy=getattr(args, 'proxy', None),
|
|
712
840
|
grpc_proxy=getattr(args, 'grpc_proxy', None),
|
|
713
841
|
)
|
|
842
|
+
|
|
843
|
+
#
|
|
844
|
+
# End of GrpcConfig class
|
|
845
|
+
#
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
date: 20250827093647, utime: 1756287407
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/components/v2/scanoss_components_pb2_grpc.py
RENAMED
|
File without changes
|
{scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/cryptography/v2/scanoss_cryptography_pb2.py
RENAMED
|
File without changes
|
{scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/cryptography/v2/scanoss_cryptography_pb2_grpc.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/dependencies/v2/scanoss_dependencies_pb2.py
RENAMED
|
File without changes
|
{scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/dependencies/v2/scanoss_dependencies_pb2_grpc.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2.py
RENAMED
|
File without changes
|
{scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2_grpc.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/inspection/dependency_track/project_violation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|