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.
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/PKG-INFO +8 -9
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/README.md +3 -4
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/__init__.py +1 -1
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/request.py +9 -18
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/audit.py +10 -6
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/authz.py +2 -23
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/base.py +2 -21
- pangea_sdk-5.2.0b1/pangea/asyncio/services/data_guard.py +65 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/intel.py +0 -3
- pangea_sdk-5.2.0b1/pangea/asyncio/services/prompt_guard.py +39 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/sanitize.py +2 -26
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/share.py +1 -23
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/deep_verify.py +1 -7
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/dump_audit.py +7 -8
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/request.py +7 -16
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/__init__.py +2 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/audit/audit.py +10 -5
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/authz.py +1 -21
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/base.py +2 -16
- pangea_sdk-5.2.0b1/pangea/services/data_guard.py +147 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/intel.py +0 -18
- pangea_sdk-5.2.0b1/pangea/services/prompt_guard.py +48 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/redact.py +0 -16
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/sanitize.py +0 -22
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/share/share.py +1 -23
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/utils.py +9 -8
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pyproject.toml +7 -7
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/__init__.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/file_uploader.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/__init__.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/authn.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/embargo.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/file_scan.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/redact.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/asyncio/services/vault.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/audit_logger.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/config.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/crypto/rsa.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/deprecated.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/exceptions.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/file_uploader.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/py.typed +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/response.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/audit/exceptions.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/audit/models.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/audit/signing.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/audit/util.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/authn/authn.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/authn/models.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/embargo.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/file_scan.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/share/file_format.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/vault/models/asymmetric.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/vault/models/common.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/vault/models/keys.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/vault/models/secret.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/vault/models/symmetric.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/services/vault/vault.py +0 -0
- {pangea_sdk-5.2.0 → pangea_sdk-5.2.0b1}/pangea/tools.py +0 -0
- {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.
|
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.
|
21
|
-
Requires-Dist: cryptography (>=43.0.
|
22
|
-
Requires-Dist: deprecated (>=1.2.
|
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.
|
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
|
68
|
-
|
69
|
-
|
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
|
37
|
-
|
38
|
-
|
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
|
|
@@ -5,11 +5,12 @@ from __future__ import annotations
|
|
5
5
|
import asyncio
|
6
6
|
import json
|
7
7
|
import time
|
8
|
-
from typing import
|
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:
|
63
|
-
data["config_id"] = self.config_id
|
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)
|
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
|
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
|
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
|
-
|
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.
|
635
|
+
await self.fix_consistency_proofs(tree_sizes)
|
632
636
|
|
633
|
-
async def
|
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
|
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
|
-
|
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 =
|
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
|
-
"""
|
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\
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
-
|
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.
|
964
|
+
self.fix_consistency_proofs(tree_sizes)
|
960
965
|
|
961
|
-
def
|
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:
|