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.
Files changed (108) hide show
  1. {scanoss-1.31.5/src/scanoss.egg-info → scanoss-1.32.0}/PKG-INFO +1 -1
  2. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/__init__.py +1 -1
  3. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/cli.py +6 -1
  4. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/components.py +27 -8
  5. scanoss-1.32.0/src/scanoss/data/build_date.txt +1 -0
  6. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanner.py +3 -0
  7. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanossapi.py +22 -24
  8. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanossgrpc.py +196 -64
  9. {scanoss-1.31.5 → scanoss-1.32.0/src/scanoss.egg-info}/PKG-INFO +1 -1
  10. scanoss-1.31.5/src/scanoss/data/build_date.txt +0 -1
  11. {scanoss-1.31.5 → scanoss-1.32.0}/LICENSE +0 -0
  12. {scanoss-1.31.5 → scanoss-1.32.0}/PACKAGE.md +0 -0
  13. {scanoss-1.31.5 → scanoss-1.32.0}/README.md +0 -0
  14. {scanoss-1.31.5 → scanoss-1.32.0}/pyproject.toml +0 -0
  15. {scanoss-1.31.5 → scanoss-1.32.0}/setup.cfg +0 -0
  16. {scanoss-1.31.5 → scanoss-1.32.0}/src/protoc_gen_swagger/__init__.py +0 -0
  17. {scanoss-1.31.5 → scanoss-1.32.0}/src/protoc_gen_swagger/options/__init__.py +0 -0
  18. {scanoss-1.31.5 → scanoss-1.32.0}/src/protoc_gen_swagger/options/annotations_pb2.py +0 -0
  19. {scanoss-1.31.5 → scanoss-1.32.0}/src/protoc_gen_swagger/options/annotations_pb2_grpc.py +0 -0
  20. {scanoss-1.31.5 → scanoss-1.32.0}/src/protoc_gen_swagger/options/openapiv2_pb2.py +0 -0
  21. {scanoss-1.31.5 → scanoss-1.32.0}/src/protoc_gen_swagger/options/openapiv2_pb2_grpc.py +0 -0
  22. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/__init__.py +0 -0
  23. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/common/__init__.py +0 -0
  24. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/common/v2/__init__.py +0 -0
  25. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/common/v2/scanoss_common_pb2.py +0 -0
  26. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/common/v2/scanoss_common_pb2_grpc.py +0 -0
  27. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/components/__init__.py +0 -0
  28. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/components/v2/__init__.py +0 -0
  29. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/components/v2/scanoss_components_pb2.py +0 -0
  30. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/components/v2/scanoss_components_pb2_grpc.py +0 -0
  31. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/cryptography/v2/scanoss_cryptography_pb2.py +0 -0
  32. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/cryptography/v2/scanoss_cryptography_pb2_grpc.py +0 -0
  33. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/dependencies/__init__.py +0 -0
  34. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/dependencies/v2/__init__.py +0 -0
  35. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/dependencies/v2/scanoss_dependencies_pb2.py +0 -0
  36. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/dependencies/v2/scanoss_dependencies_pb2_grpc.py +0 -0
  37. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/geoprovenance/__init__.py +0 -0
  38. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/geoprovenance/v2/__init__.py +0 -0
  39. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2.py +0 -0
  40. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2_grpc.py +0 -0
  41. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/licenses/__init__.py +0 -0
  42. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/licenses/v2/__init__.py +0 -0
  43. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/licenses/v2/scanoss_licenses_pb2.py +0 -0
  44. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/licenses/v2/scanoss_licenses_pb2_grpc.py +0 -0
  45. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/scanning/__init__.py +0 -0
  46. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/scanning/v2/__init__.py +0 -0
  47. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/scanning/v2/scanoss_scanning_pb2.py +0 -0
  48. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/scanning/v2/scanoss_scanning_pb2_grpc.py +0 -0
  49. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/semgrep/__init__.py +0 -0
  50. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/semgrep/v2/__init__.py +0 -0
  51. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/semgrep/v2/scanoss_semgrep_pb2.py +0 -0
  52. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/semgrep/v2/scanoss_semgrep_pb2_grpc.py +0 -0
  53. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/vulnerabilities/__init__.py +0 -0
  54. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/vulnerabilities/v2/__init__.py +0 -0
  55. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2.py +0 -0
  56. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2_grpc.py +0 -0
  57. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/constants.py +0 -0
  58. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/cryptography.py +0 -0
  59. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/csvoutput.py +0 -0
  60. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/cyclonedx.py +0 -0
  61. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/data/scanoss-settings-schema.json +0 -0
  62. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/data/spdx-exceptions.json +0 -0
  63. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/data/spdx-licenses.json +0 -0
  64. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/export/__init__.py +0 -0
  65. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/export/dependency_track.py +0 -0
  66. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/file_filters.py +0 -0
  67. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/filecount.py +0 -0
  68. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/inspection/__init__.py +0 -0
  69. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/inspection/dependency_track/project_violation.py +0 -0
  70. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/inspection/policy_check.py +0 -0
  71. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/inspection/raw/component_summary.py +0 -0
  72. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/inspection/raw/copyleft.py +0 -0
  73. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/inspection/raw/license_summary.py +0 -0
  74. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/inspection/raw/raw_base.py +0 -0
  75. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/inspection/raw/undeclared_component.py +0 -0
  76. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/inspection/utils/license_utils.py +0 -0
  77. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/results.py +0 -0
  78. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scancodedeps.py +0 -0
  79. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanners/__init__.py +0 -0
  80. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanners/container_scanner.py +0 -0
  81. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanners/folder_hasher.py +0 -0
  82. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanners/scanner_config.py +0 -0
  83. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanners/scanner_hfh.py +0 -0
  84. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanoss_settings.py +0 -0
  85. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanossbase.py +0 -0
  86. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scanpostprocessor.py +0 -0
  87. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/scantype.py +0 -0
  88. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/services/dependency_track_service.py +0 -0
  89. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/spdxlite.py +0 -0
  90. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/threadeddependencies.py +0 -0
  91. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/threadedscanning.py +0 -0
  92. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/utils/__init__.py +0 -0
  93. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/utils/abstract_presenter.py +0 -0
  94. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/utils/crc64.py +0 -0
  95. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/utils/file.py +0 -0
  96. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/utils/simhash.py +0 -0
  97. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss/winnowing.py +0 -0
  98. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss.egg-info/SOURCES.txt +0 -0
  99. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss.egg-info/dependency_links.txt +0 -0
  100. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss.egg-info/entry_points.txt +0 -0
  101. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss.egg-info/requires.txt +0 -0
  102. {scanoss-1.31.5 → scanoss-1.32.0}/src/scanoss.egg-info/top_level.txt +0 -0
  103. {scanoss-1.31.5 → scanoss-1.32.0}/tests/test_csv_output.py +0 -0
  104. {scanoss-1.31.5 → scanoss-1.32.0}/tests/test_file_filters.py +0 -0
  105. {scanoss-1.31.5 → scanoss-1.32.0}/tests/test_policy_inspect.py +0 -0
  106. {scanoss-1.31.5 → scanoss-1.32.0}/tests/test_scan_post_processor.py +0 -0
  107. {scanoss-1.31.5 → scanoss-1.32.0}/tests/test_spdxlite.py +0 -0
  108. {scanoss-1.31.5 → scanoss-1.32.0}/tests/test_winnowing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scanoss
3
- Version: 1.31.5
3
+ Version: 1.32.0
4
4
  Summary: Simple Python library to leverage the SCANOSS APIs
5
5
  Home-page: https://scanoss.com
6
6
  Author: SCANOSS
@@ -22,4 +22,4 @@ SPDX-License-Identifier: MIT
22
22
  THE SOFTWARE.
23
23
  """
24
24
 
25
- __version__ = '1.31.5'
25
+ __version__ = '1.32.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('--ignore-cert-errors', action='store_true', help='Ignore certificate errors')
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 load_purls(self, json_file: Optional[str] = None, purls: Optional[List[str]] = None) -> Optional[dict]:
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 = {'purls': parsed_purls}
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('purls', []))
117
- self.print_debug(f'Parsed Purls ({purl_count}): {purl_request}')
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 PURLs parsed from request.')
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.load_purls(json_file, purls)
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 = 180,
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 > 5 else 180
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
- self.headers['User-Agent'] = f'scanoss-py/{__version__}'
107
- self.headers['user-agent'] = f'scanoss-py/{__version__}'
108
- self.load_generic_headers()
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 == 503: # Service limits have most likely been reached
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 >= 400:
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 self.url and not os.environ.get('SCANOSS_SCAN_URL'):
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 VulnerabilityResponse
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
- self.metadata.append(('user-agent', f'scanoss-py/{__version__}'))
144
- self.load_generic_headers()
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 is False: # insecure connection
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 gRPC service.')
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
- SUCCESS_STATUS = 'SUCCESS'
303
-
304
- merged_response = {'files': [], 'status': {'status': SUCCESS_STATUS, 'message': 'Success'}}
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 the any of the responses was not successful
310
- if 'status' in response and response['status']['status'] != SUCCESS_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: VulnerabilityResponse
367
+ resp: ComponentsVulnerabilityResponse
325
368
  try:
326
- request = ParseDict(purls, PurlRequest()) # Parse the JSON/Dict into the purl request object
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.GetVulnerabilities(request, metadata=metadata, timeout=self.timeout)
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 self.url and not os.environ.get('SCANOSS_GRPC_URL'):
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scanoss
3
- Version: 1.31.5
3
+ Version: 1.32.0
4
4
  Summary: Simple Python library to leverage the SCANOSS APIs
5
5
  Home-page: https://scanoss.com
6
6
  Author: SCANOSS
@@ -1 +0,0 @@
1
- date: 20250827093647, utime: 1756287407
File without changes
File without changes
File without changes
File without changes
File without changes