pangea-sdk 5.2.0__tar.gz → 5.2.0b1__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 (60) hide show
  1. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/PKG-INFO +8 -9
  2. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/README.md +3 -4
  3. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/__init__.py +1 -1
  4. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/request.py +9 -18
  5. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/audit.py +10 -6
  6. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/authz.py +2 -23
  7. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/base.py +2 -21
  8. pangea_sdk-5.2.0b1/pangea/asyncio/services/data_guard.py +65 -0
  9. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/intel.py +0 -3
  10. pangea_sdk-5.2.0b1/pangea/asyncio/services/prompt_guard.py +39 -0
  11. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/sanitize.py +2 -26
  12. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/share.py +1 -23
  13. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/deep_verify.py +1 -7
  14. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/dump_audit.py +7 -8
  15. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/request.py +7 -16
  16. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/__init__.py +2 -0
  17. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/audit/audit.py +10 -5
  18. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/authz.py +1 -21
  19. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/base.py +2 -16
  20. pangea_sdk-5.2.0b1/pangea/services/data_guard.py +147 -0
  21. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/intel.py +0 -18
  22. pangea_sdk-5.2.0b1/pangea/services/prompt_guard.py +48 -0
  23. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/redact.py +0 -16
  24. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/sanitize.py +0 -22
  25. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/share/share.py +1 -23
  26. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/utils.py +9 -8
  27. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pyproject.toml +7 -7
  28. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/__init__.py +0 -0
  29. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/file_uploader.py +0 -0
  30. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/__init__.py +0 -0
  31. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/authn.py +0 -0
  32. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/embargo.py +0 -0
  33. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/file_scan.py +0 -0
  34. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/redact.py +0 -0
  35. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/vault.py +0 -0
  36. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/audit_logger.py +0 -0
  37. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/config.py +0 -0
  38. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/crypto/rsa.py +0 -0
  39. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/deprecated.py +0 -0
  40. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/exceptions.py +0 -0
  41. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/file_uploader.py +0 -0
  42. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/py.typed +0 -0
  43. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/response.py +0 -0
  44. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/audit/exceptions.py +0 -0
  45. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/audit/models.py +0 -0
  46. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/audit/signing.py +0 -0
  47. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/audit/util.py +0 -0
  48. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/authn/authn.py +0 -0
  49. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/authn/models.py +0 -0
  50. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/embargo.py +0 -0
  51. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/file_scan.py +0 -0
  52. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/share/file_format.py +0 -0
  53. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/vault/models/asymmetric.py +0 -0
  54. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/vault/models/common.py +0 -0
  55. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/vault/models/keys.py +0 -0
  56. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/vault/models/secret.py +0 -0
  57. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/vault/models/symmetric.py +0 -0
  58. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/vault/vault.py +0 -0
  59. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/tools.py +0 -0
  60. {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/verify_audit.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pangea-sdk
3
- Version: 5.2.0
3
+ Version: 5.2.0b1
4
4
  Summary: Pangea API SDK
5
5
  Home-page: https://pangea.cloud/docs/sdk/python/
6
6
  License: MIT
@@ -17,11 +17,11 @@ Classifier: Programming Language :: Python :: 3.12
17
17
  Classifier: Programming Language :: Python :: 3.13
18
18
  Classifier: Topic :: Software Development
19
19
  Classifier: Topic :: Software Development :: Libraries
20
- Requires-Dist: aiohttp (>=3.11.10,<4.0.0)
21
- Requires-Dist: cryptography (>=43.0.3,<44.0.0)
22
- Requires-Dist: deprecated (>=1.2.15,<2.0.0)
20
+ Requires-Dist: aiohttp (>=3.10.10,<4.0.0)
21
+ Requires-Dist: cryptography (>=43.0.1,<44.0.0)
22
+ Requires-Dist: deprecated (>=1.2.14,<2.0.0)
23
23
  Requires-Dist: google-crc32c (>=1.5.0,<2.0.0)
24
- Requires-Dist: pydantic (>=2.10.3,<3.0.0)
24
+ Requires-Dist: pydantic (>=2.9.2,<3.0.0)
25
25
  Requires-Dist: python-dateutil (>=2.9.0,<3.0.0)
26
26
  Requires-Dist: requests (>=2.31.0,<3.0.0)
27
27
  Requires-Dist: requests-toolbelt (>=1.0.0,<2.0.0)
@@ -64,10 +64,9 @@ $ poetry add pangea-sdk
64
64
  #### Beta releases
65
65
 
66
66
  Pre-release versions may be available with the `b` (beta) denotation in the
67
- version number. These releases serve to preview Beta and Early Access services
68
- and APIs. Per Semantic Versioning, they are considered unstable and do not carry
69
- the same compatibility guarantees as stable releases.
70
- [Beta changelog](https://github.com/pangeacyber/pangea-python/blob/beta/CHANGELOG.md).
67
+ version number. These releases serve to preview beta services and APIs. Per
68
+ Semantic Versioning, they are considered unstable and do not carry the same
69
+ compatibility guarantees as stable releases. [Beta changelog](https://github.com/pangeacyber/pangea-python/blob/beta/CHANGELOG.md).
71
70
 
72
71
  Via pip:
73
72
 
@@ -33,10 +33,9 @@ $ poetry add pangea-sdk
33
33
  #### Beta releases
34
34
 
35
35
  Pre-release versions may be available with the `b` (beta) denotation in the
36
- version number. These releases serve to preview Beta and Early Access services
37
- and APIs. Per Semantic Versioning, they are considered unstable and do not carry
38
- the same compatibility guarantees as stable releases.
39
- [Beta changelog](https://github.com/pangeacyber/pangea-python/blob/beta/CHANGELOG.md).
36
+ version number. These releases serve to preview beta services and APIs. Per
37
+ Semantic Versioning, they are considered unstable and do not carry the same
38
+ compatibility guarantees as stable releases. [Beta changelog](https://github.com/pangeacyber/pangea-python/blob/beta/CHANGELOG.md).
40
39
 
41
40
  Via pip:
42
41
 
@@ -1,4 +1,4 @@
1
- __version__ = "5.2.0"
1
+ __version__ = "5.2.0beta1"
2
2
 
3
3
  from pangea.asyncio.request import PangeaRequestAsync
4
4
  from pangea.config import PangeaConfig
@@ -5,11 +5,12 @@ from __future__ import annotations
5
5
  import asyncio
6
6
  import json
7
7
  import time
8
- from typing import Any, Dict, List, Optional, Sequence, Tuple, Type, Union
8
+ from typing import Dict, List, Optional, Sequence, Tuple, Type, Union, cast
9
9
 
10
10
  import aiohttp
11
11
  from aiohttp import FormData
12
12
  from pydantic import BaseModel
13
+ from pydantic_core import to_jsonable_python
13
14
  from typing_extensions import Any, TypeVar
14
15
 
15
16
  import pangea.exceptions as pe
@@ -55,17 +56,20 @@ class PangeaRequestAsync(PangeaRequestBase):
55
56
  if data is None:
56
57
  data = {}
57
58
 
59
+ # Normalize.
60
+ data = cast(dict[str, Any], to_jsonable_python(data))
61
+
58
62
  if url is None:
59
63
  url = self._url(endpoint)
60
64
 
61
65
  # Set config ID if available
62
- if self.config_id and data.get("config_id", None) is None: # type: ignore[union-attr]
63
- data["config_id"] = self.config_id # type: ignore[index]
66
+ if self.config_id and data.get("config_id", None) is None:
67
+ data["config_id"] = self.config_id
64
68
 
65
69
  self.logger.debug(
66
70
  json.dumps({"service": self.service, "action": "post", "url": url, "data": data}, default=default_encoder)
67
71
  )
68
- transfer_method = data.get("transfer_method", None) # type: ignore[union-attr]
72
+ transfer_method = data.get("transfer_method", None)
69
73
 
70
74
  if files and type(data) is dict and (transfer_method == TransferMethod.POST_URL.value):
71
75
  requests_response = await self._full_post_presigned_url(
@@ -182,20 +186,7 @@ class PangeaRequestAsync(PangeaRequestBase):
182
186
  if resp.status < 200 or resp.status >= 300:
183
187
  raise pe.PresignedUploadError(f"presigned PUT failure: {resp.status}", await resp.text())
184
188
 
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
-
189
+ async def download_file(self, url: str, filename: Optional[str] = None) -> AttachedFile:
199
190
  self.logger.debug(
200
191
  json.dumps(
201
192
  {
@@ -174,16 +174,14 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
174
174
  verbose: Optional[bool] = None,
175
175
  ) -> PangeaResponse[LogResult]:
176
176
  """
177
- Log an event
177
+ Log an entry
178
178
 
179
179
  Create a log entry in the Secure Audit Log.
180
-
181
180
  Args:
182
181
  event (dict[str, Any]): event to be logged
183
182
  verify (bool, optional): True to verify logs consistency after response.
184
183
  sign_local (bool, optional): True to sign event with local key.
185
184
  verbose (bool, optional): True to get a more verbose response.
186
-
187
185
  Raises:
188
186
  AuditException: If an audit based api exception happens
189
187
  PangeaAPIException: If an API Error happens
@@ -194,7 +192,13 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
194
192
  Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/audit#log-an-entry).
195
193
 
196
194
  Examples:
197
- response = await audit.log_event({"message": "hello world"}, verbose=True)
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")
198
202
  """
199
203
 
200
204
  input = self._get_log_request(event, sign_local=sign_local, verify=verify, verbose=verbose)
@@ -628,9 +632,9 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
628
632
  if pub_root is not None:
629
633
  self.pub_roots[tree_size] = pub_root
630
634
 
631
- await self._fix_consistency_proofs(tree_sizes)
635
+ await self.fix_consistency_proofs(tree_sizes)
632
636
 
633
- async def _fix_consistency_proofs(self, tree_sizes: Iterable[int]) -> None:
637
+ async def fix_consistency_proofs(self, tree_sizes: Iterable[int]):
634
638
  # on very rare occasions, the consistency proof in Arweave may be wrong
635
639
  # override it with the proof from pangea (not the root hash, just the proof)
636
640
  for tree_size in tree_sizes:
@@ -1,12 +1,9 @@
1
1
  # Copyright 2022 Pangea Cyber Corporation
2
2
  # Author: Pangea Cyber Corporation
3
3
 
4
- from __future__ import annotations
5
-
6
4
  from typing import Any, Dict, List, Optional
7
5
 
8
6
  from pangea.asyncio.services.base import ServiceBaseAsync
9
- from pangea.config import PangeaConfig
10
7
  from pangea.response import PangeaResponse
11
8
  from pangea.services.authz import (
12
9
  CheckRequest,
@@ -39,8 +36,8 @@ class AuthZAsync(ServiceBaseAsync):
39
36
 
40
37
  Examples:
41
38
  import os
42
- from pangea.asyncio.services import AuthZAsync
43
39
  from pangea.config import PangeaConfig
40
+ from pangea.services import AuthZ
44
41
 
45
42
  PANGEA_TOKEN = os.getenv("PANGEA_AUTHZ_TOKEN")
46
43
 
@@ -52,25 +49,7 @@ class AuthZAsync(ServiceBaseAsync):
52
49
 
53
50
  service_name = "authz"
54
51
 
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
-
52
+ def __init__(self, token: str, config=None, logger_name="pangea", config_id: Optional[str] = None):
74
53
  super().__init__(token, config, logger_name, config_id=config_id)
75
54
 
76
55
  async def tuple_create(self, tuples: List[Tuple]) -> PangeaResponse[TupleCreateResult]:
@@ -1,11 +1,8 @@
1
1
  # Copyright 2022 Pangea Cyber Corporation
2
2
  # Author: Pangea Cyber Corporation
3
- from __future__ import annotations
4
3
 
5
4
  from typing import Dict, Optional, Type, Union
6
5
 
7
- from typing_extensions import override
8
-
9
6
  from pangea.asyncio.request import PangeaRequestAsync
10
7
  from pangea.exceptions import AcceptedRequestException
11
8
  from pangea.response import AttachedFile, PangeaResponse, PangeaResponseResult
@@ -26,7 +23,6 @@ class ServiceBaseAsync(ServiceBase):
26
23
 
27
24
  return self._request
28
25
 
29
- @override
30
26
  async def poll_result( # type: ignore[override]
31
27
  self,
32
28
  exception: Optional[AcceptedRequestException] = None,
@@ -40,8 +36,7 @@ class ServiceBaseAsync(ServiceBase):
40
36
  Returns request's result that has been accepted by the server
41
37
 
42
38
  Args:
43
- exception: Exception that was previously raised by the SDK on a call
44
- that is being processed.
39
+ exception (AcceptedRequestException): Exception raise by SDK on the call that is been processed.
45
40
 
46
41
  Returns:
47
42
  PangeaResponse
@@ -63,21 +58,7 @@ class ServiceBaseAsync(ServiceBase):
63
58
  else:
64
59
  raise AttributeError("Need to set exception, response or request_id")
65
60
 
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
-
61
+ async def download_file(self, url: str, filename: Optional[str] = None) -> AttachedFile: # type: ignore[override]
81
62
  return await self.request.download_file(url=url, filename=filename)
82
63
 
83
64
  async def close(self):
@@ -0,0 +1,65 @@
1
+ from __future__ import annotations
2
+
3
+ from pangea.asyncio.services.base import ServiceBaseAsync
4
+ from pangea.response import PangeaResponse, PangeaResponseResult
5
+ from pangea.services.data_guard import TextGuardResult
6
+
7
+
8
+ class DataGuard(ServiceBaseAsync):
9
+ """Data Guard service client.
10
+
11
+ Provides methods to interact with Pangea's Data Guard service.
12
+ """
13
+
14
+ service_name = "data-guard"
15
+
16
+ async def guard_text(
17
+ self,
18
+ text: str,
19
+ *,
20
+ recipe: str = "pangea_prompt_guard",
21
+ debug: bool = False,
22
+ ) -> PangeaResponse[TextGuardResult]:
23
+ """
24
+ Text guard (Beta)
25
+
26
+ Guard text.
27
+
28
+ How to install a [Beta release](https://pangea.cloud/docs/sdk/python/#beta-releases).
29
+
30
+ OperationId: data_guard_post_v1_text_guard
31
+
32
+ Args:
33
+ text: Text.
34
+ recipe: Recipe.
35
+ debug: Debug.
36
+
37
+ Examples:
38
+ response = await data_guard.guard_text("text")
39
+ """
40
+
41
+ return await self.request.post(
42
+ "v1/text/guard", TextGuardResult, data={"text": text, "recipe": recipe, "debug": debug}
43
+ )
44
+
45
+ async def guard_file(
46
+ self,
47
+ file_url: str,
48
+ ) -> PangeaResponse[PangeaResponseResult]:
49
+ """
50
+ File guard (Beta)
51
+
52
+ Guard a file URL.
53
+
54
+ How to install a [Beta release](https://pangea.cloud/docs/sdk/python/#beta-releases).
55
+
56
+ OperationId: data_guard_post_v1_file_guard
57
+
58
+ Args:
59
+ file_url: File URL.
60
+
61
+ Examples:
62
+ response = await data_guard.guard_file("https://example.org/file.txt")
63
+ """
64
+
65
+ return await self.request.post("v1/file/guard", PangeaResponseResult, data={"file_url": file_url})
@@ -790,7 +790,6 @@ 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,
794
793
  ) -> PangeaResponse[m.UserBreachedResult]:
795
794
  """
796
795
  Look up breached users
@@ -809,7 +808,6 @@ class UserIntelAsync(ServiceBaseAsync):
809
808
  verbose (bool, optional): Echo the API parameters in the response
810
809
  raw (bool, optional): Include raw data from this provider
811
810
  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
813
811
 
814
812
  Raises:
815
813
  PangeaAPIException: If an API Error happens
@@ -837,7 +835,6 @@ class UserIntelAsync(ServiceBaseAsync):
837
835
  end=end,
838
836
  verbose=verbose,
839
837
  raw=raw,
840
- cursor=cursor,
841
838
  )
842
839
  return await self.request.post(
843
840
  "v1/user/breached", m.UserBreachedResult, data=input.model_dump(exclude_none=True)
@@ -0,0 +1,39 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from pangea.asyncio.services.base import ServiceBaseAsync
6
+ from pangea.services.prompt_guard import GuardResult, Message
7
+
8
+ if TYPE_CHECKING:
9
+ from collections.abc import Iterable
10
+
11
+ from pangea.response import PangeaResponse
12
+
13
+
14
+ class PromptGuard(ServiceBaseAsync):
15
+ """Prompt Guard service client.
16
+
17
+ Provides methods to interact with Pangea's Prompt Guard service.
18
+ """
19
+
20
+ service_name = "prompt-guard"
21
+
22
+ async def guard(self, messages: Iterable[Message]) -> PangeaResponse[GuardResult]:
23
+ """
24
+ Guard (Beta)
25
+
26
+ Guard messages.
27
+
28
+ How to install a [Beta release](https://pangea.cloud/docs/sdk/python/#beta-releases).
29
+
30
+ OperationId: prompt_guard_post_v1_guard
31
+
32
+ Args:
33
+ messages: Messages..
34
+
35
+ Examples:
36
+ response = await prompt_guard.guard([Message(role="user", content="hello world")])
37
+ """
38
+
39
+ return await self.request.post("v1/guard", GuardResult, data={"messages": messages})
@@ -1,13 +1,10 @@
1
1
  # Copyright 2022 Pangea Cyber Corporation
2
2
  # Author: Pangea Cyber Corporation
3
- from __future__ import annotations
4
-
5
3
  import io
6
4
  from typing import List, Optional, Tuple
7
5
 
8
6
  import pangea.services.sanitize as m
9
7
  from pangea.asyncio.services.base import ServiceBaseAsync
10
- from pangea.config import PangeaConfig
11
8
  from pangea.response import PangeaResponse, TransferMethod
12
9
  from pangea.utils import FileUploadParams, get_file_upload_params
13
10
 
@@ -19,38 +16,17 @@ class SanitizeAsync(ServiceBaseAsync):
19
16
  import os
20
17
 
21
18
  # Pangea SDK
22
- from pangea.asyncio.services import SanitizeAsync
23
19
  from pangea.config import PangeaConfig
20
+ from pangea.asyncio.services import Sanitize
24
21
 
25
22
  PANGEA_SANITIZE_TOKEN = os.getenv("PANGEA_SANITIZE_TOKEN")
26
23
  config = PangeaConfig(domain="pangea.cloud")
27
24
 
28
- sanitize = SanitizeAsync(token=PANGEA_SANITIZE_TOKEN, config=config)
25
+ sanitize = Sanitize(token=PANGEA_SANITIZE_TOKEN, config=config)
29
26
  """
30
27
 
31
28
  service_name = "sanitize"
32
29
 
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
-
54
30
  async def sanitize(
55
31
  self,
56
32
  transfer_method: TransferMethod = TransferMethod.POST_URL,
@@ -7,38 +7,16 @@ from typing import Dict, List, Optional, Tuple, Union
7
7
 
8
8
  import pangea.services.share.share as m
9
9
  from pangea.asyncio.services.base import ServiceBaseAsync
10
- from pangea.config import PangeaConfig
11
10
  from pangea.response import PangeaResponse, TransferMethod
12
11
  from pangea.services.share.file_format import FileFormat
13
12
  from pangea.utils import get_file_size, get_file_upload_params
14
13
 
15
14
 
16
15
  class ShareAsync(ServiceBaseAsync):
17
- """Secure Share service client."""
16
+ """Share service client."""
18
17
 
19
18
  service_name = "share"
20
19
 
21
- def __init__(
22
- self, token: str, config: PangeaConfig | None = None, logger_name: str = "pangea", config_id: str | None = None
23
- ) -> None:
24
- """
25
- Secure Share client
26
-
27
- Initializes a new Secure Share client.
28
-
29
- Args:
30
- token: Pangea API token.
31
- config: Configuration.
32
- logger_name: Logger name.
33
- config_id: Configuration ID.
34
-
35
- Examples:
36
- config = PangeaConfig(domain="aws.us.pangea.cloud")
37
- authz = ShareAsync(token="pangea_token", config=config)
38
- """
39
-
40
- super().__init__(token, config, logger_name, config_id=config_id)
41
-
42
20
  async def buckets(self) -> PangeaResponse[m.BucketsResult]:
43
21
  """
44
22
  Buckets
@@ -263,14 +263,8 @@ def main():
263
263
  audit = init_audit(args.token, args.domain)
264
264
  errors = deep_verify(audit, args.file)
265
265
 
266
- print("\n\nWarnings:")
267
- val = errors["not_persisted"]
268
- print(f"\tnot_persisted: {val}")
269
-
270
- print("\nTotal errors:")
266
+ print("\n\nTotal errors:")
271
267
  for key, val in errors.items():
272
- if key == "not_persisted":
273
- continue
274
268
  print(f"\t{key.title()}: {val}")
275
269
  print()
276
270
 
@@ -63,12 +63,11 @@ def dump_before(audit: Audit, output: io.TextIOWrapper, start: datetime) -> int:
63
63
  cnt = 0
64
64
  if search_res.result and search_res.result.count > 0:
65
65
  leaf_index = search_res.result.events[0].leaf_index
66
- if leaf_index is not None:
67
- for row in reversed(search_res.result.events):
68
- if row.leaf_index != leaf_index:
69
- break
70
- dump_event(output, row, search_res)
71
- cnt += 1
66
+ for row in reversed(search_res.result.events):
67
+ if row.leaf_index != leaf_index:
68
+ break
69
+ dump_event(output, row, search_res)
70
+ cnt += 1
72
71
  print(f"Dumping before... {cnt} events")
73
72
  return cnt
74
73
 
@@ -90,7 +89,7 @@ def dump_after(audit: Audit, output: io.TextIOWrapper, start: datetime, last_eve
90
89
  cnt = 0
91
90
  if search_res.result and search_res.result.count > 0:
92
91
  leaf_index = search_res.result.events[0].leaf_index
93
- if leaf_index is not None and leaf_index == last_leaf_index:
92
+ if leaf_index == last_leaf_index:
94
93
  start_idx: int = 1 if last_event_hash == search_res.result.events[0].hash else 0
95
94
  for row in search_res.result.events[start_idx:]:
96
95
  if row.leaf_index != leaf_index:
@@ -125,7 +124,7 @@ def dump_page(
125
124
  msg = f"Dumping... {search_res.result.count} events"
126
125
 
127
126
  if search_res.result.count <= 1:
128
- return end, 0, True, "", 0
127
+ return end, 0 # type: ignore[return-value]
129
128
 
130
129
  offset = 0
131
130
  result_id = search_res.result.id
@@ -6,12 +6,13 @@ import copy
6
6
  import json
7
7
  import logging
8
8
  import time
9
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence, Tuple, Type, Union
9
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence, Tuple, Type, Union, cast
10
10
 
11
11
  import requests
12
12
  from pydantic import BaseModel
13
+ from pydantic_core import to_jsonable_python
13
14
  from requests.adapters import HTTPAdapter, Retry
14
- from requests_toolbelt import MultipartDecoder # type: ignore[import-untyped]
15
+ from requests_toolbelt import MultipartDecoder # type: ignore
15
16
  from typing_extensions import TypeVar
16
17
 
17
18
  import pangea
@@ -232,6 +233,9 @@ class PangeaRequest(PangeaRequestBase):
232
233
  if data is None:
233
234
  data = {}
234
235
 
236
+ # Normalize.
237
+ data = cast(dict[str, Any], to_jsonable_python(data))
238
+
235
239
  if url is None:
236
240
  url = self._url(endpoint)
237
241
 
@@ -402,20 +406,7 @@ class PangeaRequest(PangeaRequestBase):
402
406
 
403
407
  return self._check_response(pangea_response)
404
408
 
405
- def download_file(self, url: str, filename: str | None = None) -> AttachedFile:
406
- """
407
- Download file
408
-
409
- Download a file from the specified URL and save it with the given
410
- filename.
411
-
412
- Args:
413
- url: URL of the file to download
414
- filename: Name to save the downloaded file as. If not provided, the
415
- filename will be determined from the Content-Disposition header or
416
- the URL.
417
- """
418
-
409
+ def download_file(self, url: str, filename: Optional[str] = None) -> AttachedFile:
419
410
  self.logger.debug(
420
411
  json.dumps(
421
412
  {
@@ -1,9 +1,11 @@
1
1
  from .audit.audit import Audit
2
2
  from .authn.authn import AuthN
3
3
  from .authz import AuthZ
4
+ from .data_guard import DataGuard
4
5
  from .embargo import Embargo
5
6
  from .file_scan import FileScan
6
7
  from .intel import DomainIntel, FileIntel, IpIntel, UrlIntel, UserIntel
8
+ from .prompt_guard import PromptGuard
7
9
  from .redact import Redact
8
10
  from .sanitize import Sanitize
9
11
  from .share.share import Share
@@ -492,7 +492,7 @@ class Audit(ServiceBase, AuditBase):
492
492
  verbose: Optional[bool] = None,
493
493
  ) -> PangeaResponse[LogResult]:
494
494
  """
495
- Log an event
495
+ Log an entry
496
496
 
497
497
  Create a log entry in the Secure Audit Log.
498
498
 
@@ -501,7 +501,6 @@ class Audit(ServiceBase, AuditBase):
501
501
  verify (bool, optional): True to verify logs consistency after response.
502
502
  sign_local (bool, optional): True to sign event with local key.
503
503
  verbose (bool, optional): True to get a more verbose response.
504
-
505
504
  Raises:
506
505
  AuditException: If an audit based api exception happens
507
506
  PangeaAPIException: If an API Error happens
@@ -512,7 +511,13 @@ class Audit(ServiceBase, AuditBase):
512
511
  Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/audit#/v1/log).
513
512
 
514
513
  Examples:
515
- response = audit.log_event({"message": "hello world"}, verbose=True)
514
+ try:
515
+ log_response = audit.log({"message": "hello world"}, verbose=True)
516
+ print(f"Response. Hash: {log_response.result.hash}")
517
+ except pe.PangeaAPIException as e:
518
+ print(f"Request Error: {e.response.summary}")
519
+ for err in e.errors:
520
+ print(f"\\t{err.detail} \\n")
516
521
  """
517
522
 
518
523
  input = self._get_log_request(event, sign_local=sign_local, verify=verify, verbose=verbose)
@@ -956,9 +961,9 @@ class Audit(ServiceBase, AuditBase):
956
961
  if pub_root is not None:
957
962
  self.pub_roots[tree_size] = pub_root
958
963
 
959
- self._fix_consistency_proofs(tree_sizes)
964
+ self.fix_consistency_proofs(tree_sizes)
960
965
 
961
- def _fix_consistency_proofs(self, tree_sizes: Iterable[int]) -> None:
966
+ def fix_consistency_proofs(self, tree_sizes: Iterable[int]):
962
967
  # on very rare occasions, the consistency proof in Arweave may be wrong
963
968
  # override it with the proof from pangea (not the root hash, just the proof)
964
969
  for tree_size in tree_sizes: