pangea-sdk 6.2.0b1__tar.gz → 6.3.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.2.0b1/README.md → pangea_sdk-6.3.0/PKG-INFO +52 -9
- pangea_sdk-6.2.0b1/PKG-INFO → pangea_sdk-6.3.0/README.md +29 -34
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/__init__.py +9 -1
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/asyncio/__init__.py +1 -0
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/asyncio/file_uploader.py +4 -2
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/asyncio/request.py +70 -169
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/asyncio/services/__init__.py +2 -1
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/asyncio/services/ai_guard.py +9 -12
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/asyncio/services/audit.py +13 -307
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/asyncio/services/authn.py +40 -32
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/asyncio/services/authz.py +51 -17
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/asyncio/services/base.py +4 -0
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/asyncio/services/file_scan.py +8 -2
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/asyncio/services/intel.py +26 -28
- pangea_sdk-6.3.0/pangea/asyncio/services/redact.py +206 -0
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/asyncio/services/sanitize.py +5 -1
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/asyncio/services/share.py +5 -1
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/asyncio/services/vault.py +71 -55
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/audit_logger.py +3 -1
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/deep_verify.py +13 -13
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/deprecated.py +1 -1
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/dump_audit.py +2 -3
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/exceptions.py +8 -5
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/file_uploader.py +4 -0
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/request.py +80 -200
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/response.py +21 -18
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/__init__.py +2 -1
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/ai_guard.py +35 -24
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/audit/audit.py +17 -314
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/audit/models.py +69 -307
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/audit/signing.py +1 -1
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/audit/util.py +10 -10
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/authn/authn.py +39 -31
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/authn/models.py +183 -148
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/authz.py +108 -60
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/base.py +7 -4
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/embargo.py +6 -0
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/file_scan.py +8 -2
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/intel.py +36 -19
- pangea_sdk-6.3.0/pangea/services/redact.py +388 -0
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/sanitize.py +5 -1
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/share/share.py +13 -7
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/vault/models/asymmetric.py +4 -0
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/vault/models/common.py +15 -12
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/vault/models/keys.py +4 -9
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/vault/models/secret.py +3 -8
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/vault/models/symmetric.py +4 -0
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/vault/vault.py +69 -59
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/tools.py +13 -9
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/utils.py +3 -5
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/verify_audit.py +23 -27
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pyproject.toml +20 -21
- pangea_sdk-6.2.0b1/pangea/asyncio/services/management.py +0 -576
- pangea_sdk-6.2.0b1/pangea/asyncio/services/redact.py +0 -463
- pangea_sdk-6.2.0b1/pangea/services/management.py +0 -720
- pangea_sdk-6.2.0b1/pangea/services/redact.py +0 -850
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/asyncio/services/embargo.py +0 -0
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/asyncio/services/prompt_guard.py +0 -0
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/config.py +0 -0
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/crypto/rsa.py +0 -0
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/py.typed +0 -0
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/audit/exceptions.py +0 -0
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/prompt_guard.py +0 -0
- {pangea_sdk-6.2.0b1 → pangea_sdk-6.3.0}/pangea/services/share/file_format.py +0 -0
@@ -1,3 +1,26 @@
|
|
1
|
+
Metadata-Version: 2.3
|
2
|
+
Name: pangea-sdk
|
3
|
+
Version: 6.3.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.14,<4.0.0)
|
13
|
+
Requires-Dist: cryptography (>=45.0.5,<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.1,<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==6.2.
|
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==6.2.
|
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.2.0b1
|
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==6.2.
|
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==6.2.
|
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.3.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,24 +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
|
8
12
|
from collections.abc import Iterable, Mapping
|
9
|
-
from typing import Dict, List,
|
13
|
+
from typing import Dict, List, Optional, Sequence, Tuple, Type, Union, cast
|
10
14
|
|
11
15
|
import aiohttp
|
12
16
|
from aiohttp import FormData
|
13
|
-
from pydantic import BaseModel
|
17
|
+
from pydantic import BaseModel
|
14
18
|
from pydantic_core import to_jsonable_python
|
15
|
-
from typing_extensions import Any, TypeVar
|
19
|
+
from typing_extensions import Any, TypeAlias, TypeVar, override
|
16
20
|
|
17
21
|
import pangea.exceptions as pe
|
18
22
|
from pangea.request import MultipartResponse, PangeaRequestBase
|
19
23
|
from pangea.response import AttachedFile, PangeaResponse, PangeaResponseResult, ResponseStatus, TransferMethod
|
20
24
|
from pangea.utils import default_encoder
|
21
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
|
+
|
22
41
|
TResult = TypeVar("TResult", bound=PangeaResponseResult)
|
23
42
|
|
24
43
|
|
@@ -31,86 +50,26 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
31
50
|
be set in PangeaConfig.
|
32
51
|
"""
|
33
52
|
|
34
|
-
async def delete(self, endpoint: str) -> None:
|
35
|
-
"""
|
36
|
-
Makes a DELETE call to a Pangea endpoint.
|
37
|
-
|
38
|
-
Args:
|
39
|
-
endpoint: The Pangea API endpoint.
|
40
|
-
"""
|
41
|
-
|
42
|
-
url = self._url(endpoint)
|
43
|
-
|
44
|
-
self.logger.debug(
|
45
|
-
json.dumps({"service": self.service, "action": "delete", "url": url}, default=default_encoder)
|
46
|
-
)
|
47
|
-
|
48
|
-
requests_response = await self._http_delete(url, headers=self._headers())
|
49
|
-
await self._check_http_errors(requests_response)
|
50
|
-
|
51
|
-
@overload
|
52
53
|
async def post(
|
53
54
|
self,
|
54
55
|
endpoint: str,
|
55
56
|
result_class: Type[TResult],
|
56
|
-
data: str | BaseModel |
|
57
|
+
data: str | BaseModel | Mapping[str, Any] | None = None,
|
57
58
|
files: Optional[List[Tuple]] = None,
|
58
59
|
poll_result: bool = True,
|
59
60
|
url: Optional[str] = None,
|
60
|
-
*,
|
61
|
-
pangea_response: Literal[True] = True,
|
62
61
|
) -> PangeaResponse[TResult]:
|
63
|
-
"""
|
64
|
-
Makes a POST call to a Pangea Service endpoint.
|
62
|
+
"""Makes the POST call to a Pangea Service endpoint.
|
65
63
|
|
66
64
|
Args:
|
67
|
-
endpoint: The Pangea Service API endpoint.
|
68
|
-
data: The POST body payload object
|
65
|
+
endpoint(str): The Pangea Service API endpoint.
|
66
|
+
data(dict): The POST body payload object
|
69
67
|
|
70
68
|
Returns:
|
71
69
|
PangeaResponse which contains the response in its entirety and
|
72
70
|
various properties to retrieve individual fields
|
73
71
|
"""
|
74
72
|
|
75
|
-
@overload
|
76
|
-
async def post(
|
77
|
-
self,
|
78
|
-
endpoint: str,
|
79
|
-
result_class: Type[TResult],
|
80
|
-
data: str | BaseModel | dict[str, Any] | None = None,
|
81
|
-
files: Optional[List[Tuple]] = None,
|
82
|
-
poll_result: bool = True,
|
83
|
-
url: Optional[str] = None,
|
84
|
-
*,
|
85
|
-
pangea_response: Literal[False],
|
86
|
-
) -> TResult:
|
87
|
-
"""
|
88
|
-
Makes a POST call to a Pangea Service endpoint.
|
89
|
-
|
90
|
-
Args:
|
91
|
-
endpoint: The Pangea Service API endpoint.
|
92
|
-
data: The POST body payload object
|
93
|
-
"""
|
94
|
-
|
95
|
-
async def post(
|
96
|
-
self,
|
97
|
-
endpoint: str,
|
98
|
-
result_class: Type[TResult],
|
99
|
-
data: str | BaseModel | dict[str, Any] | None = None,
|
100
|
-
files: Optional[List[Tuple]] = None,
|
101
|
-
poll_result: bool = True,
|
102
|
-
url: Optional[str] = None,
|
103
|
-
*,
|
104
|
-
pangea_response: bool = True,
|
105
|
-
) -> PangeaResponse[TResult] | TResult:
|
106
|
-
"""
|
107
|
-
Makes a POST call to a Pangea Service endpoint.
|
108
|
-
|
109
|
-
Args:
|
110
|
-
endpoint: The Pangea Service API endpoint.
|
111
|
-
data: The POST body payload object
|
112
|
-
"""
|
113
|
-
|
114
73
|
if isinstance(data, BaseModel):
|
115
74
|
data = data.model_dump(exclude_none=True)
|
116
75
|
|
@@ -122,7 +81,7 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
122
81
|
data = {}
|
123
82
|
|
124
83
|
# Normalize.
|
125
|
-
data = cast(dict[str, Any], to_jsonable_python(data))
|
84
|
+
data = cast(dict[str, Any], to_jsonable_python(data, exclude_none=True))
|
126
85
|
|
127
86
|
if url is None:
|
128
87
|
url = self._url(endpoint)
|
@@ -141,19 +100,18 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
141
100
|
endpoint, result_class=result_class, data=data, files=files
|
142
101
|
)
|
143
102
|
else:
|
103
|
+
headers = self._headers()
|
104
|
+
if transfer_method == TransferMethod.MULTIPART.value:
|
105
|
+
del headers["Content-Type"]
|
144
106
|
requests_response = await self._http_post(
|
145
|
-
url, headers=
|
107
|
+
url, headers=headers, data=data, files=files, presigned_url_post=False
|
146
108
|
)
|
147
109
|
|
148
110
|
await self._check_http_errors(requests_response)
|
149
111
|
|
150
|
-
if not pangea_response:
|
151
|
-
type_adapter = TypeAdapter(result_class)
|
152
|
-
return type_adapter.validate_python(await requests_response.json())
|
153
|
-
|
154
112
|
if "multipart/form-data" in requests_response.headers.get("content-type", ""):
|
155
113
|
multipart_response = await self._process_multipart_response(requests_response)
|
156
|
-
|
114
|
+
pangea_response: PangeaResponse = PangeaResponse(
|
157
115
|
requests_response,
|
158
116
|
result_class=result_class,
|
159
117
|
json=multipart_response.pangea_json,
|
@@ -166,108 +124,49 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
166
124
|
json.dumps({"service": self.service, "action": "post", "url": url, "response": json_resp})
|
167
125
|
)
|
168
126
|
|
169
|
-
|
127
|
+
pangea_response = PangeaResponse(requests_response, result_class=result_class, json=json_resp)
|
170
128
|
except aiohttp.ContentTypeError as e:
|
171
|
-
raise pe.PangeaException(
|
129
|
+
raise pe.PangeaException(
|
130
|
+
f"Failed to decode json response. {e}. Body: {await requests_response.text()}"
|
131
|
+
) from e
|
172
132
|
|
173
133
|
if poll_result:
|
174
|
-
|
134
|
+
pangea_response = await self._handle_queued_result(pangea_response)
|
175
135
|
|
176
|
-
return self._check_response(
|
136
|
+
return self._check_response(pangea_response)
|
177
137
|
|
178
|
-
|
179
|
-
|
180
|
-
self,
|
181
|
-
path: str,
|
182
|
-
result_class: Type[TResult],
|
183
|
-
check_response: bool = True,
|
184
|
-
*,
|
185
|
-
params: (
|
186
|
-
Mapping[str | bytes | int | float, str | bytes | int | float | Iterable[str | bytes | int | float] | None]
|
187
|
-
| None
|
188
|
-
) = None,
|
189
|
-
pangea_response: Literal[True] = True,
|
190
|
-
) -> PangeaResponse[TResult]:
|
191
|
-
"""
|
192
|
-
Makes the GET call to a Pangea Service endpoint.
|
138
|
+
async def get(self, path: str, result_class: Type[TResult], check_response: bool = True) -> PangeaResponse[TResult]:
|
139
|
+
"""Makes the GET call to a Pangea Service endpoint.
|
193
140
|
|
194
141
|
Args:
|
195
|
-
|
196
|
-
|
142
|
+
endpoint(str): The Pangea Service API endpoint.
|
143
|
+
path(str): Additional URL path
|
197
144
|
|
198
145
|
Returns:
|
199
146
|
PangeaResponse which contains the response in its entirety and
|
200
147
|
various properties to retrieve individual fields
|
201
148
|
"""
|
202
149
|
|
203
|
-
@overload
|
204
|
-
async def get(
|
205
|
-
self,
|
206
|
-
path: str,
|
207
|
-
result_class: Type[TResult],
|
208
|
-
check_response: bool = True,
|
209
|
-
*,
|
210
|
-
params: (
|
211
|
-
Mapping[str | bytes | int | float, str | bytes | int | float | Iterable[str | bytes | int | float] | None]
|
212
|
-
| None
|
213
|
-
) = None,
|
214
|
-
pangea_response: Literal[False] = False,
|
215
|
-
) -> TResult:
|
216
|
-
"""
|
217
|
-
Makes the GET call to a Pangea Service endpoint.
|
218
|
-
|
219
|
-
Args:
|
220
|
-
path: Additional URL path
|
221
|
-
params: Dictionary of querystring data to attach to the request
|
222
|
-
"""
|
223
|
-
|
224
|
-
async def get(
|
225
|
-
self,
|
226
|
-
path: str,
|
227
|
-
result_class: Type[TResult],
|
228
|
-
check_response: bool = True,
|
229
|
-
*,
|
230
|
-
params: (
|
231
|
-
Mapping[str | bytes | int | float, str | bytes | int | float | Iterable[str | bytes | int | float] | None]
|
232
|
-
| None
|
233
|
-
) = None,
|
234
|
-
pangea_response: bool = True,
|
235
|
-
) -> PangeaResponse[TResult] | TResult:
|
236
|
-
"""
|
237
|
-
Makes the GET call to a Pangea Service endpoint.
|
238
|
-
|
239
|
-
Args:
|
240
|
-
path: Additional URL path
|
241
|
-
params: Dictionary of querystring data to attach to the request
|
242
|
-
pangea_response: Whether or not the response body follows Pangea's
|
243
|
-
standard response schema
|
244
|
-
"""
|
245
|
-
|
246
150
|
url = self._url(path)
|
247
151
|
self.logger.debug(json.dumps({"service": self.service, "action": "get", "url": url}))
|
248
152
|
|
249
|
-
async with self.session.get(url,
|
153
|
+
async with self.session.get(url, headers=self._headers()) as requests_response:
|
250
154
|
await self._check_http_errors(requests_response)
|
251
|
-
|
252
|
-
if not pangea_response:
|
253
|
-
type_adapter = TypeAdapter(result_class)
|
254
|
-
return type_adapter.validate_python(await requests_response.json())
|
255
|
-
|
256
|
-
pangea_response_obj = PangeaResponse(
|
155
|
+
pangea_response = PangeaResponse(
|
257
156
|
requests_response, result_class=result_class, json=await requests_response.json()
|
258
157
|
)
|
259
158
|
|
260
159
|
self.logger.debug(
|
261
160
|
json.dumps(
|
262
|
-
{"service": self.service, "action": "get", "url": url, "response":
|
161
|
+
{"service": self.service, "action": "get", "url": url, "response": pangea_response.json},
|
263
162
|
default=default_encoder,
|
264
163
|
)
|
265
164
|
)
|
266
165
|
|
267
166
|
if check_response is False:
|
268
|
-
return
|
167
|
+
return pangea_response
|
269
168
|
|
270
|
-
return self._check_response(
|
169
|
+
return self._check_response(pangea_response)
|
271
170
|
|
272
171
|
async def _check_http_errors(self, resp: aiohttp.ClientResponse):
|
273
172
|
if resp.status == 503:
|
@@ -290,7 +189,7 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
290
189
|
|
291
190
|
return await self.poll_result_by_id(request_id, response.result_class, check_response=check_response)
|
292
191
|
|
293
|
-
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]):
|
294
193
|
# Send form request with file and upload_details as body
|
295
194
|
resp = await self._http_post(url=url, data=data, files=files, presigned_url_post=True)
|
296
195
|
self.logger.debug(
|
@@ -401,46 +300,47 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
401
300
|
attached_files = await self._get_attached_files(multipart_reader)
|
402
301
|
return MultipartResponse(pangea_json, attached_files) # type: ignore[arg-type]
|
403
302
|
|
404
|
-
async def _http_delete(
|
405
|
-
self,
|
406
|
-
url: str,
|
407
|
-
*,
|
408
|
-
headers: Mapping[str, str | bytes | None] = {},
|
409
|
-
) -> aiohttp.ClientResponse:
|
410
|
-
return await self.session.delete(url, headers=headers)
|
411
|
-
|
412
303
|
async def _http_post(
|
413
304
|
self,
|
414
305
|
url: str,
|
415
|
-
headers:
|
416
|
-
data:
|
417
|
-
files:
|
306
|
+
headers: _LooseHeaders = {}, # noqa: B006
|
307
|
+
data: str | dict[str, Any] | None = None,
|
308
|
+
files: _Files | None = None,
|
418
309
|
presigned_url_post: bool = False,
|
419
310
|
) -> aiohttp.ClientResponse:
|
311
|
+
if data is None:
|
312
|
+
data = {}
|
313
|
+
|
420
314
|
if files:
|
421
315
|
form = FormData()
|
422
316
|
if presigned_url_post:
|
423
|
-
|
317
|
+
assert isinstance(data, dict)
|
318
|
+
assert isinstance(files, list)
|
319
|
+
for k, v in data.items():
|
424
320
|
form.add_field(k, v)
|
425
|
-
for
|
321
|
+
for _name, value in files:
|
426
322
|
form.add_field("file", value[1], filename=value[0], content_type=value[2])
|
427
323
|
else:
|
428
|
-
|
324
|
+
assert isinstance(files, list)
|
325
|
+
data_send: str | FormData = (
|
326
|
+
json.dumps(data, default=default_encoder) if isinstance(data, dict) else data
|
327
|
+
)
|
429
328
|
form.add_field("request", data_send, content_type="application/json")
|
430
329
|
for name, value in files:
|
431
330
|
form.add_field(name, value[1], filename=value[0], content_type=value[2])
|
432
331
|
|
433
|
-
data_send = form
|
332
|
+
data_send = form
|
434
333
|
else:
|
435
334
|
data_send = json.dumps(data, default=default_encoder) if isinstance(data, dict) else data
|
436
335
|
|
336
|
+
assert isinstance(self.session, aiohttp.ClientSession)
|
437
337
|
return await self.session.post(url, headers=headers, data=data_send)
|
438
338
|
|
439
339
|
async def _http_put(
|
440
340
|
self,
|
441
341
|
url: str,
|
442
342
|
files: Sequence[Tuple],
|
443
|
-
headers:
|
343
|
+
headers: _LooseHeaders = {}, # noqa: B006
|
444
344
|
) -> aiohttp.ClientResponse:
|
445
345
|
self.logger.debug(
|
446
346
|
json.dumps({"service": self.service, "action": "http_put", "url": url}, default=default_encoder)
|
@@ -452,8 +352,8 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
452
352
|
self,
|
453
353
|
endpoint: str,
|
454
354
|
result_class: Type[PangeaResponseResult],
|
455
|
-
data: Union[str,
|
456
|
-
files:
|
355
|
+
data: Union[str, Mapping[str, Any]] = {},
|
356
|
+
files: Sequence[Tuple] = [],
|
457
357
|
):
|
458
358
|
if len(files) == 0:
|
459
359
|
raise AttributeError("files attribute should have at least 1 file")
|
@@ -477,7 +377,7 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
477
377
|
self,
|
478
378
|
endpoint: str,
|
479
379
|
result_class: Type[PangeaResponseResult],
|
480
|
-
data: Union[str,
|
380
|
+
data: Union[str, Mapping[str, Any]] = {},
|
481
381
|
) -> PangeaResponse:
|
482
382
|
# Send request
|
483
383
|
try:
|
@@ -528,7 +428,7 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
528
428
|
{"service": self.service, "action": "poll_presigned_url", "step": "exit", "cause": {str(e)}}
|
529
429
|
)
|
530
430
|
)
|
531
|
-
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
|
532
432
|
|
533
433
|
self.logger.debug(json.dumps({"service": self.service, "action": "poll_presigned_url", "step": "exit"}))
|
534
434
|
|
@@ -560,6 +460,7 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
560
460
|
self.logger.debug(json.dumps({"service": self.service, "action": "poll_result_retry", "step": "exit"}))
|
561
461
|
return self._check_response(response)
|
562
462
|
|
463
|
+
@override
|
563
464
|
def _init_session(self) -> aiohttp.ClientSession:
|
564
465
|
# retry_config = Retry(
|
565
466
|
# total=self.config.request_retries,
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# ruff: noqa: F401
|
2
|
+
|
1
3
|
from .ai_guard import AIGuardAsync
|
2
4
|
from .audit import AuditAsync
|
3
5
|
from .authn import AuthNAsync
|
@@ -5,7 +7,6 @@ from .authz import AuthZAsync
|
|
5
7
|
from .embargo import EmbargoAsync
|
6
8
|
from .file_scan import FileScanAsync
|
7
9
|
from .intel import DomainIntelAsync, FileIntelAsync, IpIntelAsync, UrlIntelAsync, UserIntelAsync
|
8
|
-
from .management import ManagementAsync
|
9
10
|
from .prompt_guard import PromptGuardAsync
|
10
11
|
from .redact import RedactAsync
|
11
12
|
from .sanitize import SanitizeAsync
|