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/scanossgrpc.py
CHANGED
|
@@ -1,57 +1,137 @@
|
|
|
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
|
+
import concurrent.futures
|
|
26
|
+
import http.client as http_client
|
|
27
|
+
import logging
|
|
25
28
|
import os
|
|
29
|
+
import sys
|
|
30
|
+
import time
|
|
26
31
|
import uuid
|
|
32
|
+
from dataclasses import dataclass
|
|
33
|
+
from enum import Enum, IntEnum
|
|
34
|
+
from typing import Dict, Optional
|
|
35
|
+
from urllib.parse import urlparse
|
|
27
36
|
|
|
28
37
|
import grpc
|
|
29
|
-
import
|
|
30
|
-
|
|
38
|
+
import requests
|
|
39
|
+
import urllib3
|
|
31
40
|
from google.protobuf.json_format import MessageToDict, ParseDict
|
|
41
|
+
from pypac import PACSession
|
|
32
42
|
from pypac.parser import PACFile
|
|
33
43
|
from pypac.resolver import ProxyResolver
|
|
34
|
-
from
|
|
44
|
+
from urllib3.exceptions import InsecureRequestWarning
|
|
35
45
|
|
|
46
|
+
from scanoss.api.licenses.v2.scanoss_licenses_pb2_grpc import LicenseStub
|
|
47
|
+
from scanoss.api.scanning.v2.scanoss_scanning_pb2_grpc import ScanningStub
|
|
48
|
+
from scanoss.constants import DEFAULT_TIMEOUT
|
|
49
|
+
|
|
50
|
+
from . import __version__
|
|
51
|
+
from .api.common.v2.scanoss_common_pb2 import (
|
|
52
|
+
ComponentsRequest,
|
|
53
|
+
EchoRequest,
|
|
54
|
+
StatusCode,
|
|
55
|
+
StatusResponse,
|
|
56
|
+
)
|
|
57
|
+
from .api.components.v2.scanoss_components_pb2 import (
|
|
58
|
+
CompSearchRequest,
|
|
59
|
+
CompVersionRequest,
|
|
60
|
+
)
|
|
36
61
|
from .api.components.v2.scanoss_components_pb2_grpc import ComponentsStub
|
|
37
62
|
from .api.cryptography.v2.scanoss_cryptography_pb2_grpc import CryptographyStub
|
|
63
|
+
from .api.dependencies.v2.scanoss_dependencies_pb2 import DependencyRequest
|
|
38
64
|
from .api.dependencies.v2.scanoss_dependencies_pb2_grpc import DependenciesStub
|
|
39
|
-
from .api.
|
|
65
|
+
from .api.geoprovenance.v2.scanoss_geoprovenance_pb2_grpc import GeoProvenanceStub
|
|
66
|
+
from .api.scanning.v2.scanoss_scanning_pb2 import HFHRequest
|
|
40
67
|
from .api.semgrep.v2.scanoss_semgrep_pb2_grpc import SemgrepStub
|
|
41
|
-
from .api.
|
|
42
|
-
from .api.dependencies.v2.scanoss_dependencies_pb2 import DependencyRequest, DependencyResponse
|
|
43
|
-
from .api.common.v2.scanoss_common_pb2 import EchoRequest, EchoResponse, StatusResponse, StatusCode, PurlRequest
|
|
44
|
-
from .api.vulnerabilities.v2.scanoss_vulnerabilities_pb2 import VulnerabilityResponse
|
|
45
|
-
from .api.semgrep.v2.scanoss_semgrep_pb2 import SemgrepResponse
|
|
46
|
-
from .api.components.v2.scanoss_components_pb2 import (CompSearchRequest, CompSearchResponse,
|
|
47
|
-
CompVersionRequest, CompVersionResponse)
|
|
68
|
+
from .api.vulnerabilities.v2.scanoss_vulnerabilities_pb2_grpc import VulnerabilitiesStub
|
|
48
69
|
from .scanossbase import ScanossBase
|
|
49
|
-
from . import __version__
|
|
50
70
|
|
|
51
|
-
DEFAULT_URL =
|
|
52
|
-
DEFAULT_URL2 =
|
|
53
|
-
SCANOSS_GRPC_URL = os.environ.get(
|
|
54
|
-
SCANOSS_API_KEY = os.environ.get(
|
|
71
|
+
DEFAULT_URL = 'https://api.osskb.org' # default free service URL
|
|
72
|
+
DEFAULT_URL2 = 'https://api.scanoss.com' # default premium service URL
|
|
73
|
+
SCANOSS_GRPC_URL = os.environ.get('SCANOSS_GRPC_URL') if os.environ.get('SCANOSS_GRPC_URL') else DEFAULT_URL
|
|
74
|
+
SCANOSS_API_KEY = os.environ.get('SCANOSS_API_KEY') if os.environ.get('SCANOSS_API_KEY') else ''
|
|
75
|
+
DEFAULT_URI_PREFIX = '/v2'
|
|
76
|
+
|
|
77
|
+
MAX_CONCURRENT_REQUESTS = 5 # Maximum number of concurrent requests to make
|
|
78
|
+
|
|
79
|
+
# REST API endpoint mappings with HTTP methods
|
|
80
|
+
REST_ENDPOINTS = {
|
|
81
|
+
'vulnerabilities.GetComponentsVulnerabilities': {'path': '/vulnerabilities/components', 'method': 'POST'},
|
|
82
|
+
'dependencies.Echo': {'path': '/dependencies/echo', 'method': 'POST'},
|
|
83
|
+
'dependencies.GetDependencies': {'path': '/dependencies/dependencies', 'method': 'POST'},
|
|
84
|
+
'cryptography.Echo': {'path': '/cryptography/echo', 'method': 'POST'},
|
|
85
|
+
'cryptography.GetComponentsAlgorithms': {'path': '/cryptography/algorithms/components', 'method': 'POST'},
|
|
86
|
+
'cryptography.GetComponentsAlgorithmsInRange': {
|
|
87
|
+
'path': '/cryptography/algorithms/range/components',
|
|
88
|
+
'method': 'POST',
|
|
89
|
+
},
|
|
90
|
+
'cryptography.GetComponentsEncryptionHints': {'path': '/cryptography/hints/components', 'method': 'POST'},
|
|
91
|
+
'cryptography.GetComponentsHintsInRange': {'path': '/cryptography/hints/components/range', 'method': 'POST'},
|
|
92
|
+
'cryptography.GetComponentsVersionsInRange': {
|
|
93
|
+
'path': '/cryptography/algorithms/versions/range/components',
|
|
94
|
+
'method': 'POST',
|
|
95
|
+
},
|
|
96
|
+
'components.SearchComponents': {'path': '/components/search', 'method': 'GET'},
|
|
97
|
+
'components.GetComponentVersions': {'path': '/components/versions', 'method': 'GET'},
|
|
98
|
+
'geoprovenance.GetCountryContributorsByComponents': {
|
|
99
|
+
'path': '/geoprovenance/countries/components',
|
|
100
|
+
'method': 'POST',
|
|
101
|
+
},
|
|
102
|
+
'geoprovenance.GetOriginByComponents': {'path': '/geoprovenance/origin/components', 'method': 'POST'},
|
|
103
|
+
'licenses.GetComponentsLicenses': {'path': '/licenses/components', 'method': 'POST'},
|
|
104
|
+
'semgrep.GetComponentsIssues': {'path': '/semgrep/issues/components', 'method': 'POST'},
|
|
105
|
+
'scanning.FolderHashScan': {'path': '/scanning/hfh/scan', 'method': 'POST'},
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class ScanossGrpcError(Exception):
|
|
110
|
+
"""
|
|
111
|
+
Custom exception for SCANOSS gRPC errors
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class ScanossGrpcStatusCode(IntEnum):
|
|
118
|
+
"""Status codes for SCANOSS gRPC responses"""
|
|
119
|
+
|
|
120
|
+
UNSPECIFIED = 0
|
|
121
|
+
SUCCESS = 1
|
|
122
|
+
SUCCEEDED_WITH_WARNINGS = 2
|
|
123
|
+
WARNING = 3
|
|
124
|
+
FAILED = 4
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class ScanossRESTStatusCode(Enum):
|
|
128
|
+
"""Status codes for SCANOSS REST responses"""
|
|
129
|
+
|
|
130
|
+
UNSPECIFIED = 'UNSPECIFIED'
|
|
131
|
+
SUCCESS = 'SUCCESS'
|
|
132
|
+
SUCCEEDED_WITH_WARNINGS = 'SUCCEEDED_WITH_WARNINGS'
|
|
133
|
+
WARNING = 'WARNING'
|
|
134
|
+
FAILED = 'FAILED'
|
|
55
135
|
|
|
56
136
|
|
|
57
137
|
class ScanossGrpc(ScanossBase):
|
|
@@ -59,9 +139,23 @@ class ScanossGrpc(ScanossBase):
|
|
|
59
139
|
Client for gRPC functionality
|
|
60
140
|
"""
|
|
61
141
|
|
|
62
|
-
def __init__(
|
|
63
|
-
|
|
64
|
-
|
|
142
|
+
def __init__( # noqa: PLR0912, PLR0913, PLR0915
|
|
143
|
+
self,
|
|
144
|
+
url: Optional[str] = None,
|
|
145
|
+
debug: bool = False,
|
|
146
|
+
trace: bool = False,
|
|
147
|
+
quiet: bool = False,
|
|
148
|
+
ca_cert: Optional[str] = None,
|
|
149
|
+
api_key: Optional[str] = None,
|
|
150
|
+
ver_details: Optional[str] = None,
|
|
151
|
+
timeout: int = 600,
|
|
152
|
+
proxy: Optional[str] = None,
|
|
153
|
+
grpc_proxy: Optional[str] = None,
|
|
154
|
+
pac: Optional[PACFile] = None,
|
|
155
|
+
req_headers: Optional[dict] = None,
|
|
156
|
+
ignore_cert_errors: bool = False,
|
|
157
|
+
use_grpc: Optional[bool] = False,
|
|
158
|
+
):
|
|
65
159
|
"""
|
|
66
160
|
|
|
67
161
|
:param url:
|
|
@@ -78,22 +172,56 @@ class ScanossGrpc(ScanossBase):
|
|
|
78
172
|
grpc_proxy='http://<ip>:<port>'
|
|
79
173
|
"""
|
|
80
174
|
super().__init__(debug, trace, quiet)
|
|
81
|
-
self.url = url if url else SCANOSS_GRPC_URL
|
|
82
175
|
self.api_key = api_key if api_key else SCANOSS_API_KEY
|
|
83
|
-
if self.api_key and not url and not os.environ.get("SCANOSS_GRPC_URL"):
|
|
84
|
-
self.url = DEFAULT_URL2 # API key specific and no alternative URL, so use the default premium
|
|
85
|
-
self.url = self.url.lower()
|
|
86
|
-
self.orig_url = self.url # Used for proxy lookup
|
|
87
176
|
self.timeout = timeout
|
|
88
177
|
self.proxy = proxy
|
|
89
178
|
self.grpc_proxy = grpc_proxy
|
|
90
179
|
self.pac = pac
|
|
91
180
|
self.metadata = []
|
|
181
|
+
self.ignore_cert_errors = ignore_cert_errors
|
|
182
|
+
self.use_grpc = use_grpc
|
|
183
|
+
self.req_headers = req_headers if req_headers else {}
|
|
184
|
+
self.headers = {}
|
|
185
|
+
self.retry_limit = 2 # default retry limit
|
|
186
|
+
|
|
92
187
|
if self.api_key:
|
|
93
188
|
self.metadata.append(('x-api-key', api_key)) # Set API key if we have one
|
|
189
|
+
self.headers['X-Session'] = self.api_key
|
|
190
|
+
self.headers['x-api-key'] = self.api_key
|
|
94
191
|
if ver_details:
|
|
95
192
|
self.metadata.append(('x-scanoss-client', ver_details))
|
|
96
|
-
|
|
193
|
+
self.headers['x-scanoss-client'] = ver_details
|
|
194
|
+
user_agent = f'scanoss-py/{__version__}'
|
|
195
|
+
self.metadata.append(('user-agent', user_agent))
|
|
196
|
+
self.headers['User-Agent'] = user_agent
|
|
197
|
+
self.headers['user-agent'] = user_agent
|
|
198
|
+
self.headers['Content-Type'] = 'application/json'
|
|
199
|
+
# Set the correct URL/API key combination
|
|
200
|
+
self.url = url if url else SCANOSS_GRPC_URL
|
|
201
|
+
if self.api_key and not url and not os.environ.get('SCANOSS_GRPC_URL'):
|
|
202
|
+
self.url = DEFAULT_URL2 # API key specific and no alternative URL, so use the default premium
|
|
203
|
+
self.load_generic_headers(url)
|
|
204
|
+
self.url = self.url.lower()
|
|
205
|
+
self.orig_url = self.url.strip().rstrip('/') # Used for proxy lookup
|
|
206
|
+
# REST setup
|
|
207
|
+
if self.trace:
|
|
208
|
+
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
|
|
209
|
+
http_client.HTTPConnection.debuglevel = 1
|
|
210
|
+
if pac and not proxy: # Set up a PAC session if requested (and no proxy has been explicitly set)
|
|
211
|
+
self.print_debug('Setting up PAC session...')
|
|
212
|
+
self.session = PACSession(pac=pac)
|
|
213
|
+
else:
|
|
214
|
+
self.session = requests.sessions.Session()
|
|
215
|
+
if self.ignore_cert_errors:
|
|
216
|
+
self.print_debug('Ignoring cert errors...')
|
|
217
|
+
urllib3.disable_warnings(InsecureRequestWarning)
|
|
218
|
+
self.session.verify = False
|
|
219
|
+
elif ca_cert:
|
|
220
|
+
self.session.verify = ca_cert
|
|
221
|
+
self.proxies = {'https': proxy, 'http': proxy} if proxy else None
|
|
222
|
+
if self.proxies:
|
|
223
|
+
self.session.proxies = self.proxies
|
|
224
|
+
|
|
97
225
|
secure = True if self.url.startswith('https:') else False # Is it a secure connection?
|
|
98
226
|
if self.url.startswith('http'):
|
|
99
227
|
u = urlparse(self.url)
|
|
@@ -107,12 +235,15 @@ class ScanossGrpc(ScanossBase):
|
|
|
107
235
|
cert_data = ScanossGrpc._load_cert(ca_cert)
|
|
108
236
|
self.print_debug(f'Setting up (secure: {secure}) connection to {self.url}...')
|
|
109
237
|
self._get_proxy_config()
|
|
110
|
-
if secure
|
|
238
|
+
if not secure: # insecure connection
|
|
111
239
|
self.comp_search_stub = ComponentsStub(grpc.insecure_channel(self.url))
|
|
112
240
|
self.crypto_stub = CryptographyStub(grpc.insecure_channel(self.url))
|
|
113
241
|
self.dependencies_stub = DependenciesStub(grpc.insecure_channel(self.url))
|
|
114
242
|
self.semgrep_stub = SemgrepStub(grpc.insecure_channel(self.url))
|
|
115
243
|
self.vuln_stub = VulnerabilitiesStub(grpc.insecure_channel(self.url))
|
|
244
|
+
self.provenance_stub = GeoProvenanceStub(grpc.insecure_channel(self.url))
|
|
245
|
+
self.scanning_stub = ScanningStub(grpc.insecure_channel(self.url))
|
|
246
|
+
self.license_stub = LicenseStub(grpc.insecure_channel(self.url))
|
|
116
247
|
else:
|
|
117
248
|
if ca_cert is not None:
|
|
118
249
|
credentials = grpc.ssl_channel_credentials(cert_data) # secure with specified certificate
|
|
@@ -123,68 +254,34 @@ class ScanossGrpc(ScanossBase):
|
|
|
123
254
|
self.dependencies_stub = DependenciesStub(grpc.secure_channel(self.url, credentials))
|
|
124
255
|
self.semgrep_stub = SemgrepStub(grpc.secure_channel(self.url, credentials))
|
|
125
256
|
self.vuln_stub = VulnerabilitiesStub(grpc.secure_channel(self.url, credentials))
|
|
257
|
+
self.provenance_stub = GeoProvenanceStub(grpc.secure_channel(self.url, credentials))
|
|
258
|
+
self.scanning_stub = ScanningStub(grpc.secure_channel(self.url, credentials))
|
|
259
|
+
self.license_stub = LicenseStub(grpc.secure_channel(self.url, credentials))
|
|
126
260
|
|
|
127
261
|
@classmethod
|
|
128
262
|
def _load_cert(cls, cert_file: str) -> bytes:
|
|
129
263
|
with open(cert_file, 'rb') as f:
|
|
130
264
|
return f.read()
|
|
131
265
|
|
|
132
|
-
def deps_echo(self, message: str = 'Hello there!') ->
|
|
266
|
+
def deps_echo(self, message: str = 'Hello there!') -> Optional[dict]:
|
|
133
267
|
"""
|
|
134
268
|
Send Echo message to the Dependency service
|
|
135
269
|
:param self:
|
|
136
270
|
:param message: Message to send (default: Hello there!)
|
|
137
271
|
:return: echo or None
|
|
138
272
|
"""
|
|
139
|
-
|
|
140
|
-
resp: EchoResponse
|
|
141
|
-
try:
|
|
142
|
-
metadata = self.metadata[:]
|
|
143
|
-
metadata.append(('x-request-id', request_id)) # Set a Request ID
|
|
144
|
-
resp = self.dependencies_stub.Echo(EchoRequest(message=message), metadata=metadata, timeout=3)
|
|
145
|
-
except Exception as e:
|
|
146
|
-
self.print_stderr(f'ERROR: {e.__class__.__name__} Problem encountered sending gRPC message '
|
|
147
|
-
f'(rqId: {request_id}): {e}')
|
|
148
|
-
else:
|
|
149
|
-
# self.print_stderr(f'resp: {resp} - call: {call}')
|
|
150
|
-
# response_id = ""
|
|
151
|
-
# if not call:
|
|
152
|
-
# self.print_stderr(f'No call to leverage.')
|
|
153
|
-
# for key, value in call.trailing_metadata():
|
|
154
|
-
# print('Greeter client received trailing metadata: key=%s value=%s' % (key, value))
|
|
155
|
-
#
|
|
156
|
-
# for key, value in call.trailing_metadata():
|
|
157
|
-
# if key == 'x-response-id':
|
|
158
|
-
# response_id = value
|
|
159
|
-
# self.print_stderr(f'Response ID: {response_id}. Metadata: {call.trailing_metadata()}')
|
|
160
|
-
if resp:
|
|
161
|
-
return resp.message
|
|
162
|
-
self.print_stderr(f'ERROR: Problem sending Echo request ({message}) to {self.url}. rqId: {request_id}')
|
|
163
|
-
return None
|
|
273
|
+
return self._call_api('dependencies.Echo', self.dependencies_stub.Echo, {'message': message}, EchoRequest)
|
|
164
274
|
|
|
165
|
-
def crypto_echo(self, message: str = 'Hello there!') ->
|
|
275
|
+
def crypto_echo(self, message: str = 'Hello there!') -> Optional[dict]:
|
|
166
276
|
"""
|
|
167
277
|
Send Echo message to the Cryptography service
|
|
168
278
|
:param self:
|
|
169
279
|
:param message: Message to send (default: Hello there!)
|
|
170
280
|
:return: echo or None
|
|
171
281
|
"""
|
|
172
|
-
|
|
173
|
-
resp: EchoResponse
|
|
174
|
-
try:
|
|
175
|
-
metadata = self.metadata[:]
|
|
176
|
-
metadata.append(('x-request-id', request_id)) # Set a Request ID
|
|
177
|
-
resp = self.crypto_stub.Echo(EchoRequest(message=message), metadata=metadata, timeout=3)
|
|
178
|
-
except Exception as e:
|
|
179
|
-
self.print_stderr(f'ERROR: {e.__class__.__name__} Problem encountered sending gRPC message '
|
|
180
|
-
f'(rqId: {request_id}): {e}')
|
|
181
|
-
else:
|
|
182
|
-
if resp:
|
|
183
|
-
return resp.message
|
|
184
|
-
self.print_stderr(f'ERROR: Problem sending Echo request ({message}) to {self.url}. rqId: {request_id}')
|
|
185
|
-
return None
|
|
282
|
+
return self._call_api('cryptography.Echo', self.crypto_stub.Echo, {'message': message}, EchoRequest)
|
|
186
283
|
|
|
187
|
-
def get_dependencies(self, dependencies:
|
|
284
|
+
def get_dependencies(self, dependencies: Optional[dict] = None, depth: int = 1) -> Optional[dict]:
|
|
188
285
|
if not dependencies:
|
|
189
286
|
self.print_stderr('ERROR: No dependency data supplied to submit to the API.')
|
|
190
287
|
return None
|
|
@@ -193,7 +290,7 @@ class ScanossGrpc(ScanossBase):
|
|
|
193
290
|
self.print_stderr(f'ERROR: No response for dependency request: {dependencies}')
|
|
194
291
|
return resp
|
|
195
292
|
|
|
196
|
-
def get_dependencies_json(self, dependencies: dict, depth: int = 1) -> dict:
|
|
293
|
+
def get_dependencies_json(self, dependencies: dict, depth: int = 1) -> Optional[dict]:
|
|
197
294
|
"""
|
|
198
295
|
Client function to call the rpc for GetDependencies
|
|
199
296
|
:param dependencies: Message to send to the service
|
|
@@ -201,197 +298,293 @@ class ScanossGrpc(ScanossBase):
|
|
|
201
298
|
:return: Server response or None
|
|
202
299
|
"""
|
|
203
300
|
if not dependencies:
|
|
204
|
-
self.print_stderr(
|
|
301
|
+
self.print_stderr('ERROR: No message supplied to send to gRPC service.')
|
|
205
302
|
return None
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
303
|
+
files_json = dependencies.get('files')
|
|
304
|
+
if files_json is None or len(files_json) == 0:
|
|
305
|
+
self.print_stderr('ERROR: No dependency data supplied to send to decoration service.')
|
|
306
|
+
return None
|
|
307
|
+
all_responses = []
|
|
308
|
+
# Process the dependency files in parallel
|
|
309
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_CONCURRENT_REQUESTS) as executor:
|
|
310
|
+
future_to_file = {
|
|
311
|
+
executor.submit(self._process_dep_file, file, depth, self.use_grpc): file for file in files_json
|
|
312
|
+
}
|
|
313
|
+
for future in concurrent.futures.as_completed(future_to_file):
|
|
314
|
+
response = future.result()
|
|
315
|
+
if response:
|
|
316
|
+
all_responses.append(response)
|
|
317
|
+
# End of concurrent processing
|
|
318
|
+
success_status = 'SUCCESS'
|
|
319
|
+
merged_response = {'files': [], 'status': {'status': success_status, 'message': 'Success'}}
|
|
320
|
+
# Merge the responses
|
|
321
|
+
for response in all_responses:
|
|
322
|
+
if response:
|
|
323
|
+
if 'files' in response and len(response['files']) > 0:
|
|
324
|
+
merged_response['files'].append(response['files'][0])
|
|
325
|
+
# Overwrite the status if any of the responses was not successful
|
|
326
|
+
if 'status' in response and response['status']['status'] != success_status:
|
|
327
|
+
merged_response['status'] = response['status']
|
|
328
|
+
return merged_response
|
|
228
329
|
|
|
229
|
-
def
|
|
330
|
+
def _process_dep_file(self, file, depth: int = 1, use_grpc: Optional[bool] = None) -> Optional[dict]:
|
|
230
331
|
"""
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
:
|
|
332
|
+
Process a single dependency file using either gRPC or REST
|
|
333
|
+
|
|
334
|
+
Args:
|
|
335
|
+
file: dependency file purls
|
|
336
|
+
depth: depth to search (default: 1)
|
|
337
|
+
use_grpc: Whether to use gRPC or REST (None = use instance default)
|
|
338
|
+
|
|
339
|
+
Returns:
|
|
340
|
+
response JSON or None
|
|
234
341
|
"""
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
resp = self.crypto_stub.GetAlgorithms(request, metadata=metadata, timeout=self.timeout)
|
|
246
|
-
except Exception as e:
|
|
247
|
-
self.print_stderr(f'ERROR: {e.__class__.__name__} Problem encountered sending gRPC message '
|
|
248
|
-
f'(rqId: {request_id}): {e}')
|
|
249
|
-
else:
|
|
250
|
-
if resp:
|
|
251
|
-
if not self._check_status_response(resp.status, request_id):
|
|
252
|
-
return None
|
|
253
|
-
resp_dict = MessageToDict(resp, preserving_proto_field_name=True) # Convert gRPC response to a dict
|
|
254
|
-
del resp_dict['status']
|
|
255
|
-
return resp_dict
|
|
256
|
-
return None
|
|
342
|
+
file_request = {'files': [file], 'depth': depth}
|
|
343
|
+
|
|
344
|
+
return self._call_api(
|
|
345
|
+
'dependencies.GetDependencies',
|
|
346
|
+
self.dependencies_stub.GetDependencies,
|
|
347
|
+
file_request,
|
|
348
|
+
DependencyRequest,
|
|
349
|
+
'Sending dependency data for decoration (rqId: {rqId})...',
|
|
350
|
+
use_grpc=use_grpc,
|
|
351
|
+
)
|
|
257
352
|
|
|
258
|
-
def get_vulnerabilities_json(self, purls: dict) -> dict:
|
|
353
|
+
def get_vulnerabilities_json(self, purls: Optional[dict] = None, use_grpc: Optional[bool] = None) -> Optional[dict]:
|
|
259
354
|
"""
|
|
260
355
|
Client function to call the rpc for Vulnerability GetVulnerabilities
|
|
261
|
-
|
|
262
|
-
|
|
356
|
+
It will either use REST (default) or gRPC
|
|
357
|
+
|
|
358
|
+
Args:
|
|
359
|
+
purls (dict): Message to send to the service
|
|
360
|
+
|
|
361
|
+
Returns:
|
|
362
|
+
Server response or None
|
|
263
363
|
"""
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
metadata.append(('x-request-id', request_id)) # Set a Request ID
|
|
273
|
-
self.print_debug(f'Sending crypto data for decoration (rqId: {request_id})...')
|
|
274
|
-
resp = self.vuln_stub.GetVulnerabilities(request, metadata=metadata, timeout=self.timeout)
|
|
275
|
-
except Exception as e:
|
|
276
|
-
self.print_stderr(f'ERROR: {e.__class__.__name__} Problem encountered sending gRPC message '
|
|
277
|
-
f'(rqId: {request_id}): {e}')
|
|
278
|
-
else:
|
|
279
|
-
if resp:
|
|
280
|
-
if not self._check_status_response(resp.status, request_id):
|
|
281
|
-
return None
|
|
282
|
-
resp_dict = MessageToDict(resp, preserving_proto_field_name=True) # Convert gRPC response to a dict
|
|
283
|
-
del resp_dict['status']
|
|
284
|
-
return resp_dict
|
|
285
|
-
return None
|
|
364
|
+
return self._call_api(
|
|
365
|
+
'vulnerabilities.GetComponentsVulnerabilities',
|
|
366
|
+
self.vuln_stub.GetComponentsVulnerabilities,
|
|
367
|
+
purls,
|
|
368
|
+
ComponentsRequest,
|
|
369
|
+
'Sending vulnerability data for decoration (rqId: {rqId})...',
|
|
370
|
+
use_grpc=use_grpc,
|
|
371
|
+
)
|
|
286
372
|
|
|
287
|
-
def get_semgrep_json(self, purls: dict) -> dict:
|
|
373
|
+
def get_semgrep_json(self, purls: Optional[dict] = None, use_grpc: Optional[bool] = None) -> Optional[dict]:
|
|
288
374
|
"""
|
|
289
375
|
Client function to call the rpc for Semgrep GetIssues
|
|
290
|
-
|
|
291
|
-
:
|
|
376
|
+
|
|
377
|
+
Args:
|
|
378
|
+
purls (dict): Message to send to the service
|
|
379
|
+
use_grpc (bool): Whether to use gRPC or REST
|
|
380
|
+
|
|
381
|
+
Returns:
|
|
382
|
+
Server response or None
|
|
292
383
|
"""
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
metadata.append(('x-request-id', request_id)) # Set a Request ID
|
|
302
|
-
self.print_debug(f'Sending semgrep data for decoration (rqId: {request_id})...')
|
|
303
|
-
resp = self.semgrep_stub.GetIssues(request, metadata=metadata, timeout=self.timeout)
|
|
304
|
-
except Exception as e:
|
|
305
|
-
self.print_stderr(f'ERROR: {e.__class__.__name__} Problem encountered sending gRPC message '
|
|
306
|
-
f'(rqId: {request_id}): {e}')
|
|
307
|
-
else:
|
|
308
|
-
if resp:
|
|
309
|
-
if not self._check_status_response(resp.status, request_id):
|
|
310
|
-
return None
|
|
311
|
-
resp_dict = MessageToDict(resp, preserving_proto_field_name=True) # Convert gRPC response to a dict
|
|
312
|
-
del resp_dict['status']
|
|
313
|
-
return resp_dict
|
|
314
|
-
return None
|
|
384
|
+
return self._call_api(
|
|
385
|
+
'semgrep.GetComponentsIssues',
|
|
386
|
+
self.semgrep_stub.GetComponentsIssues,
|
|
387
|
+
purls,
|
|
388
|
+
ComponentsRequest,
|
|
389
|
+
'Sending semgrep data for decoration (rqId: {rqId})...',
|
|
390
|
+
use_grpc=use_grpc,
|
|
391
|
+
)
|
|
315
392
|
|
|
316
|
-
def search_components_json(self, search: dict) -> dict:
|
|
393
|
+
def search_components_json(self, search: dict, use_grpc: Optional[bool] = None) -> Optional[dict]:
|
|
317
394
|
"""
|
|
318
395
|
Client function to call the rpc for Components SearchComponents
|
|
319
|
-
|
|
320
|
-
:
|
|
396
|
+
|
|
397
|
+
Args:
|
|
398
|
+
search (dict): Message to send to the service
|
|
399
|
+
Returns:
|
|
400
|
+
Server response or None
|
|
321
401
|
"""
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
metadata.append(('x-request-id', request_id)) # Set a Request ID
|
|
331
|
-
self.print_debug(f'Sending component search data (rqId: {request_id})...')
|
|
332
|
-
resp = self.comp_search_stub.SearchComponents(request, metadata=metadata, timeout=self.timeout)
|
|
333
|
-
except Exception as e:
|
|
334
|
-
self.print_stderr(f'ERROR: {e.__class__.__name__} Problem encountered sending gRPC message '
|
|
335
|
-
f'(rqId: {request_id}): {e}')
|
|
336
|
-
else:
|
|
337
|
-
if resp:
|
|
338
|
-
if not self._check_status_response(resp.status, request_id):
|
|
339
|
-
return None
|
|
340
|
-
resp_dict = MessageToDict(resp, preserving_proto_field_name=True) # Convert gRPC response to a dict
|
|
341
|
-
del resp_dict['status']
|
|
342
|
-
return resp_dict
|
|
343
|
-
return None
|
|
402
|
+
return self._call_api(
|
|
403
|
+
'components.SearchComponents',
|
|
404
|
+
self.comp_search_stub.SearchComponents,
|
|
405
|
+
search,
|
|
406
|
+
CompSearchRequest,
|
|
407
|
+
'Sending component search data for decoration (rqId: {rqId})...',
|
|
408
|
+
use_grpc=use_grpc,
|
|
409
|
+
)
|
|
344
410
|
|
|
345
|
-
def get_component_versions_json(self, search: dict) -> dict:
|
|
411
|
+
def get_component_versions_json(self, search: dict, use_grpc: Optional[bool] = None) -> Optional[dict]:
|
|
346
412
|
"""
|
|
347
413
|
Client function to call the rpc for Components GetComponentVersions
|
|
348
|
-
|
|
349
|
-
:
|
|
414
|
+
|
|
415
|
+
Args:
|
|
416
|
+
search (dict): Message to send to the service
|
|
417
|
+
Returns:
|
|
418
|
+
Server response or None
|
|
419
|
+
"""
|
|
420
|
+
return self._call_api(
|
|
421
|
+
'components.GetComponentVersions',
|
|
422
|
+
self.comp_search_stub.GetComponentVersions,
|
|
423
|
+
search,
|
|
424
|
+
CompVersionRequest,
|
|
425
|
+
'Sending component version data for decoration (rqId: {rqId})...',
|
|
426
|
+
use_grpc=use_grpc,
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
def folder_hash_scan(self, request: Dict, use_grpc: Optional[bool] = None) -> Optional[Dict]:
|
|
430
|
+
"""
|
|
431
|
+
Client function to call the rpc for Folder Hashing Scan
|
|
432
|
+
|
|
433
|
+
Args:
|
|
434
|
+
request (Dict): Folder Hash Request
|
|
435
|
+
use_grpc (Optional[bool]): Whether to use gRPC or REST API
|
|
436
|
+
|
|
437
|
+
Returns:
|
|
438
|
+
Optional[Dict]: Folder Hash Response, or None if the request was not succesfull
|
|
350
439
|
"""
|
|
351
|
-
|
|
352
|
-
|
|
440
|
+
return self._call_api(
|
|
441
|
+
'scanning.FolderHashScan',
|
|
442
|
+
self.scanning_stub.FolderHashScan,
|
|
443
|
+
request,
|
|
444
|
+
HFHRequest,
|
|
445
|
+
'Sending folder hash scan data (rqId: {rqId})...',
|
|
446
|
+
use_grpc=use_grpc,
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
def _call_api(
|
|
450
|
+
self,
|
|
451
|
+
endpoint_key: str,
|
|
452
|
+
rpc_method,
|
|
453
|
+
request_input,
|
|
454
|
+
request_type,
|
|
455
|
+
debug_msg: Optional[str] = None,
|
|
456
|
+
use_grpc: Optional[bool] = None,
|
|
457
|
+
) -> Optional[Dict]:
|
|
458
|
+
"""
|
|
459
|
+
Unified method to call either gRPC or REST API based on configuration
|
|
460
|
+
|
|
461
|
+
Args:
|
|
462
|
+
endpoint_key (str): The key to lookup the REST endpoint in REST_ENDPOINTS
|
|
463
|
+
rpc_method: The gRPC stub method (used only if use_grpc is True)
|
|
464
|
+
request_input: Either a dict or a gRPC request object
|
|
465
|
+
request_type: The type of the gRPC request object (used only if use_grpc is True)
|
|
466
|
+
debug_msg (str, optional): Debug message template that can include {rqId} placeholder
|
|
467
|
+
use_grpc (bool, optional): Override the instance's use_grpc setting. If None, uses self.use_grpc
|
|
468
|
+
|
|
469
|
+
Returns:
|
|
470
|
+
dict: The parsed response as a dictionary, or None if something went wrong
|
|
471
|
+
"""
|
|
472
|
+
if not request_input:
|
|
473
|
+
self.print_stderr('ERROR: No message supplied to send to service.')
|
|
353
474
|
return None
|
|
475
|
+
|
|
476
|
+
# Determine whether to use gRPC or REST
|
|
477
|
+
use_grpc_flag = use_grpc if use_grpc is not None else self.use_grpc
|
|
478
|
+
|
|
479
|
+
if use_grpc_flag:
|
|
480
|
+
return self._call_rpc(rpc_method, request_input, request_type, debug_msg)
|
|
481
|
+
else:
|
|
482
|
+
# For REST, we only need the dict input
|
|
483
|
+
if not isinstance(request_input, dict):
|
|
484
|
+
request_input = MessageToDict(request_input, preserving_proto_field_name=True)
|
|
485
|
+
return self._call_rest(endpoint_key, request_input, debug_msg)
|
|
486
|
+
|
|
487
|
+
def _call_rpc(self, rpc_method, request_input, request_type, debug_msg: Optional[str] = None) -> Optional[Dict]:
|
|
488
|
+
"""
|
|
489
|
+
Call a gRPC method and return the response as a dictionary
|
|
490
|
+
|
|
491
|
+
Args:
|
|
492
|
+
rpc_method (): The gRPC stub method
|
|
493
|
+
request_input (): Either a dict or a gRPC request object.
|
|
494
|
+
request_type (): The type of the gRPC request object.
|
|
495
|
+
debug_msg (str, optional): Debug message template that can include {rqId} placeholder.
|
|
496
|
+
|
|
497
|
+
Returns:
|
|
498
|
+
dict: The parsed gRPC response as a dictionary, or None if something went wrong
|
|
499
|
+
"""
|
|
354
500
|
request_id = str(uuid.uuid4())
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
request = ParseDict(search, CompVersionRequest()) # Parse the JSON/Dict into the purl request object
|
|
358
|
-
metadata = self.metadata[:]
|
|
359
|
-
metadata.append(('x-request-id', request_id)) # Set a Request ID
|
|
360
|
-
self.print_debug(f'Sending component version data (rqId: {request_id})...')
|
|
361
|
-
resp = self.comp_search_stub.GetComponentVersions(request, metadata=metadata, timeout=self.timeout)
|
|
362
|
-
except Exception as e:
|
|
363
|
-
self.print_stderr(f'ERROR: {e.__class__.__name__} Problem encountered sending gRPC message '
|
|
364
|
-
f'(rqId: {request_id}): {e}')
|
|
501
|
+
if isinstance(request_input, dict):
|
|
502
|
+
request_obj = ParseDict(request_input, request_type())
|
|
365
503
|
else:
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
504
|
+
request_obj = request_input
|
|
505
|
+
metadata = self.metadata[:] + [('x-request-id', request_id)]
|
|
506
|
+
if debug_msg:
|
|
507
|
+
self.print_debug(debug_msg.format(rqId=request_id))
|
|
508
|
+
try:
|
|
509
|
+
resp = rpc_method(request_obj, metadata=metadata, timeout=self.timeout)
|
|
510
|
+
except grpc.RpcError as e:
|
|
511
|
+
raise ScanossGrpcError(
|
|
512
|
+
f'{e.__class__.__name__} while sending gRPC message (rqId: {request_id}): {e.details()}'
|
|
513
|
+
)
|
|
514
|
+
if resp and not self._check_status_response_grpc(resp.status, request_id):
|
|
515
|
+
return None
|
|
516
|
+
|
|
517
|
+
resp_dict = MessageToDict(resp, preserving_proto_field_name=True)
|
|
518
|
+
return resp_dict
|
|
373
519
|
|
|
374
|
-
def
|
|
520
|
+
def _check_status_response_grpc(self, status_response: StatusResponse, request_id: str = None) -> bool:
|
|
375
521
|
"""
|
|
376
522
|
Check the response object to see if the command was successful or not
|
|
377
523
|
:param status_response: Status Response
|
|
378
524
|
:return: True if successful, False otherwise
|
|
379
525
|
"""
|
|
526
|
+
|
|
380
527
|
if not status_response:
|
|
381
528
|
self.print_stderr(f'Warning: No status response supplied (rqId: {request_id}). Assuming it was ok.')
|
|
382
529
|
return True
|
|
383
530
|
self.print_debug(f'Checking response status (rqId: {request_id}): {status_response}')
|
|
384
531
|
status_code: StatusCode = status_response.status
|
|
385
|
-
if status_code >
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
532
|
+
if status_code > ScanossGrpcStatusCode.SUCCESS:
|
|
533
|
+
ret_val = False # default to failed
|
|
534
|
+
msg = 'Unsuccessful'
|
|
535
|
+
if status_code == ScanossGrpcStatusCode.SUCCEEDED_WITH_WARNINGS:
|
|
536
|
+
msg = 'Succeeded with warnings'
|
|
537
|
+
ret_val = True # No need to fail as it succeeded with warnings
|
|
538
|
+
elif status_code == ScanossGrpcStatusCode.WARNING:
|
|
539
|
+
msg = 'Failed with warnings'
|
|
391
540
|
self.print_stderr(f'{msg} (rqId: {request_id} - status: {status_code}): {status_response.message}')
|
|
392
|
-
return
|
|
541
|
+
return ret_val
|
|
393
542
|
return True
|
|
394
543
|
|
|
544
|
+
def check_status_response_rest(self, status_dict: dict, request_id: Optional[str] = None) -> bool:
|
|
545
|
+
"""
|
|
546
|
+
Check the REST response dictionary to see if the command was successful or not
|
|
547
|
+
|
|
548
|
+
Args:
|
|
549
|
+
status_dict (dict): Status dictionary from REST response containing 'status' and 'message' keys
|
|
550
|
+
request_id (str, optional): Request ID for logging
|
|
551
|
+
Returns:
|
|
552
|
+
bool: True if successful, False otherwise
|
|
553
|
+
"""
|
|
554
|
+
if not status_dict:
|
|
555
|
+
self.print_stderr(f'Warning: No status response supplied (rqId: {request_id}). Assuming it was ok.')
|
|
556
|
+
return True
|
|
557
|
+
|
|
558
|
+
if request_id:
|
|
559
|
+
self.print_debug(f'Checking response status (rqId: {request_id}): {status_dict}')
|
|
560
|
+
|
|
561
|
+
# Get status from dictionary - it can be either a string or nested dict
|
|
562
|
+
status = status_dict.get('status')
|
|
563
|
+
message = status_dict.get('message', '')
|
|
564
|
+
ret_val = True
|
|
565
|
+
|
|
566
|
+
# Handle case where status might be a string directly
|
|
567
|
+
if isinstance(status, str):
|
|
568
|
+
status_str = status.upper()
|
|
569
|
+
if status_str == ScanossRESTStatusCode.SUCCESS.value:
|
|
570
|
+
ret_val = True
|
|
571
|
+
elif status_str == ScanossRESTStatusCode.SUCCEEDED_WITH_WARNINGS.value:
|
|
572
|
+
self.print_stderr(f'Succeeded with warnings (rqId: {request_id}): {message}')
|
|
573
|
+
ret_val = True
|
|
574
|
+
elif status_str == ScanossRESTStatusCode.WARNING.value:
|
|
575
|
+
self.print_stderr(f'Failed with warnings (rqId: {request_id}): {message}')
|
|
576
|
+
ret_val = False
|
|
577
|
+
elif status_str == ScanossRESTStatusCode.FAILED.value:
|
|
578
|
+
self.print_stderr(f'Unsuccessful (rqId: {request_id}): {message}')
|
|
579
|
+
ret_val = False
|
|
580
|
+
else:
|
|
581
|
+
self.print_debug(f'Unknown status "{status_str}" (rqId: {request_id}). Assuming success.')
|
|
582
|
+
ret_val = True
|
|
583
|
+
|
|
584
|
+
# Otherwise asume success
|
|
585
|
+
self.print_debug(f'Unexpected status type {type(status)} (rqId: {request_id}). Assuming success.')
|
|
586
|
+
return ret_val
|
|
587
|
+
|
|
395
588
|
def _get_proxy_config(self):
|
|
396
589
|
"""
|
|
397
590
|
Set the grpc_proxy/http_proxy/https_proxy environment variables if PAC file has been specified
|
|
@@ -399,21 +592,376 @@ class ScanossGrpc(ScanossBase):
|
|
|
399
592
|
:param self:
|
|
400
593
|
"""
|
|
401
594
|
if self.grpc_proxy:
|
|
402
|
-
self.print_debug(
|
|
403
|
-
os.environ[
|
|
595
|
+
self.print_debug('Setting GRPC (grpc_proxy) proxy...')
|
|
596
|
+
os.environ['grpc_proxy'] = self.grpc_proxy
|
|
404
597
|
elif self.proxy:
|
|
405
|
-
self.print_debug(
|
|
406
|
-
os.environ[
|
|
407
|
-
os.environ[
|
|
598
|
+
self.print_debug('Setting GRPC (http_proxy/https_proxy) proxies...')
|
|
599
|
+
os.environ['http_proxy'] = self.proxy
|
|
600
|
+
os.environ['https_proxy'] = self.proxy
|
|
408
601
|
elif self.pac:
|
|
409
602
|
self.print_debug(f'Attempting to get GRPC proxy details from PAC for {self.orig_url}...')
|
|
410
603
|
resolver = ProxyResolver(self.pac)
|
|
411
604
|
proxies = resolver.get_proxy_for_requests(self.orig_url)
|
|
412
605
|
if proxies:
|
|
413
606
|
self.print_trace(f'Setting proxies: {proxies}')
|
|
414
|
-
os.environ[
|
|
415
|
-
os.environ[
|
|
607
|
+
os.environ['http_proxy'] = proxies.get('http') or ''
|
|
608
|
+
os.environ['https_proxy'] = proxies.get('https') or ''
|
|
609
|
+
|
|
610
|
+
def get_provenance_json(self, purls: dict, use_grpc: Optional[bool] = None) -> Optional[Dict]:
|
|
611
|
+
"""
|
|
612
|
+
Client function to call the rpc for GetComponentContributors
|
|
613
|
+
|
|
614
|
+
Args:
|
|
615
|
+
purls (dict): ComponentsRequest
|
|
616
|
+
use_grpc (bool): Whether to use gRPC or REST (None = use instance default)
|
|
617
|
+
|
|
618
|
+
Returns:
|
|
619
|
+
dict: JSON response or None
|
|
620
|
+
"""
|
|
621
|
+
return self._call_api(
|
|
622
|
+
'geoprovenance.GetCountryContributorsByComponents',
|
|
623
|
+
self.provenance_stub.GetCountryContributorsByComponents,
|
|
624
|
+
purls,
|
|
625
|
+
ComponentsRequest,
|
|
626
|
+
'Sending data for provenance decoration (rqId: {rqId})...',
|
|
627
|
+
use_grpc=use_grpc,
|
|
628
|
+
)
|
|
629
|
+
|
|
630
|
+
def get_provenance_origin(self, request: Dict, use_grpc: Optional[bool] = None) -> Optional[Dict]:
|
|
631
|
+
"""
|
|
632
|
+
Client function to call the rpc for GetOriginByComponents
|
|
633
|
+
|
|
634
|
+
Args:
|
|
635
|
+
request (Dict): GetOriginByComponents Request
|
|
636
|
+
|
|
637
|
+
Returns:
|
|
638
|
+
Optional[Dict]: OriginResponse, or None if the request was not successfull
|
|
639
|
+
"""
|
|
640
|
+
return self._call_api(
|
|
641
|
+
'geoprovenance.GetOriginByComponents',
|
|
642
|
+
self.provenance_stub.GetOriginByComponents,
|
|
643
|
+
request,
|
|
644
|
+
ComponentsRequest,
|
|
645
|
+
'Sending data for provenance origin decoration (rqId: {rqId})...',
|
|
646
|
+
use_grpc=use_grpc,
|
|
647
|
+
)
|
|
648
|
+
|
|
649
|
+
def get_crypto_algorithms_for_purl(self, request: Dict, use_grpc: Optional[bool] = None) -> Optional[Dict]:
|
|
650
|
+
"""
|
|
651
|
+
Client function to call the rpc for GetComponentsAlgorithms for a list of purls
|
|
652
|
+
|
|
653
|
+
Args:
|
|
654
|
+
request (Dict): ComponentsRequest
|
|
655
|
+
use_grpc (Optional[bool]): Whether to use gRPC or REST (None = use instance default)
|
|
656
|
+
|
|
657
|
+
Returns:
|
|
658
|
+
Optional[Dict]: AlgorithmResponse, or None if the request was not successfull
|
|
659
|
+
"""
|
|
660
|
+
return self._call_api(
|
|
661
|
+
'cryptography.GetComponentsAlgorithms',
|
|
662
|
+
self.crypto_stub.GetComponentsAlgorithms,
|
|
663
|
+
request,
|
|
664
|
+
ComponentsRequest,
|
|
665
|
+
'Sending data for cryptographic algorithms decoration (rqId: {rqId})...',
|
|
666
|
+
use_grpc=use_grpc,
|
|
667
|
+
)
|
|
668
|
+
|
|
669
|
+
def get_crypto_algorithms_in_range_for_purl(self, request: Dict, use_grpc: Optional[bool] = None) -> Optional[Dict]:
|
|
670
|
+
"""
|
|
671
|
+
Client function to call the rpc for GetComponentsAlgorithmsInRange for a list of purls
|
|
672
|
+
|
|
673
|
+
Args:
|
|
674
|
+
request (Dict): ComponentsRequest
|
|
675
|
+
use_grpc (Optional[bool]): Whether to use gRPC or REST (None = use instance default)
|
|
676
|
+
|
|
677
|
+
Returns:
|
|
678
|
+
Optional[Dict]: AlgorithmsInRangeResponse, or None if the request was not successfull
|
|
679
|
+
"""
|
|
680
|
+
return self._call_api(
|
|
681
|
+
'cryptography.GetComponentsAlgorithmsInRange',
|
|
682
|
+
self.crypto_stub.GetComponentsAlgorithmsInRange,
|
|
683
|
+
request,
|
|
684
|
+
ComponentsRequest,
|
|
685
|
+
'Sending data for cryptographic algorithms in range decoration (rqId: {rqId})...',
|
|
686
|
+
use_grpc=use_grpc,
|
|
687
|
+
)
|
|
688
|
+
|
|
689
|
+
def get_encryption_hints_for_purl(self, request: Dict, use_grpc: Optional[bool] = None) -> Optional[Dict]:
|
|
690
|
+
"""
|
|
691
|
+
Client function to call the rpc for GetComponentsEncryptionHints for a list of purls
|
|
692
|
+
|
|
693
|
+
Args:
|
|
694
|
+
request (Dict): ComponentsRequest
|
|
695
|
+
use_grpc (Optional[bool]): Whether to use gRPC or REST (None = use instance default)
|
|
696
|
+
|
|
697
|
+
Returns:
|
|
698
|
+
Optional[Dict]: HintsResponse, or None if the request was not successfull
|
|
699
|
+
"""
|
|
700
|
+
return self._call_api(
|
|
701
|
+
'cryptography.GetComponentsEncryptionHints',
|
|
702
|
+
self.crypto_stub.GetComponentsEncryptionHints,
|
|
703
|
+
request,
|
|
704
|
+
ComponentsRequest,
|
|
705
|
+
'Sending data for encryption hints decoration (rqId: {rqId})...',
|
|
706
|
+
use_grpc=use_grpc,
|
|
707
|
+
)
|
|
708
|
+
|
|
709
|
+
def get_encryption_hints_in_range_for_purl(self, request: Dict, use_grpc: Optional[bool] = None) -> Optional[Dict]:
|
|
710
|
+
"""
|
|
711
|
+
Client function to call the rpc for GetComponentsHintsInRange for a list of purls
|
|
712
|
+
|
|
713
|
+
Args:
|
|
714
|
+
request (Dict): ComponentsRequest
|
|
715
|
+
use_grpc (Optional[bool]): Whether to use gRPC or REST (None = use instance default)
|
|
716
|
+
|
|
717
|
+
Returns:
|
|
718
|
+
Optional[Dict]: HintsInRangeResponse, or None if the request was not successfull
|
|
719
|
+
"""
|
|
720
|
+
return self._call_api(
|
|
721
|
+
'cryptography.GetComponentsHintsInRange',
|
|
722
|
+
self.crypto_stub.GetComponentsHintsInRange,
|
|
723
|
+
request,
|
|
724
|
+
ComponentsRequest,
|
|
725
|
+
'Sending data for encryption hints in range decoration (rqId: {rqId})...',
|
|
726
|
+
use_grpc=use_grpc,
|
|
727
|
+
)
|
|
728
|
+
|
|
729
|
+
def get_versions_in_range_for_purl(self, request: Dict, use_grpc: Optional[bool] = None) -> Optional[Dict]:
|
|
730
|
+
"""
|
|
731
|
+
Client function to call the rpc for GetComponentsVersionsInRange for a list of purls
|
|
732
|
+
|
|
733
|
+
Args:
|
|
734
|
+
request (Dict): ComponentsRequest
|
|
735
|
+
use_grpc (Optional[bool]): Whether to use gRPC or REST (None = use instance default)
|
|
736
|
+
|
|
737
|
+
Returns:
|
|
738
|
+
Optional[Dict]: VersionsInRangeResponse, or None if the request was not successfull
|
|
739
|
+
"""
|
|
740
|
+
return self._call_api(
|
|
741
|
+
'cryptography.GetComponentsVersionsInRange',
|
|
742
|
+
self.crypto_stub.GetComponentsVersionsInRange,
|
|
743
|
+
request,
|
|
744
|
+
ComponentsRequest,
|
|
745
|
+
'Sending data for cryptographic versions in range decoration (rqId: {rqId})...',
|
|
746
|
+
use_grpc=use_grpc,
|
|
747
|
+
)
|
|
748
|
+
|
|
749
|
+
def get_licenses(self, request: Dict, use_grpc: Optional[bool] = None) -> Optional[Dict]:
|
|
750
|
+
"""
|
|
751
|
+
Client function to call the rpc for Licenses GetComponentsLicenses
|
|
752
|
+
It will either use REST (default) or gRPC depending on the use_grpc flag
|
|
753
|
+
|
|
754
|
+
Args:
|
|
755
|
+
request (Dict): ComponentsRequest
|
|
756
|
+
Returns:
|
|
757
|
+
Optional[Dict]: ComponentsLicenseResponse, or None if the request was not successfull
|
|
758
|
+
"""
|
|
759
|
+
return self._call_api(
|
|
760
|
+
'licenses.GetComponentsLicenses',
|
|
761
|
+
self.license_stub.GetComponentsLicenses,
|
|
762
|
+
request,
|
|
763
|
+
ComponentsRequest,
|
|
764
|
+
'Sending data for license decoration (rqId: {rqId})...',
|
|
765
|
+
use_grpc=use_grpc,
|
|
766
|
+
)
|
|
767
|
+
|
|
768
|
+
def load_generic_headers(self, url: Optional[str] = None):
|
|
769
|
+
"""
|
|
770
|
+
Adds custom headers from req_headers to metadata.
|
|
771
|
+
|
|
772
|
+
If x-api-key is present and no URL is configured (directly or via
|
|
773
|
+
environment), sets URL to the premium endpoint (DEFAULT_URL2).
|
|
774
|
+
"""
|
|
775
|
+
if self.req_headers: # Load generic headers
|
|
776
|
+
for key, value in self.req_headers.items():
|
|
777
|
+
if key == 'x-api-key': # Set premium URL if x-api-key header is set
|
|
778
|
+
if not url and not os.environ.get('SCANOSS_GRPC_URL'):
|
|
779
|
+
self.url = DEFAULT_URL2 # API key specific and no alternative URL, so use the default premium
|
|
780
|
+
self.api_key = value
|
|
781
|
+
self.metadata.append((key, value))
|
|
782
|
+
self.headers[key] = value
|
|
783
|
+
|
|
784
|
+
#
|
|
785
|
+
# End of gRPC Client Functions
|
|
786
|
+
#
|
|
787
|
+
# Start of REST Client Functions
|
|
788
|
+
#
|
|
789
|
+
|
|
790
|
+
def _rest_get(self, uri: str, request_id: str, params: Optional[dict] = None) -> Optional[dict]:
|
|
791
|
+
"""
|
|
792
|
+
Send a GET request to the specified URI with optional query parameters.
|
|
793
|
+
|
|
794
|
+
Args:
|
|
795
|
+
uri (str): URI to send GET request to
|
|
796
|
+
request_id (str): request id
|
|
797
|
+
params (dict, optional): Optional query parameters as dictionary
|
|
798
|
+
|
|
799
|
+
Returns:
|
|
800
|
+
dict: JSON response or None
|
|
801
|
+
"""
|
|
802
|
+
if not uri:
|
|
803
|
+
self.print_stderr('Error: Missing URI. Cannot perform GET request.')
|
|
804
|
+
return None
|
|
805
|
+
self.print_trace(f'Sending REST GET request to {uri}...')
|
|
806
|
+
headers = self.headers.copy()
|
|
807
|
+
headers['x-request-id'] = request_id
|
|
808
|
+
retry = 0
|
|
809
|
+
while retry <= self.retry_limit:
|
|
810
|
+
retry += 1
|
|
811
|
+
try:
|
|
812
|
+
response = self.session.get(uri, headers=headers, params=params, timeout=self.timeout)
|
|
813
|
+
response.raise_for_status() # Raises an HTTPError for bad responses
|
|
814
|
+
return response.json()
|
|
815
|
+
except (requests.exceptions.SSLError, requests.exceptions.ProxyError) as e:
|
|
816
|
+
self.print_stderr(f'ERROR: Exception ({e.__class__.__name__}) sending GET request - {e}.')
|
|
817
|
+
raise Exception(f'ERROR: The SCANOSS API GET request failed for {uri}') from e
|
|
818
|
+
except requests.exceptions.HTTPError as e:
|
|
819
|
+
self.print_stderr(f'ERROR: HTTP error sending GET request ({request_id}): {e}')
|
|
820
|
+
raise Exception(
|
|
821
|
+
f'ERROR: The SCANOSS API GET request failed with status {e.response.status_code} for {uri}'
|
|
822
|
+
) from e
|
|
823
|
+
except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
|
|
824
|
+
if retry > self.retry_limit: # Timed out retry_limit or more times, fail
|
|
825
|
+
self.print_stderr(f'ERROR: {e.__class__.__name__} sending GET request ({request_id}): {e}')
|
|
826
|
+
raise Exception(
|
|
827
|
+
f'ERROR: The SCANOSS API GET request timed out ({e.__class__.__name__}) for {uri}'
|
|
828
|
+
) from e
|
|
829
|
+
else:
|
|
830
|
+
self.print_stderr(f'Warning: {e.__class__.__name__} communicating with {self.url}. Retrying...')
|
|
831
|
+
time.sleep(5)
|
|
832
|
+
except requests.exceptions.RequestException as e:
|
|
833
|
+
self.print_stderr(f'Error: Problem sending GET request to {uri}: {e}')
|
|
834
|
+
raise Exception(f'ERROR: The SCANOSS API GET request failed for {uri}') from e
|
|
835
|
+
except Exception as e:
|
|
836
|
+
self.print_stderr(
|
|
837
|
+
f'ERROR: Exception ({e.__class__.__name__}) sending GET request ({request_id}) to {uri}: {e}'
|
|
838
|
+
)
|
|
839
|
+
raise Exception(f'ERROR: The SCANOSS API GET request failed for {uri}') from e
|
|
840
|
+
return None
|
|
841
|
+
|
|
842
|
+
def _rest_post(self, uri: str, request_id: str, data: dict) -> Optional[dict]:
|
|
843
|
+
"""
|
|
844
|
+
Post the specified data to the given URI.
|
|
845
|
+
|
|
846
|
+
Args:
|
|
847
|
+
uri (str): URI to post to
|
|
848
|
+
request_id (str): request id
|
|
849
|
+
data (dict): json data to post
|
|
850
|
+
|
|
851
|
+
Returns:
|
|
852
|
+
dict: JSON response or None
|
|
853
|
+
"""
|
|
854
|
+
if not uri:
|
|
855
|
+
self.print_stderr('Error: Missing URI. Cannot search for project.')
|
|
856
|
+
return None
|
|
857
|
+
self.print_trace(f'Sending REST POST data to {uri}...')
|
|
858
|
+
headers = self.headers.copy()
|
|
859
|
+
headers['x-request-id'] = request_id
|
|
860
|
+
retry = 0
|
|
861
|
+
while retry <= self.retry_limit:
|
|
862
|
+
retry += 1
|
|
863
|
+
try:
|
|
864
|
+
response = self.session.post(uri, headers=headers, json=data, timeout=self.timeout)
|
|
865
|
+
response.raise_for_status() # Raises an HTTPError for bad responses
|
|
866
|
+
return response.json()
|
|
867
|
+
except (requests.exceptions.SSLError, requests.exceptions.ProxyError) as e:
|
|
868
|
+
self.print_stderr(f'ERROR: Exception ({e.__class__.__name__}) POSTing data - {e}.')
|
|
869
|
+
raise Exception(f'ERROR: The SCANOSS Decoration API request failed for {uri}') from e
|
|
870
|
+
except requests.exceptions.HTTPError as e:
|
|
871
|
+
self.print_stderr(f'ERROR: HTTP error POSTing data ({request_id}): {e}')
|
|
872
|
+
raise Exception(
|
|
873
|
+
f'ERROR: The SCANOSS Decoration API request failed with status {e.response.status_code} for {uri}'
|
|
874
|
+
) from e
|
|
875
|
+
except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
|
|
876
|
+
if retry > self.retry_limit: # Timed out retry_limit or more times, fail
|
|
877
|
+
self.print_stderr(f'ERROR: {e.__class__.__name__} POSTing decoration data ({request_id}): {e}')
|
|
878
|
+
raise Exception(
|
|
879
|
+
f'ERROR: The SCANOSS Decoration API request timed out ({e.__class__.__name__}) for {uri}'
|
|
880
|
+
) from e
|
|
881
|
+
else:
|
|
882
|
+
self.print_stderr(f'Warning: {e.__class__.__name__} communicating with {self.url}. Retrying...')
|
|
883
|
+
time.sleep(5)
|
|
884
|
+
except requests.exceptions.RequestException as e:
|
|
885
|
+
self.print_stderr(f'Error: Problem posting data to {uri}: {e}')
|
|
886
|
+
raise Exception(f'ERROR: The SCANOSS Decoration API request failed for {uri}') from e
|
|
887
|
+
except Exception as e:
|
|
888
|
+
self.print_stderr(
|
|
889
|
+
f'ERROR: Exception ({e.__class__.__name__}) POSTing data ({request_id}) to {uri}: {e}'
|
|
890
|
+
)
|
|
891
|
+
raise Exception(f'ERROR: The SCANOSS Decoration API request failed for {uri}') from e
|
|
892
|
+
return None
|
|
893
|
+
|
|
894
|
+
def _call_rest(self, endpoint_key: str, request_input: dict, debug_msg: Optional[str] = None) -> Optional[Dict]:
|
|
895
|
+
"""
|
|
896
|
+
Call a REST endpoint and return the response as a dictionary
|
|
897
|
+
|
|
898
|
+
Args:
|
|
899
|
+
endpoint_key (str): The key to lookup the REST endpoint in REST_ENDPOINTS
|
|
900
|
+
request_input (dict): The request data to send
|
|
901
|
+
debug_msg (str, optional): Debug message template that can include {rqId} placeholder.
|
|
902
|
+
|
|
903
|
+
Returns:
|
|
904
|
+
dict: The parsed REST response as a dictionary, or None if something went wrong
|
|
905
|
+
"""
|
|
906
|
+
if endpoint_key not in REST_ENDPOINTS:
|
|
907
|
+
raise ScanossGrpcError(f'Unknown REST endpoint key: {endpoint_key}')
|
|
908
|
+
|
|
909
|
+
endpoint_config = REST_ENDPOINTS[endpoint_key]
|
|
910
|
+
endpoint_path = endpoint_config['path']
|
|
911
|
+
method = endpoint_config['method']
|
|
912
|
+
endpoint_url = f'{self.orig_url}{DEFAULT_URI_PREFIX}{endpoint_path}'
|
|
913
|
+
request_id = str(uuid.uuid4())
|
|
914
|
+
|
|
915
|
+
if debug_msg:
|
|
916
|
+
self.print_debug(debug_msg.format(rqId=request_id))
|
|
917
|
+
|
|
918
|
+
if method == 'GET':
|
|
919
|
+
response = self._rest_get(endpoint_url, request_id, params=request_input)
|
|
920
|
+
else: # POST
|
|
921
|
+
response = self._rest_post(endpoint_url, request_id, request_input)
|
|
922
|
+
|
|
923
|
+
if response and 'status' in response and not self.check_status_response_rest(response['status'], request_id):
|
|
924
|
+
return None
|
|
925
|
+
|
|
926
|
+
return response
|
|
927
|
+
|
|
416
928
|
|
|
417
929
|
#
|
|
418
930
|
# End of ScanossGrpc Class
|
|
419
931
|
#
|
|
932
|
+
|
|
933
|
+
|
|
934
|
+
@dataclass
|
|
935
|
+
class GrpcConfig:
|
|
936
|
+
url: str = DEFAULT_URL
|
|
937
|
+
api_key: Optional[str] = SCANOSS_API_KEY
|
|
938
|
+
debug: Optional[bool] = False
|
|
939
|
+
trace: Optional[bool] = False
|
|
940
|
+
quiet: Optional[bool] = False
|
|
941
|
+
ver_details: Optional[str] = None
|
|
942
|
+
ca_cert: Optional[str] = None
|
|
943
|
+
timeout: Optional[int] = DEFAULT_TIMEOUT
|
|
944
|
+
proxy: Optional[str] = None
|
|
945
|
+
grpc_proxy: Optional[str] = None
|
|
946
|
+
pac: Optional[PACFile] = None
|
|
947
|
+
req_headers: Optional[dict] = None
|
|
948
|
+
|
|
949
|
+
|
|
950
|
+
def create_grpc_config_from_args(args) -> GrpcConfig:
|
|
951
|
+
return GrpcConfig(
|
|
952
|
+
url=getattr(args, 'api2url', DEFAULT_URL),
|
|
953
|
+
api_key=getattr(args, 'key', SCANOSS_API_KEY),
|
|
954
|
+
debug=getattr(args, 'debug', False),
|
|
955
|
+
trace=getattr(args, 'trace', False),
|
|
956
|
+
quiet=getattr(args, 'quiet', False),
|
|
957
|
+
ver_details=getattr(args, 'ver_details', None),
|
|
958
|
+
ca_cert=getattr(args, 'ca_cert', None),
|
|
959
|
+
timeout=getattr(args, 'timeout', DEFAULT_TIMEOUT),
|
|
960
|
+
proxy=getattr(args, 'proxy', None),
|
|
961
|
+
grpc_proxy=getattr(args, 'grpc_proxy', None),
|
|
962
|
+
)
|
|
963
|
+
|
|
964
|
+
|
|
965
|
+
#
|
|
966
|
+
# End of GrpcConfig class
|
|
967
|
+
#
|