pangea-sdk 6.1.0__tar.gz → 6.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pangea_sdk-6.1.0/README.md → pangea_sdk-6.2.0/PKG-INFO +52 -9
- pangea_sdk-6.1.0/PKG-INFO → pangea_sdk-6.2.0/README.md +29 -34
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/__init__.py +9 -1
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/asyncio/__init__.py +1 -0
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/asyncio/file_uploader.py +4 -2
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/asyncio/request.py +53 -18
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/asyncio/services/__init__.py +2 -0
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/asyncio/services/ai_guard.py +9 -12
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/asyncio/services/audit.py +12 -7
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/asyncio/services/authn.py +36 -25
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/asyncio/services/authz.py +6 -6
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/asyncio/services/base.py +4 -0
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/asyncio/services/file_scan.py +8 -2
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/asyncio/services/intel.py +26 -28
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/asyncio/services/redact.py +7 -3
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/asyncio/services/sanitize.py +5 -1
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/asyncio/services/share.py +5 -1
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/asyncio/services/vault.py +19 -15
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/audit_logger.py +3 -1
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/deep_verify.py +13 -13
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/deprecated.py +1 -1
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/dump_audit.py +2 -3
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/exceptions.py +8 -5
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/file_uploader.py +4 -0
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/request.py +64 -48
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/response.py +21 -18
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/__init__.py +2 -0
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/ai_guard.py +35 -24
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/audit/audit.py +16 -13
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/audit/models.py +71 -34
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/audit/signing.py +1 -1
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/audit/util.py +10 -10
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/authn/authn.py +36 -25
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/authn/models.py +10 -56
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/authz.py +10 -6
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/base.py +7 -4
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/embargo.py +6 -0
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/file_scan.py +8 -2
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/intel.py +36 -19
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/redact.py +7 -3
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/sanitize.py +5 -1
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/share/share.py +13 -7
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/vault/models/asymmetric.py +4 -0
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/vault/models/common.py +4 -0
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/vault/models/symmetric.py +4 -0
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/vault/vault.py +17 -19
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/tools.py +13 -9
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/utils.py +3 -5
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/verify_audit.py +23 -27
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pyproject.toml +20 -21
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/asyncio/services/embargo.py +0 -0
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/asyncio/services/prompt_guard.py +0 -0
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/config.py +0 -0
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/crypto/rsa.py +0 -0
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/py.typed +0 -0
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/audit/exceptions.py +0 -0
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/prompt_guard.py +0 -0
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/share/file_format.py +0 -0
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/vault/models/keys.py +0 -0
- {pangea_sdk-6.1.0 → pangea_sdk-6.2.0}/pangea/services/vault/models/secret.py +0 -0
@@ -1,3 +1,26 @@
|
|
1
|
+
Metadata-Version: 2.3
|
2
|
+
Name: pangea-sdk
|
3
|
+
Version: 6.2.0
|
4
|
+
Summary: Pangea API SDK
|
5
|
+
License: MIT
|
6
|
+
Keywords: Pangea,SDK,Audit
|
7
|
+
Author: Glenn Gallien
|
8
|
+
Author-email: glenn.gallien@pangea.cloud
|
9
|
+
Requires-Python: >=3.9.2,<4.0.0
|
10
|
+
Classifier: Topic :: Software Development
|
11
|
+
Classifier: Topic :: Software Development :: Libraries
|
12
|
+
Requires-Dist: aiohttp (>=3.12.13,<4.0.0)
|
13
|
+
Requires-Dist: cryptography (>=45.0.4,<46.0.0)
|
14
|
+
Requires-Dist: deprecated (>=1.2.18,<2.0.0)
|
15
|
+
Requires-Dist: google-crc32c (>=1.7.1,<2.0.0)
|
16
|
+
Requires-Dist: pydantic (>=2.11.7,<3.0.0)
|
17
|
+
Requires-Dist: python-dateutil (>=2.9.0.post0,<3.0.0)
|
18
|
+
Requires-Dist: requests (>=2.32.4,<3.0.0)
|
19
|
+
Requires-Dist: requests-toolbelt (>=1.0.0,<2.0.0)
|
20
|
+
Requires-Dist: typing-extensions (>=4.14.0,<5.0.0)
|
21
|
+
Requires-Dist: yarl (>=1.20.1,<2.0.0)
|
22
|
+
Description-Content-Type: text/markdown
|
23
|
+
|
1
24
|
<a href="https://pangea.cloud?utm_source=github&utm_medium=python-sdk" target="_blank" rel="noopener noreferrer">
|
2
25
|
<img src="https://pangea-marketing.s3.us-west-2.amazonaws.com/pangea-color.svg" alt="Pangea Logo" height="40" />
|
3
26
|
</a>
|
@@ -40,13 +63,13 @@ the same compatibility guarantees as stable releases.
|
|
40
63
|
Via pip:
|
41
64
|
|
42
65
|
```bash
|
43
|
-
$ pip3 install pangea-sdk==
|
66
|
+
$ pip3 install pangea-sdk==6.2.0b2
|
44
67
|
```
|
45
68
|
|
46
69
|
Via poetry:
|
47
70
|
|
48
71
|
```bash
|
49
|
-
$ poetry add pangea-sdk==
|
72
|
+
$ poetry add pangea-sdk==6.2.0b2
|
50
73
|
```
|
51
74
|
|
52
75
|
## Usage
|
@@ -79,6 +102,26 @@ audit = Audit(token, config)
|
|
79
102
|
response = audit.log(message="Hello, World!")
|
80
103
|
```
|
81
104
|
|
105
|
+
## Configuration
|
106
|
+
|
107
|
+
The SDK supports the following configuration options via `PangeaConfig`:
|
108
|
+
|
109
|
+
- `base_url_template` — Template for constructing the base URL for API requests.
|
110
|
+
The placeholder `{SERVICE_NAME}` will be replaced with the service name slug.
|
111
|
+
This is a more powerful version of `domain` that allows for setting more than
|
112
|
+
just the host of the API server. Defaults to
|
113
|
+
`https://{SERVICE_NAME}.aws.us.pangea.cloud`.
|
114
|
+
- `domain` — Base domain for API requests. This is a weaker version of
|
115
|
+
`base_url_template` that only allows for setting the host of the API server.
|
116
|
+
Use `base_url_template` for more control over the URL, such as setting
|
117
|
+
service-specific paths. Defaults to `aws.us.pangea.cloud`.
|
118
|
+
- `request_retries` — Number of retries on the initial request.
|
119
|
+
- `request_backoff` — Backoff strategy passed to 'requests'.
|
120
|
+
- `request_timeout` — Timeout used on initial request attempts.
|
121
|
+
- `poll_result_timeout` — Timeout used to poll results after 202 (in secs).
|
122
|
+
- `queued_retry_enabled` — Enable queued request retry support.
|
123
|
+
- `custom_user_agent` — Custom user agent to be used in the request headers.
|
124
|
+
|
82
125
|
## asyncio support
|
83
126
|
|
84
127
|
asyncio support is available through the `pangea.asyncio.services` module. The
|
@@ -133,6 +176,7 @@ options:
|
|
133
176
|
```
|
134
177
|
|
135
178
|
It accepts multiple file formats:
|
179
|
+
|
136
180
|
- a Verification Artifact from the Pangea User Console
|
137
181
|
- a search response from the REST API:
|
138
182
|
|
@@ -140,7 +184,6 @@ It accepts multiple file formats:
|
|
140
184
|
$ curl -H "Authorization: Bearer ${PANGEA_TOKEN}" -X POST -H 'Content-Type: application/json' --data '{"verbose": true}' https://audit.aws.us.pangea.cloud/v1/search
|
141
185
|
```
|
142
186
|
|
143
|
-
|
144
187
|
### Bulk Download Audit Data
|
145
188
|
|
146
189
|
Download all audit logs for a given time range. Start and end date should be provided,
|
@@ -190,14 +233,14 @@ options:
|
|
190
233
|
```
|
191
234
|
|
192
235
|
It accepts multiple file formats:
|
236
|
+
|
193
237
|
- a Verification Artifact from the Pangea User Console
|
194
238
|
- a file generated by the `dump_audit` command
|
195
239
|
- a search response from the REST API (see `verify_audit`)
|
196
240
|
|
241
|
+
[Documentation]: https://pangea.cloud/docs/sdk/python/
|
242
|
+
[GA Examples]: https://github.com/pangeacyber/pangea-python/tree/main/examples
|
243
|
+
[Beta Examples]: https://github.com/pangeacyber/pangea-python/tree/beta/examples
|
244
|
+
[Pangea Console]: https://console.pangea.cloud/
|
245
|
+
[Secure Audit Log]: https://pangea.cloud/docs/audit
|
197
246
|
|
198
|
-
|
199
|
-
[Documentation]: https://pangea.cloud/docs/sdk/python/
|
200
|
-
[GA Examples]: https://github.com/pangeacyber/pangea-python/tree/main/examples
|
201
|
-
[Beta Examples]: https://github.com/pangeacyber/pangea-python/tree/beta/examples
|
202
|
-
[Pangea Console]: https://console.pangea.cloud/
|
203
|
-
[Secure Audit Log]: https://pangea.cloud/docs/audit
|
@@ -1,26 +1,3 @@
|
|
1
|
-
Metadata-Version: 2.3
|
2
|
-
Name: pangea-sdk
|
3
|
-
Version: 6.1.0
|
4
|
-
Summary: Pangea API SDK
|
5
|
-
License: MIT
|
6
|
-
Keywords: Pangea,SDK,Audit
|
7
|
-
Author: Glenn Gallien
|
8
|
-
Author-email: glenn.gallien@pangea.cloud
|
9
|
-
Requires-Python: >=3.9.2,<4.0.0
|
10
|
-
Classifier: Topic :: Software Development
|
11
|
-
Classifier: Topic :: Software Development :: Libraries
|
12
|
-
Requires-Dist: aiohttp (>=3.11.16,<4.0.0)
|
13
|
-
Requires-Dist: cryptography (>=44.0.2,<44.0.3)
|
14
|
-
Requires-Dist: deprecated (>=1.2.18,<2.0.0)
|
15
|
-
Requires-Dist: google-crc32c (>=1.7.1,<2.0.0)
|
16
|
-
Requires-Dist: pydantic (>=2.11.3,<3.0.0)
|
17
|
-
Requires-Dist: python-dateutil (>=2.9.0.post0,<3.0.0)
|
18
|
-
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
19
|
-
Requires-Dist: requests-toolbelt (>=1.0.0,<2.0.0)
|
20
|
-
Requires-Dist: typing-extensions (>=4.13.2,<5.0.0)
|
21
|
-
Requires-Dist: yarl (>=1.20.0,<2.0.0)
|
22
|
-
Description-Content-Type: text/markdown
|
23
|
-
|
24
1
|
<a href="https://pangea.cloud?utm_source=github&utm_medium=python-sdk" target="_blank" rel="noopener noreferrer">
|
25
2
|
<img src="https://pangea-marketing.s3.us-west-2.amazonaws.com/pangea-color.svg" alt="Pangea Logo" height="40" />
|
26
3
|
</a>
|
@@ -63,13 +40,13 @@ the same compatibility guarantees as stable releases.
|
|
63
40
|
Via pip:
|
64
41
|
|
65
42
|
```bash
|
66
|
-
$ pip3 install pangea-sdk==
|
43
|
+
$ pip3 install pangea-sdk==6.2.0b2
|
67
44
|
```
|
68
45
|
|
69
46
|
Via poetry:
|
70
47
|
|
71
48
|
```bash
|
72
|
-
$ poetry add pangea-sdk==
|
49
|
+
$ poetry add pangea-sdk==6.2.0b2
|
73
50
|
```
|
74
51
|
|
75
52
|
## Usage
|
@@ -102,6 +79,26 @@ audit = Audit(token, config)
|
|
102
79
|
response = audit.log(message="Hello, World!")
|
103
80
|
```
|
104
81
|
|
82
|
+
## Configuration
|
83
|
+
|
84
|
+
The SDK supports the following configuration options via `PangeaConfig`:
|
85
|
+
|
86
|
+
- `base_url_template` — Template for constructing the base URL for API requests.
|
87
|
+
The placeholder `{SERVICE_NAME}` will be replaced with the service name slug.
|
88
|
+
This is a more powerful version of `domain` that allows for setting more than
|
89
|
+
just the host of the API server. Defaults to
|
90
|
+
`https://{SERVICE_NAME}.aws.us.pangea.cloud`.
|
91
|
+
- `domain` — Base domain for API requests. This is a weaker version of
|
92
|
+
`base_url_template` that only allows for setting the host of the API server.
|
93
|
+
Use `base_url_template` for more control over the URL, such as setting
|
94
|
+
service-specific paths. Defaults to `aws.us.pangea.cloud`.
|
95
|
+
- `request_retries` — Number of retries on the initial request.
|
96
|
+
- `request_backoff` — Backoff strategy passed to 'requests'.
|
97
|
+
- `request_timeout` — Timeout used on initial request attempts.
|
98
|
+
- `poll_result_timeout` — Timeout used to poll results after 202 (in secs).
|
99
|
+
- `queued_retry_enabled` — Enable queued request retry support.
|
100
|
+
- `custom_user_agent` — Custom user agent to be used in the request headers.
|
101
|
+
|
105
102
|
## asyncio support
|
106
103
|
|
107
104
|
asyncio support is available through the `pangea.asyncio.services` module. The
|
@@ -156,6 +153,7 @@ options:
|
|
156
153
|
```
|
157
154
|
|
158
155
|
It accepts multiple file formats:
|
156
|
+
|
159
157
|
- a Verification Artifact from the Pangea User Console
|
160
158
|
- a search response from the REST API:
|
161
159
|
|
@@ -163,7 +161,6 @@ It accepts multiple file formats:
|
|
163
161
|
$ curl -H "Authorization: Bearer ${PANGEA_TOKEN}" -X POST -H 'Content-Type: application/json' --data '{"verbose": true}' https://audit.aws.us.pangea.cloud/v1/search
|
164
162
|
```
|
165
163
|
|
166
|
-
|
167
164
|
### Bulk Download Audit Data
|
168
165
|
|
169
166
|
Download all audit logs for a given time range. Start and end date should be provided,
|
@@ -213,15 +210,13 @@ options:
|
|
213
210
|
```
|
214
211
|
|
215
212
|
It accepts multiple file formats:
|
213
|
+
|
216
214
|
- a Verification Artifact from the Pangea User Console
|
217
215
|
- a file generated by the `dump_audit` command
|
218
216
|
- a search response from the REST API (see `verify_audit`)
|
219
217
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
[Pangea Console]: https://console.pangea.cloud/
|
226
|
-
[Secure Audit Log]: https://pangea.cloud/docs/audit
|
227
|
-
|
218
|
+
[Documentation]: https://pangea.cloud/docs/sdk/python/
|
219
|
+
[GA Examples]: https://github.com/pangeacyber/pangea-python/tree/main/examples
|
220
|
+
[Beta Examples]: https://github.com/pangeacyber/pangea-python/tree/beta/examples
|
221
|
+
[Pangea Console]: https://console.pangea.cloud/
|
222
|
+
[Secure Audit Log]: https://pangea.cloud/docs/audit
|
@@ -1,7 +1,15 @@
|
|
1
|
-
__version__ = "6.
|
1
|
+
__version__ = "6.2.0"
|
2
2
|
|
3
3
|
from pangea.asyncio.request import PangeaRequestAsync
|
4
4
|
from pangea.config import PangeaConfig
|
5
5
|
from pangea.file_uploader import FileUploader
|
6
6
|
from pangea.request import PangeaRequest
|
7
7
|
from pangea.response import PangeaResponse
|
8
|
+
|
9
|
+
__all__ = (
|
10
|
+
"FileUploader",
|
11
|
+
"PangeaConfig",
|
12
|
+
"PangeaRequest",
|
13
|
+
"PangeaRequestAsync",
|
14
|
+
"PangeaResponse",
|
15
|
+
)
|
@@ -1,8 +1,10 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
|
+
|
4
|
+
from __future__ import annotations
|
5
|
+
|
3
6
|
import io
|
4
7
|
import logging
|
5
|
-
from typing import Dict, Optional
|
6
8
|
|
7
9
|
from pangea.asyncio.request import PangeaRequestAsync
|
8
10
|
from pangea.request import PangeaConfig
|
@@ -24,7 +26,7 @@ class FileUploaderAsync:
|
|
24
26
|
url: str,
|
25
27
|
file: io.BufferedReader,
|
26
28
|
transfer_method: TransferMethod = TransferMethod.PUT_URL,
|
27
|
-
file_details:
|
29
|
+
file_details: dict | None = None,
|
28
30
|
) -> None:
|
29
31
|
if transfer_method == TransferMethod.PUT_URL:
|
30
32
|
files = [("file", ("filename", file, "application/octet-stream"))]
|
@@ -1,23 +1,43 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
|
+
|
4
|
+
# TODO: Modernize.
|
5
|
+
# ruff: noqa: UP006, UP035
|
6
|
+
|
3
7
|
from __future__ import annotations
|
4
8
|
|
5
9
|
import asyncio
|
6
10
|
import json
|
7
11
|
import time
|
12
|
+
from collections.abc import Iterable, Mapping
|
8
13
|
from typing import Dict, List, Optional, Sequence, Tuple, Type, Union, cast
|
9
14
|
|
10
15
|
import aiohttp
|
11
16
|
from aiohttp import FormData
|
12
17
|
from pydantic import BaseModel
|
13
18
|
from pydantic_core import to_jsonable_python
|
14
|
-
from typing_extensions import Any, TypeVar
|
19
|
+
from typing_extensions import Any, TypeAlias, TypeVar, override
|
15
20
|
|
16
21
|
import pangea.exceptions as pe
|
17
22
|
from pangea.request import MultipartResponse, PangeaRequestBase
|
18
23
|
from pangea.response import AttachedFile, PangeaResponse, PangeaResponseResult, ResponseStatus, TransferMethod
|
19
24
|
from pangea.utils import default_encoder
|
20
25
|
|
26
|
+
_FileName: TypeAlias = Union[str, None]
|
27
|
+
_FileContent: TypeAlias = Union[str, bytes]
|
28
|
+
_FileContentType: TypeAlias = str
|
29
|
+
_FileCustomHeaders: TypeAlias = Mapping[str, str]
|
30
|
+
_FileSpecTuple2: TypeAlias = tuple[_FileName, _FileContent]
|
31
|
+
_FileSpecTuple3: TypeAlias = tuple[_FileName, _FileContent, _FileContentType]
|
32
|
+
_FileSpecTuple4: TypeAlias = tuple[_FileName, _FileContent, _FileContentType, _FileCustomHeaders]
|
33
|
+
_FileSpec: TypeAlias = Union[_FileContent, _FileSpecTuple2, _FileSpecTuple3, _FileSpecTuple4]
|
34
|
+
_Files: TypeAlias = Union[Mapping[str, _FileSpec], Iterable[tuple[str, _FileSpec]]]
|
35
|
+
_LooseHeaders = Union[
|
36
|
+
Mapping[str, str],
|
37
|
+
Iterable[tuple[str, str]],
|
38
|
+
]
|
39
|
+
|
40
|
+
|
21
41
|
TResult = TypeVar("TResult", bound=PangeaResponseResult)
|
22
42
|
|
23
43
|
|
@@ -34,7 +54,7 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
34
54
|
self,
|
35
55
|
endpoint: str,
|
36
56
|
result_class: Type[TResult],
|
37
|
-
data: str | BaseModel |
|
57
|
+
data: str | BaseModel | Mapping[str, Any] | None = None,
|
38
58
|
files: Optional[List[Tuple]] = None,
|
39
59
|
poll_result: bool = True,
|
40
60
|
url: Optional[str] = None,
|
@@ -61,7 +81,7 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
61
81
|
data = {}
|
62
82
|
|
63
83
|
# Normalize.
|
64
|
-
data = cast(dict[str, Any], to_jsonable_python(data))
|
84
|
+
data = cast(dict[str, Any], to_jsonable_python(data, exclude_none=True))
|
65
85
|
|
66
86
|
if url is None:
|
67
87
|
url = self._url(endpoint)
|
@@ -80,8 +100,11 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
80
100
|
endpoint, result_class=result_class, data=data, files=files
|
81
101
|
)
|
82
102
|
else:
|
103
|
+
headers = self._headers()
|
104
|
+
if transfer_method == TransferMethod.MULTIPART.value:
|
105
|
+
del headers["Content-Type"]
|
83
106
|
requests_response = await self._http_post(
|
84
|
-
url, headers=
|
107
|
+
url, headers=headers, data=data, files=files, presigned_url_post=False
|
85
108
|
)
|
86
109
|
|
87
110
|
await self._check_http_errors(requests_response)
|
@@ -103,7 +126,9 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
103
126
|
|
104
127
|
pangea_response = PangeaResponse(requests_response, result_class=result_class, json=json_resp)
|
105
128
|
except aiohttp.ContentTypeError as e:
|
106
|
-
raise pe.PangeaException(
|
129
|
+
raise pe.PangeaException(
|
130
|
+
f"Failed to decode json response. {e}. Body: {await requests_response.text()}"
|
131
|
+
) from e
|
107
132
|
|
108
133
|
if poll_result:
|
109
134
|
pangea_response = await self._handle_queued_result(pangea_response)
|
@@ -164,7 +189,7 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
164
189
|
|
165
190
|
return await self.poll_result_by_id(request_id, response.result_class, check_response=check_response)
|
166
191
|
|
167
|
-
async def post_presigned_url(self, url: str, data:
|
192
|
+
async def post_presigned_url(self, url: str, data: dict[Any, Any], files: Sequence[Tuple]):
|
168
193
|
# Send form request with file and upload_details as body
|
169
194
|
resp = await self._http_post(url=url, data=data, files=files, presigned_url_post=True)
|
170
195
|
self.logger.debug(
|
@@ -278,35 +303,44 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
278
303
|
async def _http_post(
|
279
304
|
self,
|
280
305
|
url: str,
|
281
|
-
headers:
|
282
|
-
data:
|
283
|
-
files:
|
306
|
+
headers: _LooseHeaders = {}, # noqa: B006
|
307
|
+
data: str | dict[str, Any] | None = None,
|
308
|
+
files: _Files | None = None,
|
284
309
|
presigned_url_post: bool = False,
|
285
310
|
) -> aiohttp.ClientResponse:
|
311
|
+
if data is None:
|
312
|
+
data = {}
|
313
|
+
|
286
314
|
if files:
|
287
315
|
form = FormData()
|
288
316
|
if presigned_url_post:
|
289
|
-
|
317
|
+
assert isinstance(data, dict)
|
318
|
+
assert isinstance(files, list)
|
319
|
+
for k, v in data.items():
|
290
320
|
form.add_field(k, v)
|
291
|
-
for
|
321
|
+
for _name, value in files:
|
292
322
|
form.add_field("file", value[1], filename=value[0], content_type=value[2])
|
293
323
|
else:
|
294
|
-
|
324
|
+
assert isinstance(files, list)
|
325
|
+
data_send: str | FormData = (
|
326
|
+
json.dumps(data, default=default_encoder) if isinstance(data, dict) else data
|
327
|
+
)
|
295
328
|
form.add_field("request", data_send, content_type="application/json")
|
296
329
|
for name, value in files:
|
297
330
|
form.add_field(name, value[1], filename=value[0], content_type=value[2])
|
298
331
|
|
299
|
-
data_send = form
|
332
|
+
data_send = form
|
300
333
|
else:
|
301
334
|
data_send = json.dumps(data, default=default_encoder) if isinstance(data, dict) else data
|
302
335
|
|
336
|
+
assert isinstance(self.session, aiohttp.ClientSession)
|
303
337
|
return await self.session.post(url, headers=headers, data=data_send)
|
304
338
|
|
305
339
|
async def _http_put(
|
306
340
|
self,
|
307
341
|
url: str,
|
308
342
|
files: Sequence[Tuple],
|
309
|
-
headers:
|
343
|
+
headers: _LooseHeaders = {}, # noqa: B006
|
310
344
|
) -> aiohttp.ClientResponse:
|
311
345
|
self.logger.debug(
|
312
346
|
json.dumps({"service": self.service, "action": "http_put", "url": url}, default=default_encoder)
|
@@ -318,8 +352,8 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
318
352
|
self,
|
319
353
|
endpoint: str,
|
320
354
|
result_class: Type[PangeaResponseResult],
|
321
|
-
data: Union[str,
|
322
|
-
files:
|
355
|
+
data: Union[str, Mapping[str, Any]] = {},
|
356
|
+
files: Sequence[Tuple] = [],
|
323
357
|
):
|
324
358
|
if len(files) == 0:
|
325
359
|
raise AttributeError("files attribute should have at least 1 file")
|
@@ -343,7 +377,7 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
343
377
|
self,
|
344
378
|
endpoint: str,
|
345
379
|
result_class: Type[PangeaResponseResult],
|
346
|
-
data: Union[str,
|
380
|
+
data: Union[str, Mapping[str, Any]] = {},
|
347
381
|
) -> PangeaResponse:
|
348
382
|
# Send request
|
349
383
|
try:
|
@@ -394,7 +428,7 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
394
428
|
{"service": self.service, "action": "poll_presigned_url", "step": "exit", "cause": {str(e)}}
|
395
429
|
)
|
396
430
|
)
|
397
|
-
raise pe.PresignedURLException("Failed to pull Presigned URL", loop_exc.response, e)
|
431
|
+
raise pe.PresignedURLException("Failed to pull Presigned URL", loop_exc.response, e) from e
|
398
432
|
|
399
433
|
self.logger.debug(json.dumps({"service": self.service, "action": "poll_presigned_url", "step": "exit"}))
|
400
434
|
|
@@ -426,6 +460,7 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
426
460
|
self.logger.debug(json.dumps({"service": self.service, "action": "poll_result_retry", "step": "exit"}))
|
427
461
|
return self._check_response(response)
|
428
462
|
|
463
|
+
@override
|
429
464
|
def _init_session(self) -> aiohttp.ClientSession:
|
430
465
|
# retry_config = Retry(
|
431
466
|
# total=self.config.request_retries,
|
@@ -1,15 +1,12 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
from collections.abc import Sequence
|
3
4
|
from typing import overload
|
4
5
|
|
5
|
-
from typing_extensions import TypeVar
|
6
|
-
|
7
6
|
from pangea.asyncio.services.base import ServiceBaseAsync
|
8
7
|
from pangea.config import PangeaConfig
|
9
8
|
from pangea.response import PangeaResponse
|
10
|
-
from pangea.services.ai_guard import LogFields, Overrides, TextGuardResult
|
11
|
-
|
12
|
-
_T = TypeVar("_T")
|
9
|
+
from pangea.services.ai_guard import LogFields, Message, Overrides, TextGuardResult
|
13
10
|
|
14
11
|
|
15
12
|
class AIGuardAsync(ServiceBaseAsync):
|
@@ -60,7 +57,7 @@ class AIGuardAsync(ServiceBaseAsync):
|
|
60
57
|
debug: bool | None = None,
|
61
58
|
overrides: Overrides | None = None,
|
62
59
|
log_fields: LogFields | None = None,
|
63
|
-
) -> PangeaResponse[TextGuardResult
|
60
|
+
) -> PangeaResponse[TextGuardResult]:
|
64
61
|
"""
|
65
62
|
Text Guard for scanning LLM inputs and outputs
|
66
63
|
|
@@ -88,12 +85,12 @@ class AIGuardAsync(ServiceBaseAsync):
|
|
88
85
|
async def guard_text(
|
89
86
|
self,
|
90
87
|
*,
|
91
|
-
messages:
|
88
|
+
messages: Sequence[Message],
|
92
89
|
recipe: str | None = None,
|
93
90
|
debug: bool | None = None,
|
94
91
|
overrides: Overrides | None = None,
|
95
92
|
log_fields: LogFields | None = None,
|
96
|
-
) -> PangeaResponse[TextGuardResult
|
93
|
+
) -> PangeaResponse[TextGuardResult]:
|
97
94
|
"""
|
98
95
|
Text Guard for scanning LLM inputs and outputs
|
99
96
|
|
@@ -115,19 +112,19 @@ class AIGuardAsync(ServiceBaseAsync):
|
|
115
112
|
log_field: Additional fields to include in activity log
|
116
113
|
|
117
114
|
Examples:
|
118
|
-
response = await ai_guard.guard_text(messages=[
|
115
|
+
response = await ai_guard.guard_text(messages=[Message(role="user", content="hello world")])
|
119
116
|
"""
|
120
117
|
|
121
|
-
async def guard_text(
|
118
|
+
async def guard_text(
|
122
119
|
self,
|
123
120
|
text: str | None = None,
|
124
121
|
*,
|
125
|
-
messages:
|
122
|
+
messages: Sequence[Message] | None = None,
|
126
123
|
recipe: str | None = None,
|
127
124
|
debug: bool | None = None,
|
128
125
|
overrides: Overrides | None = None,
|
129
126
|
log_fields: LogFields | None = None,
|
130
|
-
) -> PangeaResponse[TextGuardResult
|
127
|
+
) -> PangeaResponse[TextGuardResult]:
|
131
128
|
"""
|
132
129
|
Text Guard for scanning LLM inputs and outputs
|
133
130
|
|
@@ -1,8 +1,13 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
|
+
|
4
|
+
# TODO: Modernize.
|
5
|
+
# ruff: noqa: UP006, UP035
|
6
|
+
|
3
7
|
from __future__ import annotations
|
4
8
|
|
5
9
|
import datetime
|
10
|
+
from collections.abc import Mapping
|
6
11
|
from typing import Any, Dict, Iterable, List, Optional, Sequence, Union
|
7
12
|
|
8
13
|
import pangea.exceptions as pexc
|
@@ -63,7 +68,7 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
|
|
63
68
|
token: str,
|
64
69
|
config: PangeaConfig | None = None,
|
65
70
|
private_key_file: str = "",
|
66
|
-
public_key_info:
|
71
|
+
public_key_info: Mapping[str, str] = {},
|
67
72
|
tenant_id: str | None = None,
|
68
73
|
logger_name: str = "pangea",
|
69
74
|
config_id: str | None = None,
|
@@ -135,7 +140,7 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
|
|
135
140
|
A PangeaResponse where the hash of event data and optional verbose
|
136
141
|
results are returned in the response.result field.
|
137
142
|
Available response fields can be found in our
|
138
|
-
[API documentation](https://pangea.cloud/docs/api/audit
|
143
|
+
[API documentation](https://pangea.cloud/docs/api/audit#/v1/log-post).
|
139
144
|
|
140
145
|
Examples:
|
141
146
|
try:
|
@@ -186,7 +191,7 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
|
|
186
191
|
Returns:
|
187
192
|
A PangeaResponse where the hash of event data and optional verbose
|
188
193
|
results are returned in the response.result field.
|
189
|
-
Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/audit
|
194
|
+
Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/audit#/v1/log-post).
|
190
195
|
|
191
196
|
Examples:
|
192
197
|
response = await audit.log_event({"message": "hello world"}, verbose=True)
|
@@ -222,7 +227,7 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
|
|
222
227
|
Returns:
|
223
228
|
A PangeaResponse where the hash of event data and optional verbose
|
224
229
|
results are returned in the response.result field.
|
225
|
-
Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/audit
|
230
|
+
Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/audit#/v1/log-post).
|
226
231
|
|
227
232
|
Examples:
|
228
233
|
FIXME:
|
@@ -259,7 +264,7 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
|
|
259
264
|
Returns:
|
260
265
|
A PangeaResponse where the hash of event data and optional verbose
|
261
266
|
results are returned in the response.result field.
|
262
|
-
Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/audit
|
267
|
+
Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/audit#/v1/log-post).
|
263
268
|
|
264
269
|
Examples:
|
265
270
|
FIXME:
|
@@ -328,8 +333,8 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
|
|
328
333
|
|
329
334
|
Returns:
|
330
335
|
A PangeaResponse[SearchOutput] where the first page of matched events is returned in the
|
331
|
-
response.result field. Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/audit
|
332
|
-
Pagination can be found in the [search results endpoint](https://pangea.cloud/docs/api/audit
|
336
|
+
response.result field. Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/audit#/v1/results-post).
|
337
|
+
Pagination can be found in the [search results endpoint](https://pangea.cloud/docs/api/audit#/v1/download_results-post).
|
333
338
|
|
334
339
|
Examples:
|
335
340
|
response: PangeaResponse[SearchOutput] = audit.search(query="message:test", search_restriction={'source': ["monitor"]}, limit=1, verify_consistency=True, verify_events=True)
|