pangea-sdk 5.2.0__py3-none-any.whl → 5.2.0b2__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 +1 -1
- pangea/asyncio/request.py +9 -18
- pangea/asyncio/services/__init__.py +2 -0
- pangea/asyncio/services/ai_guard.py +75 -0
- pangea/asyncio/services/audit.py +10 -6
- pangea/asyncio/services/authz.py +2 -23
- pangea/asyncio/services/base.py +1 -15
- pangea/asyncio/services/intel.py +0 -3
- pangea/asyncio/services/prompt_guard.py +73 -0
- pangea/asyncio/services/sanitize.py +2 -26
- pangea/asyncio/services/share.py +1 -23
- pangea/deep_verify.py +1 -7
- pangea/dump_audit.py +7 -8
- pangea/request.py +7 -16
- pangea/services/__init__.py +2 -0
- pangea/services/ai_guard.py +157 -0
- pangea/services/audit/audit.py +10 -5
- pangea/services/authz.py +1 -21
- pangea/services/base.py +1 -14
- pangea/services/intel.py +0 -18
- pangea/services/prompt_guard.py +83 -0
- pangea/services/redact.py +0 -16
- pangea/services/sanitize.py +0 -22
- pangea/services/share/share.py +1 -23
- {pangea_sdk-5.2.0.dist-info → pangea_sdk-5.2.0b2.dist-info}/METADATA +4 -4
- {pangea_sdk-5.2.0.dist-info → pangea_sdk-5.2.0b2.dist-info}/RECORD +27 -23
- {pangea_sdk-5.2.0.dist-info → pangea_sdk-5.2.0b2.dist-info}/WHEEL +0 -0
pangea/__init__.py
CHANGED
pangea/asyncio/request.py
CHANGED
@@ -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
|
{
|
@@ -1,9 +1,11 @@
|
|
1
|
+
from .ai_guard import AIGuardAsync
|
1
2
|
from .audit import AuditAsync
|
2
3
|
from .authn import AuthNAsync
|
3
4
|
from .authz import AuthZAsync
|
4
5
|
from .embargo import EmbargoAsync
|
5
6
|
from .file_scan import FileScanAsync
|
6
7
|
from .intel import DomainIntelAsync, FileIntelAsync, IpIntelAsync, UrlIntelAsync, UserIntelAsync
|
8
|
+
from .prompt_guard import PromptGuardAsync
|
7
9
|
from .redact import RedactAsync
|
8
10
|
from .sanitize import SanitizeAsync
|
9
11
|
from .share import ShareAsync
|
@@ -0,0 +1,75 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from pangea.asyncio.services.base import ServiceBaseAsync
|
4
|
+
from pangea.config import PangeaConfig
|
5
|
+
from pangea.response import PangeaResponse
|
6
|
+
from pangea.services.ai_guard import TextGuardResult
|
7
|
+
|
8
|
+
|
9
|
+
class AIGuardAsync(ServiceBaseAsync):
|
10
|
+
"""AI Guard service client.
|
11
|
+
|
12
|
+
Provides methods to interact with Pangea's AI Guard service.
|
13
|
+
|
14
|
+
Examples:
|
15
|
+
from pangea import PangeaConfig
|
16
|
+
from pangea.asyncio.services import AIGuardAsync
|
17
|
+
|
18
|
+
config = PangeaConfig(domain="aws.us.pangea.cloud")
|
19
|
+
ai_guard = AIGuardAsync(token="pangea_token", config=config)
|
20
|
+
"""
|
21
|
+
|
22
|
+
service_name = "ai-guard"
|
23
|
+
|
24
|
+
def __init__(
|
25
|
+
self, token: str, config: PangeaConfig | None = None, logger_name: str = "pangea", config_id: str | None = None
|
26
|
+
) -> None:
|
27
|
+
"""
|
28
|
+
AI Guard service client.
|
29
|
+
|
30
|
+
Initializes a new AI Guard client.
|
31
|
+
|
32
|
+
Args:
|
33
|
+
token: Pangea API token.
|
34
|
+
config: Pangea service configuration.
|
35
|
+
logger_name: Logger name.
|
36
|
+
config_id: Configuration ID.
|
37
|
+
|
38
|
+
Examples:
|
39
|
+
from pangea import PangeaConfig
|
40
|
+
from pangea.asyncio.services import AIGuardAsync
|
41
|
+
|
42
|
+
config = PangeaConfig(domain="aws.us.pangea.cloud")
|
43
|
+
ai_guard = AIGuardAsync(token="pangea_token", config=config)
|
44
|
+
"""
|
45
|
+
|
46
|
+
super().__init__(token, config, logger_name, config_id)
|
47
|
+
|
48
|
+
async def guard_text(
|
49
|
+
self,
|
50
|
+
text: str,
|
51
|
+
*,
|
52
|
+
recipe: str = "pangea_prompt_guard",
|
53
|
+
debug: bool = False,
|
54
|
+
) -> PangeaResponse[TextGuardResult]:
|
55
|
+
"""
|
56
|
+
Text guard (Beta)
|
57
|
+
|
58
|
+
Guard text.
|
59
|
+
|
60
|
+
How to install a [Beta release](https://pangea.cloud/docs/sdk/python/#beta-releases).
|
61
|
+
|
62
|
+
OperationId: ai_guard_post_v1beta_text_guard
|
63
|
+
|
64
|
+
Args:
|
65
|
+
text: Text.
|
66
|
+
recipe: Recipe.
|
67
|
+
debug: Debug.
|
68
|
+
|
69
|
+
Examples:
|
70
|
+
response = await ai_guard.guard_text("text")
|
71
|
+
"""
|
72
|
+
|
73
|
+
return await self.request.post(
|
74
|
+
"v1beta/text/guard", TextGuardResult, data={"text": text, "recipe": recipe, "debug": debug}
|
75
|
+
)
|
pangea/asyncio/services/audit.py
CHANGED
@@ -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:
|
pangea/asyncio/services/authz.py
CHANGED
@@ -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]:
|
pangea/asyncio/services/base.py
CHANGED
@@ -63,21 +63,7 @@ class ServiceBaseAsync(ServiceBase):
|
|
63
63
|
else:
|
64
64
|
raise AttributeError("Need to set exception, response or request_id")
|
65
65
|
|
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
|
-
|
66
|
+
async def download_file(self, url: str, filename: Optional[str] = None) -> AttachedFile: # type: ignore[override]
|
81
67
|
return await self.request.download_file(url=url, filename=filename)
|
82
68
|
|
83
69
|
async def close(self):
|
pangea/asyncio/services/intel.py
CHANGED
@@ -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,73 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import TYPE_CHECKING
|
4
|
+
|
5
|
+
from pangea.asyncio.services.base import ServiceBaseAsync
|
6
|
+
from pangea.config import PangeaConfig
|
7
|
+
from pangea.services.prompt_guard import GuardResult, Message
|
8
|
+
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from collections.abc import Iterable
|
11
|
+
|
12
|
+
from pangea.response import PangeaResponse
|
13
|
+
|
14
|
+
|
15
|
+
class PromptGuardAsync(ServiceBaseAsync):
|
16
|
+
"""Prompt Guard service client.
|
17
|
+
|
18
|
+
Provides methods to interact with Pangea's Prompt Guard service.
|
19
|
+
|
20
|
+
Examples:
|
21
|
+
from pangea import PangeaConfig
|
22
|
+
from pangea.asyncio.services import PromptGuardAsync
|
23
|
+
|
24
|
+
config = PangeaConfig(domain="aws.us.pangea.cloud")
|
25
|
+
prompt_guard = PromptGuardAsync(token="pangea_token", config=config)
|
26
|
+
"""
|
27
|
+
|
28
|
+
service_name = "prompt-guard"
|
29
|
+
|
30
|
+
def __init__(
|
31
|
+
self, token: str, config: PangeaConfig | None = None, logger_name: str = "pangea", config_id: str | None = None
|
32
|
+
) -> None:
|
33
|
+
"""
|
34
|
+
Prompt Guard service client.
|
35
|
+
|
36
|
+
Initializes a new Prompt Guard client.
|
37
|
+
|
38
|
+
Args:
|
39
|
+
token: Pangea API token.
|
40
|
+
config: Pangea service configuration.
|
41
|
+
logger_name: Logger name.
|
42
|
+
config_id: Configuration ID.
|
43
|
+
|
44
|
+
Examples:
|
45
|
+
from pangea import PangeaConfig
|
46
|
+
from pangea.asyncio.services import PromptGuardAsync
|
47
|
+
|
48
|
+
config = PangeaConfig(domain="aws.us.pangea.cloud")
|
49
|
+
prompt_guard = PromptGuardAsync(token="pangea_token", config=config)
|
50
|
+
"""
|
51
|
+
|
52
|
+
super().__init__(token, config, logger_name, config_id)
|
53
|
+
|
54
|
+
async def guard(self, messages: Iterable[Message]) -> PangeaResponse[GuardResult]:
|
55
|
+
"""
|
56
|
+
Guard (Beta)
|
57
|
+
|
58
|
+
Guard messages.
|
59
|
+
|
60
|
+
How to install a [Beta release](https://pangea.cloud/docs/sdk/python/#beta-releases).
|
61
|
+
|
62
|
+
OperationId: prompt_guard_post_v1beta_guard
|
63
|
+
|
64
|
+
Args:
|
65
|
+
messages: Messages.
|
66
|
+
|
67
|
+
Examples:
|
68
|
+
from pangea.asyncio.services.prompt_guard import Message
|
69
|
+
|
70
|
+
response = await prompt_guard.guard([Message(role="user", content="hello world")])
|
71
|
+
"""
|
72
|
+
|
73
|
+
return await self.request.post("v1beta/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,
|
pangea/asyncio/services/share.py
CHANGED
@@ -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
|
pangea/deep_verify.py
CHANGED
@@ -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
|
|
pangea/dump_audit.py
CHANGED
@@ -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
|
pangea/request.py
CHANGED
@@ -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
|
{
|
pangea/services/__init__.py
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
from .ai_guard import AIGuard
|
1
2
|
from .audit.audit import Audit
|
2
3
|
from .authn.authn import AuthN
|
3
4
|
from .authz import AuthZ
|
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
|
@@ -0,0 +1,157 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import List, Optional
|
4
|
+
|
5
|
+
from typing_extensions import Literal
|
6
|
+
|
7
|
+
from pangea.config import PangeaConfig
|
8
|
+
from pangea.response import PangeaResponse, PangeaResponseResult
|
9
|
+
from pangea.services.base import ServiceBase
|
10
|
+
from pangea.services.intel import UserBreachedData
|
11
|
+
|
12
|
+
|
13
|
+
class TextGuardSecurityIssues(PangeaResponseResult):
|
14
|
+
compromised_email_addresses: int
|
15
|
+
malicious_domain_count: int
|
16
|
+
malicious_ip_count: int
|
17
|
+
malicious_url_count: int
|
18
|
+
redact_rule_match_count: int
|
19
|
+
|
20
|
+
|
21
|
+
class TextGuardFindings(PangeaResponseResult):
|
22
|
+
artifact_count: Optional[int] = None
|
23
|
+
malicious_count: Optional[int] = None
|
24
|
+
security_issues: TextGuardSecurityIssues
|
25
|
+
|
26
|
+
|
27
|
+
class RedactRecognizerResult(PangeaResponseResult):
|
28
|
+
field_type: str
|
29
|
+
"""The entity name."""
|
30
|
+
|
31
|
+
score: float
|
32
|
+
"""The certainty score that the entity matches this specific snippet."""
|
33
|
+
|
34
|
+
text: str
|
35
|
+
"""The text snippet that matched."""
|
36
|
+
|
37
|
+
start: int
|
38
|
+
"""The starting index of a snippet."""
|
39
|
+
|
40
|
+
end: int
|
41
|
+
"""The ending index of a snippet."""
|
42
|
+
|
43
|
+
redacted: bool
|
44
|
+
"""Indicates if this rule was used to anonymize a text snippet."""
|
45
|
+
|
46
|
+
|
47
|
+
class RedactReport(PangeaResponseResult):
|
48
|
+
count: int
|
49
|
+
recognizer_results: List[RedactRecognizerResult]
|
50
|
+
|
51
|
+
|
52
|
+
class IntelResults(PangeaResponseResult):
|
53
|
+
category: List[str]
|
54
|
+
"""
|
55
|
+
The categories that apply to this indicator as determined by the provider.
|
56
|
+
"""
|
57
|
+
|
58
|
+
score: int
|
59
|
+
"""The score, given by the Pangea service, for the indicator."""
|
60
|
+
|
61
|
+
verdict: Literal["malicious", "suspicious", "unknown", "benign"]
|
62
|
+
|
63
|
+
|
64
|
+
class TextGuardReport(PangeaResponseResult):
|
65
|
+
domain_intel: Optional[IntelResults] = None
|
66
|
+
ip_intel: Optional[IntelResults] = None
|
67
|
+
redact: RedactReport
|
68
|
+
url_intel: Optional[IntelResults] = None
|
69
|
+
user_intel: Optional[UserBreachedData] = None
|
70
|
+
|
71
|
+
|
72
|
+
class TextGuardArtifact(PangeaResponseResult):
|
73
|
+
defanged: bool
|
74
|
+
end: int
|
75
|
+
start: int
|
76
|
+
type: str
|
77
|
+
value: str
|
78
|
+
verdict: Optional[str] = None
|
79
|
+
"""The verdict, given by the Pangea service, for the indicator."""
|
80
|
+
|
81
|
+
|
82
|
+
class TextGuardResult(PangeaResponseResult):
|
83
|
+
artifacts: Optional[List[TextGuardArtifact]] = None
|
84
|
+
findings: TextGuardFindings
|
85
|
+
redacted_prompt: str
|
86
|
+
|
87
|
+
# `debug=True` only.
|
88
|
+
report: Optional[TextGuardReport] = None
|
89
|
+
|
90
|
+
|
91
|
+
class AIGuard(ServiceBase):
|
92
|
+
"""AI Guard service client.
|
93
|
+
|
94
|
+
Provides methods to interact with Pangea's AI Guard service.
|
95
|
+
|
96
|
+
Examples:
|
97
|
+
from pangea import PangeaConfig
|
98
|
+
from pangea.services import AIGuard
|
99
|
+
|
100
|
+
config = PangeaConfig(domain="aws.us.pangea.cloud")
|
101
|
+
ai_guard = AIGuard(token="pangea_token", config=config)
|
102
|
+
"""
|
103
|
+
|
104
|
+
service_name = "ai-guard"
|
105
|
+
|
106
|
+
def __init__(
|
107
|
+
self, token: str, config: PangeaConfig | None = None, logger_name: str = "pangea", config_id: str | None = None
|
108
|
+
) -> None:
|
109
|
+
"""
|
110
|
+
AI Guard service client.
|
111
|
+
|
112
|
+
Initializes a new AI Guard client.
|
113
|
+
|
114
|
+
Args:
|
115
|
+
token: Pangea API token.
|
116
|
+
config: Pangea service configuration.
|
117
|
+
logger_name: Logger name.
|
118
|
+
config_id: Configuration ID.
|
119
|
+
|
120
|
+
Examples:
|
121
|
+
from pangea import PangeaConfig
|
122
|
+
from pangea.services import AIGuard
|
123
|
+
|
124
|
+
config = PangeaConfig(domain="aws.us.pangea.cloud")
|
125
|
+
ai_guard = AIGuard(token="pangea_token", config=config)
|
126
|
+
"""
|
127
|
+
|
128
|
+
super().__init__(token, config, logger_name, config_id)
|
129
|
+
|
130
|
+
def guard_text(
|
131
|
+
self,
|
132
|
+
text: str,
|
133
|
+
*,
|
134
|
+
recipe: str = "pangea_prompt_guard",
|
135
|
+
debug: bool = False,
|
136
|
+
) -> PangeaResponse[TextGuardResult]:
|
137
|
+
"""
|
138
|
+
Text guard (Beta)
|
139
|
+
|
140
|
+
Guard text.
|
141
|
+
|
142
|
+
How to install a [Beta release](https://pangea.cloud/docs/sdk/python/#beta-releases).
|
143
|
+
|
144
|
+
OperationId: ai_guard_post_v1beta_text_guard
|
145
|
+
|
146
|
+
Args:
|
147
|
+
text: Text.
|
148
|
+
recipe: Recipe.
|
149
|
+
debug: Debug.
|
150
|
+
|
151
|
+
Examples:
|
152
|
+
response = ai_guard.guard_text("text")
|
153
|
+
"""
|
154
|
+
|
155
|
+
return self.request.post(
|
156
|
+
"v1beta/text/guard", TextGuardResult, data={"text": text, "recipe": recipe, "debug": debug}
|
157
|
+
)
|
pangea/services/audit/audit.py
CHANGED
@@ -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:
|
pangea/services/authz.py
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
|
-
from __future__ import annotations
|
4
3
|
|
5
4
|
import enum
|
6
5
|
from typing import Any, Dict, List, Optional, Union
|
7
6
|
|
8
|
-
from pangea.config import PangeaConfig
|
9
7
|
from pangea.response import APIRequestModel, APIResponseModel, PangeaResponse, PangeaResponseResult
|
10
8
|
from pangea.services.base import ServiceBase
|
11
9
|
|
@@ -173,25 +171,7 @@ class AuthZ(ServiceBase):
|
|
173
171
|
|
174
172
|
service_name = "authz"
|
175
173
|
|
176
|
-
def __init__(
|
177
|
-
self, token: str, config: PangeaConfig | None = None, logger_name: str = "pangea", config_id: str | None = None
|
178
|
-
) -> None:
|
179
|
-
"""
|
180
|
-
AuthZ client
|
181
|
-
|
182
|
-
Initializes a new AuthZ client.
|
183
|
-
|
184
|
-
Args:
|
185
|
-
token: Pangea API token.
|
186
|
-
config: Configuration.
|
187
|
-
logger_name: Logger name.
|
188
|
-
config_id: Configuration ID.
|
189
|
-
|
190
|
-
Examples:
|
191
|
-
config = PangeaConfig(domain="aws.us.pangea.cloud")
|
192
|
-
authz = AuthZ(token="pangea_token", config=config)
|
193
|
-
"""
|
194
|
-
|
174
|
+
def __init__(self, token: str, config=None, logger_name="pangea", config_id: Optional[str] = None):
|
195
175
|
super().__init__(token, config, logger_name, config_id=config_id)
|
196
176
|
|
197
177
|
def tuple_create(self, tuples: List[Tuple]) -> PangeaResponse[TupleCreateResult]:
|
pangea/services/base.py
CHANGED
@@ -101,18 +101,5 @@ class ServiceBase(object):
|
|
101
101
|
else:
|
102
102
|
raise AttributeError("Need to set exception, response or request_id")
|
103
103
|
|
104
|
-
def download_file(self, url: str, filename: str
|
105
|
-
"""
|
106
|
-
Download file
|
107
|
-
|
108
|
-
Download a file from the specified URL and save it with the given
|
109
|
-
filename.
|
110
|
-
|
111
|
-
Args:
|
112
|
-
url: URL of the file to download
|
113
|
-
filename: Name to save the downloaded file as. If not provided, the
|
114
|
-
filename will be determined from the Content-Disposition header or
|
115
|
-
the URL.
|
116
|
-
"""
|
117
|
-
|
104
|
+
def download_file(self, url: str, filename: Optional[str] = None) -> AttachedFile:
|
118
105
|
return self.request.download_file(url=url, filename=filename)
|
pangea/services/intel.py
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
|
-
from __future__ import annotations
|
4
|
-
|
5
3
|
import enum
|
6
4
|
import hashlib
|
7
5
|
from typing import Dict, List, Optional
|
@@ -1239,7 +1237,6 @@ class UserBreachedRequest(IntelCommonRequest):
|
|
1239
1237
|
phone_number (str): A phone number to search for. minLength: 7, maxLength: 15.
|
1240
1238
|
start (str): Earliest date for search
|
1241
1239
|
end (str): Latest date for search
|
1242
|
-
cursor (str, optional): A token given in the raw response from SpyCloud. Post this back to paginate results
|
1243
1240
|
"""
|
1244
1241
|
|
1245
1242
|
email: Optional[str] = None
|
@@ -1248,7 +1245,6 @@ class UserBreachedRequest(IntelCommonRequest):
|
|
1248
1245
|
phone_number: Optional[str] = None
|
1249
1246
|
start: Optional[str] = None
|
1250
1247
|
end: Optional[str] = None
|
1251
|
-
cursor: Optional[str] = None
|
1252
1248
|
|
1253
1249
|
|
1254
1250
|
class UserBreachedBulkRequest(IntelCommonRequest):
|
@@ -1391,7 +1387,6 @@ class UserIntel(ServiceBase):
|
|
1391
1387
|
verbose: Optional[bool] = None,
|
1392
1388
|
raw: Optional[bool] = None,
|
1393
1389
|
provider: Optional[str] = None,
|
1394
|
-
cursor: Optional[str] = None,
|
1395
1390
|
) -> PangeaResponse[UserBreachedResult]:
|
1396
1391
|
"""
|
1397
1392
|
Look up breached users
|
@@ -1410,7 +1405,6 @@ class UserIntel(ServiceBase):
|
|
1410
1405
|
verbose (bool, optional): Echo the API parameters in the response
|
1411
1406
|
raw (bool, optional): Include raw data from this provider
|
1412
1407
|
provider (str, optional): Use reputation data from this provider: "spycloud"
|
1413
|
-
cursor (str, optional): A token given in the raw response from SpyCloud. Post this back to paginate results
|
1414
1408
|
|
1415
1409
|
Raises:
|
1416
1410
|
PangeaAPIException: If an API Error happens
|
@@ -1438,7 +1432,6 @@ class UserIntel(ServiceBase):
|
|
1438
1432
|
end=end,
|
1439
1433
|
verbose=verbose,
|
1440
1434
|
raw=raw,
|
1441
|
-
cursor=cursor,
|
1442
1435
|
)
|
1443
1436
|
return self.request.post("v1/user/breached", UserBreachedResult, data=input.model_dump(exclude_none=True))
|
1444
1437
|
|
@@ -1599,17 +1592,6 @@ class UserIntel(ServiceBase):
|
|
1599
1592
|
|
1600
1593
|
@staticmethod
|
1601
1594
|
def is_password_breached(response: PangeaResponse[UserBreachedResult], hash: str) -> PasswordStatus:
|
1602
|
-
"""
|
1603
|
-
Check if a password was breached
|
1604
|
-
|
1605
|
-
Helper function that simplifies searching the response's raw data for
|
1606
|
-
the full hash.
|
1607
|
-
|
1608
|
-
Args:
|
1609
|
-
response: API response from an earlier request
|
1610
|
-
hash: Password hash
|
1611
|
-
"""
|
1612
|
-
|
1613
1595
|
if response.result.raw_data is None: # type: ignore[union-attr]
|
1614
1596
|
raise PangeaException("Need raw data to check if hash is breached. Send request with raw=true")
|
1615
1597
|
|
@@ -0,0 +1,83 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import TYPE_CHECKING, Optional
|
4
|
+
|
5
|
+
from pangea.config import PangeaConfig
|
6
|
+
from pangea.response import APIRequestModel, PangeaResponse, PangeaResponseResult
|
7
|
+
from pangea.services.base import ServiceBase
|
8
|
+
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from collections.abc import Iterable
|
11
|
+
|
12
|
+
|
13
|
+
class Message(APIRequestModel):
|
14
|
+
role: str
|
15
|
+
content: str
|
16
|
+
|
17
|
+
|
18
|
+
class GuardResult(PangeaResponseResult):
|
19
|
+
detected: bool
|
20
|
+
type: Optional[str] = None
|
21
|
+
detector: Optional[str] = None
|
22
|
+
confidence: int
|
23
|
+
|
24
|
+
|
25
|
+
class PromptGuard(ServiceBase):
|
26
|
+
"""Prompt Guard service client.
|
27
|
+
|
28
|
+
Provides methods to interact with Pangea's Prompt Guard service.
|
29
|
+
|
30
|
+
Examples:
|
31
|
+
from pangea import PangeaConfig
|
32
|
+
from pangea.services import PromptGuard
|
33
|
+
|
34
|
+
config = PangeaConfig(domain="aws.us.pangea.cloud")
|
35
|
+
prompt_guard = PromptGuard(token="pangea_token", config=config)
|
36
|
+
"""
|
37
|
+
|
38
|
+
service_name = "prompt-guard"
|
39
|
+
|
40
|
+
def __init__(
|
41
|
+
self, token: str, config: PangeaConfig | None = None, logger_name: str = "pangea", config_id: str | None = None
|
42
|
+
) -> None:
|
43
|
+
"""
|
44
|
+
Prompt Guard service client.
|
45
|
+
|
46
|
+
Initializes a new Prompt Guard client.
|
47
|
+
|
48
|
+
Args:
|
49
|
+
token: Pangea API token.
|
50
|
+
config: Pangea service configuration.
|
51
|
+
logger_name: Logger name.
|
52
|
+
config_id: Configuration ID.
|
53
|
+
|
54
|
+
Examples:
|
55
|
+
from pangea import PangeaConfig
|
56
|
+
from pangea.services import PromptGuard
|
57
|
+
|
58
|
+
config = PangeaConfig(domain="aws.us.pangea.cloud")
|
59
|
+
prompt_guard = PromptGuard(token="pangea_token", config=config)
|
60
|
+
"""
|
61
|
+
|
62
|
+
super().__init__(token, config, logger_name, config_id)
|
63
|
+
|
64
|
+
def guard(self, messages: Iterable[Message]) -> PangeaResponse[GuardResult]:
|
65
|
+
"""
|
66
|
+
Guard (Beta)
|
67
|
+
|
68
|
+
Guard messages.
|
69
|
+
|
70
|
+
How to install a [Beta release](https://pangea.cloud/docs/sdk/python/#beta-releases).
|
71
|
+
|
72
|
+
OperationId: prompt_guard_post_v1beta_guard
|
73
|
+
|
74
|
+
Args:
|
75
|
+
messages: Messages.
|
76
|
+
|
77
|
+
Examples:
|
78
|
+
from pangea.services.prompt_guard import Message
|
79
|
+
|
80
|
+
response = prompt_guard.guard([Message(role="user", content="hello world")])
|
81
|
+
"""
|
82
|
+
|
83
|
+
return self.request.post("v1beta/guard", GuardResult, data={"messages": messages})
|
pangea/services/redact.py
CHANGED
@@ -66,18 +66,6 @@ class RedactRequest(APIRequestModel):
|
|
66
66
|
rulesets: Optional[List[str]] = None
|
67
67
|
return_result: Optional[bool] = None
|
68
68
|
redaction_method_overrides: Optional[RedactionMethodOverrides] = None
|
69
|
-
vault_parameters: Optional[VaultParameters] = None
|
70
|
-
|
71
|
-
llm_request: Optional[bool] = None
|
72
|
-
"""Is this redact call going to be used in an LLM request?"""
|
73
|
-
|
74
|
-
|
75
|
-
class VaultParameters(APIRequestModel):
|
76
|
-
fpe_key_id: Optional[str] = None
|
77
|
-
"""A vault key ID of an exportable key used to redact with FPE instead of using the service config default."""
|
78
|
-
|
79
|
-
salt_secret_id: Optional[str] = None
|
80
|
-
"""A vault secret ID of a secret used to salt a hash instead of using the service config default."""
|
81
69
|
|
82
70
|
|
83
71
|
class RecognizerResult(APIResponseModel):
|
@@ -146,10 +134,6 @@ class StructuredRequest(APIRequestModel):
|
|
146
134
|
rulesets: Optional[List[str]] = None
|
147
135
|
return_result: Optional[bool] = None
|
148
136
|
redaction_method_overrides: Optional[RedactionMethodOverrides] = None
|
149
|
-
vault_parameters: Optional[VaultParameters] = None
|
150
|
-
|
151
|
-
llm_request: Optional[bool] = None
|
152
|
-
"""Is this redact call going to be used in an LLM request?"""
|
153
137
|
|
154
138
|
|
155
139
|
class StructuredResult(PangeaResponseResult):
|
pangea/services/sanitize.py
CHANGED
@@ -7,7 +7,6 @@ from typing import Dict, List, Optional, Tuple
|
|
7
7
|
|
8
8
|
from pydantic import Field
|
9
9
|
|
10
|
-
from pangea.config import PangeaConfig
|
11
10
|
from pangea.response import APIRequestModel, PangeaResponse, PangeaResponseResult, TransferMethod
|
12
11
|
from pangea.services.base import ServiceBase
|
13
12
|
from pangea.utils import FileUploadParams, get_file_upload_params
|
@@ -198,27 +197,6 @@ class Sanitize(ServiceBase):
|
|
198
197
|
|
199
198
|
service_name = "sanitize"
|
200
199
|
|
201
|
-
def __init__(
|
202
|
-
self, token: str, config: PangeaConfig | None = None, logger_name: str = "pangea", config_id: str | None = None
|
203
|
-
) -> None:
|
204
|
-
"""
|
205
|
-
Sanitize client
|
206
|
-
|
207
|
-
Initializes a new Sanitize client.
|
208
|
-
|
209
|
-
Args:
|
210
|
-
token: Pangea API token.
|
211
|
-
config: Configuration.
|
212
|
-
logger_name: Logger name.
|
213
|
-
config_id: Configuration ID.
|
214
|
-
|
215
|
-
Examples:
|
216
|
-
config = PangeaConfig(domain="aws.us.pangea.cloud")
|
217
|
-
authz = Sanitize(token="pangea_token", config=config)
|
218
|
-
"""
|
219
|
-
|
220
|
-
super().__init__(token, config, logger_name, config_id=config_id)
|
221
|
-
|
222
200
|
def sanitize(
|
223
201
|
self,
|
224
202
|
transfer_method: TransferMethod = TransferMethod.POST_URL,
|
pangea/services/share/share.py
CHANGED
@@ -6,7 +6,6 @@ import enum
|
|
6
6
|
import io
|
7
7
|
from typing import Dict, List, NewType, Optional, Tuple, Union
|
8
8
|
|
9
|
-
from pangea.config import PangeaConfig
|
10
9
|
from pangea.response import APIRequestModel, PangeaResponse, PangeaResponseResult, TransferMethod
|
11
10
|
from pangea.services.base import ServiceBase
|
12
11
|
from pangea.services.share.file_format import FileFormat
|
@@ -670,31 +669,10 @@ class BucketsResult(PangeaResponseResult):
|
|
670
669
|
|
671
670
|
|
672
671
|
class Share(ServiceBase):
|
673
|
-
"""
|
672
|
+
"""Share service client."""
|
674
673
|
|
675
674
|
service_name = "share"
|
676
675
|
|
677
|
-
def __init__(
|
678
|
-
self, token: str, config: PangeaConfig | None = None, logger_name: str = "pangea", config_id: str | None = None
|
679
|
-
) -> None:
|
680
|
-
"""
|
681
|
-
Secure Share client
|
682
|
-
|
683
|
-
Initializes a new Secure Share client.
|
684
|
-
|
685
|
-
Args:
|
686
|
-
token: Pangea API token.
|
687
|
-
config: Configuration.
|
688
|
-
logger_name: Logger name.
|
689
|
-
config_id: Configuration ID.
|
690
|
-
|
691
|
-
Examples:
|
692
|
-
config = PangeaConfig(domain="aws.us.pangea.cloud")
|
693
|
-
authz = Share(token="pangea_token", config=config)
|
694
|
-
"""
|
695
|
-
|
696
|
-
super().__init__(token, config, logger_name, config_id=config_id)
|
697
|
-
|
698
676
|
def buckets(self) -> PangeaResponse[BucketsResult]:
|
699
677
|
"""
|
700
678
|
Buckets
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pangea-sdk
|
3
|
-
Version: 5.2.
|
3
|
+
Version: 5.2.0b2
|
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.
|
20
|
+
Requires-Dist: aiohttp (>=3.10.10,<4.0.0)
|
21
21
|
Requires-Dist: cryptography (>=43.0.3,<44.0.0)
|
22
|
-
Requires-Dist: deprecated (>=1.2.
|
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)
|
@@ -1,47 +1,51 @@
|
|
1
|
-
pangea/__init__.py,sha256=
|
1
|
+
pangea/__init__.py,sha256=YW7Y-Lb2D13OAwVGQH3AL6xmqtVbLjqDaQFROwi_Yd4,251
|
2
2
|
pangea/asyncio/__init__.py,sha256=kjEMkqMQ521LlMSu5jn3_WgweyArwVZ2C-s3x7mR6Pk,45
|
3
3
|
pangea/asyncio/file_uploader.py,sha256=wI7epib7Rc5jtZw4eJ1L1SlmutDG6CPv59C8N2UPhtY,1436
|
4
|
-
pangea/asyncio/request.py,sha256=
|
5
|
-
pangea/asyncio/services/__init__.py,sha256=
|
6
|
-
pangea/asyncio/services/
|
4
|
+
pangea/asyncio/request.py,sha256=Pwk3dAzO_A7I1NyrIqK5V80xvtAL80KiNSjbrXjzQzA,17519
|
5
|
+
pangea/asyncio/services/__init__.py,sha256=L6Tdhjfx_ZECHskhLMPaCcOefi-r-imw6q_zlU4j-FY,464
|
6
|
+
pangea/asyncio/services/ai_guard.py,sha256=FxO8GRBztfOhpd7Kzj4-jWAiG0kb89-jVWL_AnbEou8,2142
|
7
|
+
pangea/asyncio/services/audit.py,sha256=bZ7gdkVWkzqLqUVc1Wnf3oDAaCLg97-zTWhY8UdX0_Y,26549
|
7
8
|
pangea/asyncio/services/authn.py,sha256=rPeLJweL8mYH_t4ebcQn4n_Wglr3kClKNnCXNCimZU4,46622
|
8
|
-
pangea/asyncio/services/authz.py,sha256=
|
9
|
-
pangea/asyncio/services/base.py,sha256=
|
9
|
+
pangea/asyncio/services/authz.py,sha256=HgW9R8DeW19wS7fpgq0NWOx41wZWcn6NYS4NMbi8p1A,9482
|
10
|
+
pangea/asyncio/services/base.py,sha256=1_W3ImEdH5C3rGynC_JWeKx1E2X9HHvUWBa5uRiZFNM,2763
|
10
11
|
pangea/asyncio/services/embargo.py,sha256=ctzj3kip6xos-Eu3JuOskrCGYC8T3JlsgAopZHiPSXM,3068
|
11
12
|
pangea/asyncio/services/file_scan.py,sha256=PLG1O-PL4Yk9uY9D6NbMrZ5LHg70Z311s7bFe46UMZA,7108
|
12
|
-
pangea/asyncio/services/intel.py,sha256=
|
13
|
+
pangea/asyncio/services/intel.py,sha256=cCm3VwWxUzEUCNhuPCeejJvr4uOeLXuYDbDwTzNG6Aw,38121
|
14
|
+
pangea/asyncio/services/prompt_guard.py,sha256=gRh7Z-ZS21EN8vXXnK1OJ5kYvmOWxKppia8tZYeuhmM,2202
|
13
15
|
pangea/asyncio/services/redact.py,sha256=jRNtXr_DZ_cY7guhut-eZmOEhy2uN_VCXrjGH6bkh74,7265
|
14
|
-
pangea/asyncio/services/sanitize.py,sha256=
|
15
|
-
pangea/asyncio/services/share.py,sha256=
|
16
|
+
pangea/asyncio/services/sanitize.py,sha256=bf98J-s-P51oSKqNBgR0wj5mlHOCBwpjWz7k0NdXCKQ,7899
|
17
|
+
pangea/asyncio/services/share.py,sha256=UYJeUKA3NLSFA8R0X7B6yBi2U1g4q04O4ftrp9SMCzA,26097
|
16
18
|
pangea/asyncio/services/vault.py,sha256=VqrJGSEdq6MlZRI6cJpkthhIsqLClSQdgVxwYCbIwEk,77079
|
17
19
|
pangea/audit_logger.py,sha256=gRkCfUUT5LDNaycwxkhZUySgY47jDfn1ZeKOul4XCQI,3842
|
18
20
|
pangea/config.py,sha256=mQUu8GX_6weIuv3vjNdG5plppXskXYASmxMWtFQh-hc,1662
|
19
21
|
pangea/crypto/rsa.py,sha256=mwSiNy571KAGr3F6oEM0CXWkl9D023ch8ldbZZeLj_4,4747
|
20
|
-
pangea/deep_verify.py,sha256=
|
22
|
+
pangea/deep_verify.py,sha256=mocaGbC6XLbMTVWxTpMv4oJtXGPWpT-SbFqT3obpiZs,8443
|
21
23
|
pangea/deprecated.py,sha256=IjFYEVvY1E0ld0SMkEYC1o62MAleX3nnT1If2dFVbHo,608
|
22
|
-
pangea/dump_audit.py,sha256=
|
24
|
+
pangea/dump_audit.py,sha256=1Je8D2fXwU4PWcZ-ZD4icfO3DNFvWqJkwsac4qFEhOo,7025
|
23
25
|
pangea/exceptions.py,sha256=OBtzUECpNa6vNp8ySkHC-tm4QjFRCOAHBkMHqzAlOu8,5656
|
24
26
|
pangea/file_uploader.py,sha256=4RQ44xt-faApC61nn2PlwHT7XYrJ4GeQA8Ug4tySEAg,1227
|
25
27
|
pangea/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
26
|
-
pangea/request.py,sha256=
|
28
|
+
pangea/request.py,sha256=ZRH4NCnbzjpzkcNnOomelK5uB49T_20PfHP_j4Swgmw,24545
|
27
29
|
pangea/response.py,sha256=lPAcYsF9Xg166CiyhCofVmQA-W4jevh0MQXxUa8Re68,7737
|
28
|
-
pangea/services/__init__.py,sha256
|
29
|
-
pangea/services/
|
30
|
+
pangea/services/__init__.py,sha256=h36HzyIGaI5kO6l3UCwKHx_Kd-m_9mYVwn5MLRVzblI,408
|
31
|
+
pangea/services/ai_guard.py,sha256=W9OxmqQiC2ymDWAoE8FK3_8prDTQhHR8lz4xE81tlMM,4157
|
32
|
+
pangea/services/audit/audit.py,sha256=IFv7jANA8S2SypQVS47x94_Cr5Z9zSsL9Dp9eXw9RHk,39593
|
30
33
|
pangea/services/audit/exceptions.py,sha256=bhVuYe4ammacOVxwg98CChxvwZf5FKgR2DcgqILOcwc,471
|
31
34
|
pangea/services/audit/models.py,sha256=1h1B9eSYQMYG3f8WNi1UcDX2-impRrET_ErjJYUnj7M,14678
|
32
35
|
pangea/services/audit/signing.py,sha256=5A4hvPtpfP2kMz8bsiiKUACriXbh5dv9gb_rbqiUtuI,5583
|
33
36
|
pangea/services/audit/util.py,sha256=Zq1qvfeplYfhCP_ud5YMvntSB0UvnCdsuYbOzZkHbjg,7620
|
34
37
|
pangea/services/authn/authn.py,sha256=cZKl2Ixc6HwHnkRecpSaAGTQUgaZUtxfLa0T3S03HMs,45478
|
35
38
|
pangea/services/authn/models.py,sha256=HH5su6jx3O9AwVGzASXZ99-eIWjgXEP5LhIVdewM13s,22394
|
36
|
-
pangea/services/authz.py,sha256=
|
37
|
-
pangea/services/base.py,sha256=
|
39
|
+
pangea/services/authz.py,sha256=HfDnovAokzAHvnjYdOCwceM-1sCmzODnjNEbQBUSfo8,12222
|
40
|
+
pangea/services/base.py,sha256=nnVVzw6Y_hej5Vcy0WvN_CR0BG_U5F5k_XTNiBX1we0,3464
|
38
41
|
pangea/services/embargo.py,sha256=9Wfku4td5ORaIENKmnGmS5jxJJIRfWp6Q51L36Jsy0I,3897
|
39
42
|
pangea/services/file_scan.py,sha256=QiO80uKqB_BnAOiYQKznXfxpa5j40qqETE3-zBRT_QE,7813
|
40
|
-
pangea/services/intel.py,sha256=
|
41
|
-
pangea/services/
|
42
|
-
pangea/services/
|
43
|
+
pangea/services/intel.py,sha256=CziBhC5K6O_kBXpD8zgJLpDtLHzBRgATGW4gHHFJT48,52039
|
44
|
+
pangea/services/prompt_guard.py,sha256=baaYbYW_HoZJNB8NCIxxFjvJelqrl9UOtWa10-N1jgg,2312
|
45
|
+
pangea/services/redact.py,sha256=ZYXkzEoriLJyCqaj5dqmgsC56mIz4T3pPToZ7TcNfhg,11465
|
46
|
+
pangea/services/sanitize.py,sha256=XP5D4CcbCZfzgU567X6H5eFBWwZuYSsHdvsdrQAZekY,12767
|
43
47
|
pangea/services/share/file_format.py,sha256=1svO1ee_aenA9zoO_AaU-Rk5Ulp7kcPOc_KwNoluyQE,2797
|
44
|
-
pangea/services/share/share.py,sha256=
|
48
|
+
pangea/services/share/share.py,sha256=iyP32UNWoT2F9C_65FiXWrVoNoO7dBjf0tVX1mF2Fz0,45644
|
45
49
|
pangea/services/vault/models/asymmetric.py,sha256=vspijmEvHm5WXri_fjOWfQc4maYyZfhDkLuaTM8-PZo,4991
|
46
50
|
pangea/services/vault/models/common.py,sha256=PSZRFqHTUtEMJJGwywEFM2AU3aV8S-sbcoo3LLQ6uTc,17981
|
47
51
|
pangea/services/vault/models/keys.py,sha256=duAuTiOby_D7MloRvN4gNj0P-b-jx9sdtplAWFxsShw,2786
|
@@ -51,6 +55,6 @@ pangea/services/vault/vault.py,sha256=ow-Zm7PYzfWIfUcA4UNnpeL2DHfZM4C7inRDmNR3zQ
|
|
51
55
|
pangea/tools.py,sha256=2-Y4SAHWFv6Ocj42J_bWrVy27M5G3wi7a8LJn0dabHc,6427
|
52
56
|
pangea/utils.py,sha256=dZ6MwFVEWXUgXvvDg-k6JnvVfsgslvtaBd7ez7afrqk,4983
|
53
57
|
pangea/verify_audit.py,sha256=nSP17OzoSPdvezRExwfcf45H8ZPZnxZu-CbEp3qFJO0,17354
|
54
|
-
pangea_sdk-5.2.
|
55
|
-
pangea_sdk-5.2.
|
56
|
-
pangea_sdk-5.2.
|
58
|
+
pangea_sdk-5.2.0b2.dist-info/METADATA,sha256=j_pSOR8g6RfcYWO7D9lQhISqYHPzfUlc5VSFFYW1elo,7514
|
59
|
+
pangea_sdk-5.2.0b2.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
60
|
+
pangea_sdk-5.2.0b2.dist-info/RECORD,,
|
File without changes
|