pangea-sdk 5.0.0__py3-none-any.whl → 5.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pangea/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "5.0.0"
1
+ __version__ = "5.2.0"
2
2
 
3
3
  from pangea.asyncio.request import PangeaRequestAsync
4
4
  from pangea.config import PangeaConfig
pangea/asyncio/request.py CHANGED
@@ -182,13 +182,27 @@ class PangeaRequestAsync(PangeaRequestBase):
182
182
  if resp.status < 200 or resp.status >= 300:
183
183
  raise pe.PresignedUploadError(f"presigned PUT failure: {resp.status}", await resp.text())
184
184
 
185
- async def download_file(self, url: str, filename: Optional[str] = None) -> AttachedFile:
185
+ async def download_file(self, url: str, filename: str | None = None) -> AttachedFile:
186
+ """
187
+ Download file
188
+
189
+ Download a file from the specified URL and save it with the given
190
+ filename.
191
+
192
+ Args:
193
+ url: URL of the file to download
194
+ filename: Name to save the downloaded file as. If not provided, the
195
+ filename will be determined from the Content-Disposition header or
196
+ the URL.
197
+ """
198
+
186
199
  self.logger.debug(
187
200
  json.dumps(
188
201
  {
189
202
  "service": self.service,
190
203
  "action": "download_file",
191
204
  "url": url,
205
+ "filename": filename,
192
206
  "status": "start",
193
207
  }
194
208
  )
@@ -303,6 +317,9 @@ class PangeaRequestAsync(PangeaRequestBase):
303
317
  raise AttributeError("files attribute should have at least 1 file")
304
318
 
305
319
  response = await self.request_presigned_url(endpoint=endpoint, result_class=result_class, data=data)
320
+ if response.success: # This should only happen when uploading a zero bytes file
321
+ return response.raw_response
322
+
306
323
  if response.accepted_result is None:
307
324
  raise pe.PangeaException("No accepted_result field when requesting presigned url")
308
325
  if response.accepted_result.post_url is None:
@@ -322,9 +339,8 @@ class PangeaRequestAsync(PangeaRequestBase):
322
339
  ) -> PangeaResponse:
323
340
  # Send request
324
341
  try:
325
- # This should return 202 (AcceptedRequestException)
326
- resp = await self.post(endpoint=endpoint, result_class=result_class, data=data, poll_result=False)
327
- raise pe.PresignedURLException("Should return 202", resp)
342
+ # This should return 202 (AcceptedRequestException) at least zero size file is sent
343
+ return await self.post(endpoint=endpoint, result_class=result_class, data=data, poll_result=False)
328
344
  except pe.AcceptedRequestException as e:
329
345
  accepted_exception = e
330
346
  except Exception as e:
@@ -6,4 +6,5 @@ from .file_scan import FileScanAsync
6
6
  from .intel import DomainIntelAsync, FileIntelAsync, IpIntelAsync, UrlIntelAsync, UserIntelAsync
7
7
  from .redact import RedactAsync
8
8
  from .sanitize import SanitizeAsync
9
+ from .share import ShareAsync
9
10
  from .vault import VaultAsync
@@ -174,14 +174,16 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
174
174
  verbose: Optional[bool] = None,
175
175
  ) -> PangeaResponse[LogResult]:
176
176
  """
177
- Log an entry
177
+ Log an event
178
178
 
179
179
  Create a log entry in the Secure Audit Log.
180
+
180
181
  Args:
181
182
  event (dict[str, Any]): event to be logged
182
183
  verify (bool, optional): True to verify logs consistency after response.
183
184
  sign_local (bool, optional): True to sign event with local key.
184
185
  verbose (bool, optional): True to get a more verbose response.
186
+
185
187
  Raises:
186
188
  AuditException: If an audit based api exception happens
187
189
  PangeaAPIException: If an API Error happens
@@ -192,13 +194,7 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
192
194
  Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/audit#log-an-entry).
193
195
 
194
196
  Examples:
195
- try:
196
- log_response = audit.log({"message"="Hello world"}, verbose=False)
197
- print(f"Response. Hash: {log_response.result.hash}")
198
- except pe.PangeaAPIException as e:
199
- print(f"Request Error: {e.response.summary}")
200
- for err in e.errors:
201
- print(f"\\t{err.detail} \\n")
197
+ response = await audit.log_event({"message": "hello world"}, verbose=True)
202
198
  """
203
199
 
204
200
  input = self._get_log_request(event, sign_local=sign_local, verify=verify, verbose=verbose)
@@ -632,9 +628,9 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
632
628
  if pub_root is not None:
633
629
  self.pub_roots[tree_size] = pub_root
634
630
 
635
- await self.fix_consistency_proofs(tree_sizes)
631
+ await self._fix_consistency_proofs(tree_sizes)
636
632
 
637
- async def fix_consistency_proofs(self, tree_sizes: Iterable[int]):
633
+ async def _fix_consistency_proofs(self, tree_sizes: Iterable[int]) -> None:
638
634
  # on very rare occasions, the consistency proof in Arweave may be wrong
639
635
  # override it with the proof from pangea (not the root hash, just the proof)
640
636
  for tree_size in tree_sizes:
@@ -1,9 +1,12 @@
1
1
  # Copyright 2022 Pangea Cyber Corporation
2
2
  # Author: Pangea Cyber Corporation
3
3
 
4
+ from __future__ import annotations
5
+
4
6
  from typing import Any, Dict, List, Optional
5
7
 
6
8
  from pangea.asyncio.services.base import ServiceBaseAsync
9
+ from pangea.config import PangeaConfig
7
10
  from pangea.response import PangeaResponse
8
11
  from pangea.services.authz import (
9
12
  CheckRequest,
@@ -36,8 +39,8 @@ class AuthZAsync(ServiceBaseAsync):
36
39
 
37
40
  Examples:
38
41
  import os
42
+ from pangea.asyncio.services import AuthZAsync
39
43
  from pangea.config import PangeaConfig
40
- from pangea.services import AuthZ
41
44
 
42
45
  PANGEA_TOKEN = os.getenv("PANGEA_AUTHZ_TOKEN")
43
46
 
@@ -49,7 +52,25 @@ class AuthZAsync(ServiceBaseAsync):
49
52
 
50
53
  service_name = "authz"
51
54
 
52
- def __init__(self, token: str, config=None, logger_name="pangea", config_id: Optional[str] = None):
55
+ def __init__(
56
+ self, token: str, config: PangeaConfig | None = None, logger_name: str = "pangea", config_id: str | None = None
57
+ ) -> None:
58
+ """
59
+ AuthZ client
60
+
61
+ Initializes a new AuthZ client.
62
+
63
+ Args:
64
+ token: Pangea API token.
65
+ config: Configuration.
66
+ logger_name: Logger name.
67
+ config_id: Configuration ID.
68
+
69
+ Examples:
70
+ config = PangeaConfig(domain="aws.us.pangea.cloud")
71
+ authz = AuthZAsync(token="pangea_token", config=config)
72
+ """
73
+
53
74
  super().__init__(token, config, logger_name, config_id=config_id)
54
75
 
55
76
  async def tuple_create(self, tuples: List[Tuple]) -> PangeaResponse[TupleCreateResult]:
@@ -1,8 +1,11 @@
1
1
  # Copyright 2022 Pangea Cyber Corporation
2
2
  # Author: Pangea Cyber Corporation
3
+ from __future__ import annotations
3
4
 
4
5
  from typing import Dict, Optional, Type, Union
5
6
 
7
+ from typing_extensions import override
8
+
6
9
  from pangea.asyncio.request import PangeaRequestAsync
7
10
  from pangea.exceptions import AcceptedRequestException
8
11
  from pangea.response import AttachedFile, PangeaResponse, PangeaResponseResult
@@ -23,6 +26,7 @@ class ServiceBaseAsync(ServiceBase):
23
26
 
24
27
  return self._request
25
28
 
29
+ @override
26
30
  async def poll_result( # type: ignore[override]
27
31
  self,
28
32
  exception: Optional[AcceptedRequestException] = None,
@@ -36,7 +40,8 @@ class ServiceBaseAsync(ServiceBase):
36
40
  Returns request's result that has been accepted by the server
37
41
 
38
42
  Args:
39
- exception (AcceptedRequestException): Exception raise by SDK on the call that is been processed.
43
+ exception: Exception that was previously raised by the SDK on a call
44
+ that is being processed.
40
45
 
41
46
  Returns:
42
47
  PangeaResponse
@@ -58,7 +63,21 @@ class ServiceBaseAsync(ServiceBase):
58
63
  else:
59
64
  raise AttributeError("Need to set exception, response or request_id")
60
65
 
61
- async def download_file(self, url: str, filename: Optional[str] = None) -> AttachedFile: # type: ignore[override]
66
+ @override
67
+ async def download_file(self, url: str, filename: str | None = None) -> AttachedFile: # type: ignore[override]
68
+ """
69
+ Download file
70
+
71
+ Download a file from the specified URL and save it with the given
72
+ filename.
73
+
74
+ Args:
75
+ url: URL of the file to download
76
+ filename: Name to save the downloaded file as. If not provided, the
77
+ filename will be determined from the Content-Disposition header or
78
+ the URL.
79
+ """
80
+
62
81
  return await self.request.download_file(url=url, filename=filename)
63
82
 
64
83
  async def close(self):
@@ -790,6 +790,7 @@ class UserIntelAsync(ServiceBaseAsync):
790
790
  verbose: Optional[bool] = None,
791
791
  raw: Optional[bool] = None,
792
792
  provider: Optional[str] = None,
793
+ cursor: Optional[str] = None,
793
794
  ) -> PangeaResponse[m.UserBreachedResult]:
794
795
  """
795
796
  Look up breached users
@@ -808,6 +809,7 @@ class UserIntelAsync(ServiceBaseAsync):
808
809
  verbose (bool, optional): Echo the API parameters in the response
809
810
  raw (bool, optional): Include raw data from this provider
810
811
  provider (str, optional): Use reputation data from this provider: "crowdstrike"
812
+ cursor (str, optional): A token given in the raw response from SpyCloud. Post this back to paginate results
811
813
 
812
814
  Raises:
813
815
  PangeaAPIException: If an API Error happens
@@ -835,6 +837,7 @@ class UserIntelAsync(ServiceBaseAsync):
835
837
  end=end,
836
838
  verbose=verbose,
837
839
  raw=raw,
840
+ cursor=cursor,
838
841
  )
839
842
  return await self.request.post(
840
843
  "v1/user/breached", m.UserBreachedResult, data=input.model_dump(exclude_none=True)
@@ -1,10 +1,13 @@
1
1
  # Copyright 2022 Pangea Cyber Corporation
2
2
  # Author: Pangea Cyber Corporation
3
+ from __future__ import annotations
4
+
3
5
  import io
4
6
  from typing import List, Optional, Tuple
5
7
 
6
8
  import pangea.services.sanitize as m
7
9
  from pangea.asyncio.services.base import ServiceBaseAsync
10
+ from pangea.config import PangeaConfig
8
11
  from pangea.response import PangeaResponse, TransferMethod
9
12
  from pangea.utils import FileUploadParams, get_file_upload_params
10
13
 
@@ -16,17 +19,38 @@ class SanitizeAsync(ServiceBaseAsync):
16
19
  import os
17
20
 
18
21
  # Pangea SDK
22
+ from pangea.asyncio.services import SanitizeAsync
19
23
  from pangea.config import PangeaConfig
20
- from pangea.asyncio.services import Sanitize
21
24
 
22
25
  PANGEA_SANITIZE_TOKEN = os.getenv("PANGEA_SANITIZE_TOKEN")
23
26
  config = PangeaConfig(domain="pangea.cloud")
24
27
 
25
- sanitize = Sanitize(token=PANGEA_SANITIZE_TOKEN, config=config)
28
+ sanitize = SanitizeAsync(token=PANGEA_SANITIZE_TOKEN, config=config)
26
29
  """
27
30
 
28
31
  service_name = "sanitize"
29
32
 
33
+ def __init__(
34
+ self, token: str, config: PangeaConfig | None = None, logger_name: str = "pangea", config_id: str | None = None
35
+ ) -> None:
36
+ """
37
+ Sanitize client
38
+
39
+ Initializes a new Sanitize client.
40
+
41
+ Args:
42
+ token: Pangea API token.
43
+ config: Configuration.
44
+ logger_name: Logger name.
45
+ config_id: Configuration ID.
46
+
47
+ Examples:
48
+ config = PangeaConfig(domain="aws.us.pangea.cloud")
49
+ authz = SanitizeAsync(token="pangea_token", config=config)
50
+ """
51
+
52
+ super().__init__(token, config, logger_name, config_id=config_id)
53
+
30
54
  async def sanitize(
31
55
  self,
32
56
  transfer_method: TransferMethod = TransferMethod.POST_URL,