pangea-sdk 6.1.1__py3-none-any.whl → 6.2.0__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 +9 -1
- pangea/asyncio/__init__.py +1 -0
- pangea/asyncio/file_uploader.py +4 -2
- pangea/asyncio/request.py +52 -17
- pangea/asyncio/services/__init__.py +2 -0
- pangea/asyncio/services/ai_guard.py +9 -12
- pangea/asyncio/services/audit.py +6 -1
- pangea/asyncio/services/authn.py +15 -4
- pangea/asyncio/services/base.py +4 -0
- pangea/asyncio/services/file_scan.py +7 -1
- pangea/asyncio/services/intel.py +26 -28
- pangea/asyncio/services/redact.py +4 -0
- pangea/asyncio/services/sanitize.py +5 -1
- pangea/asyncio/services/share.py +5 -1
- pangea/asyncio/services/vault.py +4 -0
- pangea/audit_logger.py +3 -1
- pangea/deep_verify.py +13 -13
- pangea/deprecated.py +1 -1
- pangea/dump_audit.py +2 -3
- pangea/exceptions.py +8 -5
- pangea/file_uploader.py +4 -0
- pangea/request.py +63 -47
- pangea/response.py +21 -18
- pangea/services/__init__.py +2 -0
- pangea/services/ai_guard.py +35 -24
- pangea/services/audit/audit.py +10 -7
- pangea/services/audit/models.py +71 -34
- pangea/services/audit/signing.py +1 -1
- pangea/services/audit/util.py +10 -10
- pangea/services/authn/authn.py +15 -4
- pangea/services/authn/models.py +10 -56
- pangea/services/authz.py +4 -0
- pangea/services/base.py +7 -4
- pangea/services/embargo.py +6 -0
- pangea/services/file_scan.py +7 -1
- pangea/services/intel.py +36 -19
- pangea/services/redact.py +4 -0
- pangea/services/sanitize.py +5 -1
- pangea/services/share/share.py +13 -7
- pangea/services/vault/models/asymmetric.py +4 -0
- pangea/services/vault/models/common.py +4 -0
- pangea/services/vault/models/symmetric.py +4 -0
- pangea/services/vault/vault.py +2 -4
- pangea/tools.py +13 -9
- pangea/utils.py +3 -5
- pangea/verify_audit.py +23 -27
- {pangea_sdk-6.1.1.dist-info → pangea_sdk-6.2.0.dist-info}/METADATA +36 -17
- pangea_sdk-6.2.0.dist-info/RECORD +60 -0
- pangea_sdk-6.1.1.dist-info/RECORD +0 -60
- {pangea_sdk-6.1.1.dist-info → pangea_sdk-6.2.0.dist-info}/WHEEL +0 -0
pangea/__init__.py
CHANGED
@@ -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
|
+
)
|
pangea/asyncio/__init__.py
CHANGED
pangea/asyncio/file_uploader.py
CHANGED
@@ -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"))]
|
pangea/asyncio/request.py
CHANGED
@@ -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,
|
@@ -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
|
|
pangea/asyncio/services/audit.py
CHANGED
@@ -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,
|
pangea/asyncio/services/authn.py
CHANGED
@@ -1,7 +1,12 @@
|
|
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
|
|
9
|
+
from collections.abc import Mapping
|
5
10
|
from typing import Dict, List, Literal, Optional, Union
|
6
11
|
|
7
12
|
import pangea.services.authn.models as m
|
@@ -475,7 +480,7 @@ class AuthNAsync(ServiceBaseAsync):
|
|
475
480
|
async def create(
|
476
481
|
self,
|
477
482
|
email: str,
|
478
|
-
profile:
|
483
|
+
profile: Mapping[str, str],
|
479
484
|
*,
|
480
485
|
username: str | None = None,
|
481
486
|
) -> PangeaResponse[m.UserCreateResult]:
|
@@ -866,7 +871,7 @@ class AuthNAsync(ServiceBaseAsync):
|
|
866
871
|
|
867
872
|
async def update(
|
868
873
|
self,
|
869
|
-
profile:
|
874
|
+
profile: Mapping[str, str],
|
870
875
|
id: str | None = None,
|
871
876
|
email: str | None = None,
|
872
877
|
*,
|
@@ -995,7 +1000,10 @@ class AuthNAsync(ServiceBaseAsync):
|
|
995
1000
|
)
|
996
1001
|
|
997
1002
|
async def restart(
|
998
|
-
self,
|
1003
|
+
self,
|
1004
|
+
flow_id: str,
|
1005
|
+
choice: m.FlowChoice,
|
1006
|
+
data: m.FlowRestartData = {}, # noqa: B006
|
999
1007
|
) -> PangeaResponse[m.FlowRestartResult]:
|
1000
1008
|
"""
|
1001
1009
|
Restart a sign-up/sign-in flow
|
@@ -1068,7 +1076,10 @@ class AuthNAsync(ServiceBaseAsync):
|
|
1068
1076
|
return await self.request.post("v2/flow/start", m.FlowStartResult, data=input.model_dump(exclude_none=True))
|
1069
1077
|
|
1070
1078
|
async def update(
|
1071
|
-
self,
|
1079
|
+
self,
|
1080
|
+
flow_id: str,
|
1081
|
+
choice: m.FlowChoice,
|
1082
|
+
data: m.FlowUpdateData = {}, # noqa: B006
|
1072
1083
|
) -> PangeaResponse[m.FlowUpdateResult]:
|
1073
1084
|
"""
|
1074
1085
|
Update a sign-up/sign-in flow
|
pangea/asyncio/services/base.py
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
|
+
|
4
|
+
# TODO: Modernize.
|
5
|
+
# ruff: noqa: UP006, UP035
|
6
|
+
|
7
|
+
from __future__ import annotations
|
8
|
+
|
3
9
|
import io
|
4
10
|
import logging
|
5
11
|
from typing import Dict, List, Optional, Tuple
|
@@ -98,7 +104,7 @@ class FileScanAsync(ServiceBaseAsync):
|
|
98
104
|
files: Optional[List[Tuple]] = None
|
99
105
|
if file or file_path:
|
100
106
|
if file_path:
|
101
|
-
file = open(file_path, "rb")
|
107
|
+
file = open(file_path, "rb") # noqa: SIM115
|
102
108
|
if transfer_method == TransferMethod.POST_URL:
|
103
109
|
params = get_file_upload_params(file) # type: ignore[arg-type]
|
104
110
|
crc = params.crc_hex
|
pangea/asyncio/services/intel.py
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
|
+
|
4
|
+
# TODO: Modernize.
|
5
|
+
# ruff: noqa: UP006, UP035
|
6
|
+
from __future__ import annotations
|
7
|
+
|
3
8
|
import hashlib
|
4
|
-
from typing import List, Optional
|
9
|
+
from typing import List, Literal, Optional
|
5
10
|
|
6
11
|
import pangea.services.intel as m
|
7
12
|
from pangea.asyncio.services.base import ServiceBaseAsync
|
@@ -73,11 +78,11 @@ class FileIntelAsync(ServiceBaseAsync):
|
|
73
78
|
|
74
79
|
async def hash_reputation_bulk(
|
75
80
|
self,
|
76
|
-
hashes:
|
77
|
-
hash_type:
|
78
|
-
provider:
|
79
|
-
verbose:
|
80
|
-
raw:
|
81
|
+
hashes: list[str],
|
82
|
+
hash_type: Literal["sha256", "sha", "md5"],
|
83
|
+
provider: Literal["reversinglabs", "crowdstrike"] | None = None,
|
84
|
+
verbose: bool | None = None,
|
85
|
+
raw: bool | None = None,
|
81
86
|
) -> PangeaResponse[m.FileReputationBulkResult]:
|
82
87
|
"""
|
83
88
|
Reputation check
|
@@ -85,11 +90,11 @@ class FileIntelAsync(ServiceBaseAsync):
|
|
85
90
|
Retrieve hash-based file reputation from a provider, including an optional detailed report.
|
86
91
|
|
87
92
|
Args:
|
88
|
-
hashes
|
89
|
-
hash_type
|
90
|
-
provider
|
91
|
-
verbose
|
92
|
-
raw
|
93
|
+
hashes: The hash of each file to be looked up
|
94
|
+
hash_type: One of "sha256", "sha", "md5"
|
95
|
+
provider: Use reputation data from these providers: "reversinglabs" or "crowdstrike"
|
96
|
+
verbose: Echo the API parameters in the response
|
97
|
+
raw: Include raw data from this provider
|
93
98
|
|
94
99
|
Raises:
|
95
100
|
PangeaAPIException: If an API Error happens
|
@@ -97,12 +102,8 @@ class FileIntelAsync(ServiceBaseAsync):
|
|
97
102
|
Returns:
|
98
103
|
A PangeaResponse where the sanctioned source(s) are in the
|
99
104
|
response.result field. Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/file-intel).
|
100
|
-
|
101
|
-
Examples:
|
102
|
-
FIXME:
|
103
|
-
|
104
105
|
"""
|
105
|
-
input = m.FileReputationBulkRequest(
|
106
|
+
input = m.FileReputationBulkRequest(
|
106
107
|
hashes=hashes, hash_type=hash_type, verbose=verbose, raw=raw, provider=provider
|
107
108
|
)
|
108
109
|
return await self.request.post(
|
@@ -144,8 +145,8 @@ class FileIntelAsync(ServiceBaseAsync):
|
|
144
145
|
)
|
145
146
|
"""
|
146
147
|
|
147
|
-
|
148
|
-
|
148
|
+
with open(filepath, "rb") as data:
|
149
|
+
hash = hashlib.sha256(data.read()).hexdigest()
|
149
150
|
|
150
151
|
input = m.FileReputationRequest(hash=hash, hash_type="sha256", verbose=verbose, raw=raw, provider=provider)
|
151
152
|
return await self.request.post(
|
@@ -154,8 +155,8 @@ class FileIntelAsync(ServiceBaseAsync):
|
|
154
155
|
|
155
156
|
async def filepath_reputation_bulk(
|
156
157
|
self,
|
157
|
-
filepaths:
|
158
|
-
provider:
|
158
|
+
filepaths: list[str],
|
159
|
+
provider: Literal["reversinglabs", "crowdstrike"] | None = None,
|
159
160
|
verbose: Optional[bool] = None,
|
160
161
|
raw: Optional[bool] = None,
|
161
162
|
) -> PangeaResponse[m.FileReputationBulkResult]:
|
@@ -168,10 +169,10 @@ class FileIntelAsync(ServiceBaseAsync):
|
|
168
169
|
OperationId: file_intel_post_v1_reputation
|
169
170
|
|
170
171
|
Args:
|
171
|
-
filepaths
|
172
|
-
provider
|
173
|
-
verbose
|
174
|
-
raw
|
172
|
+
filepaths: The path list to the files to be looked up
|
173
|
+
provider: Use reputation data from these providers: "reversinglabs" or "crowdstrike"
|
174
|
+
verbose: Echo the API parameters in the response
|
175
|
+
raw: Include raw data from this provider
|
175
176
|
|
176
177
|
Raises:
|
177
178
|
PangeaAPIException: If an API Error happens
|
@@ -179,9 +180,6 @@ class FileIntelAsync(ServiceBaseAsync):
|
|
179
180
|
Returns:
|
180
181
|
A PangeaResponse where the sanctioned source(s) are in the
|
181
182
|
response.result field. Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/file-intel).
|
182
|
-
|
183
|
-
Examples:
|
184
|
-
FIXME:
|
185
183
|
"""
|
186
184
|
hashes = []
|
187
185
|
for filepath in filepaths:
|
@@ -317,7 +315,7 @@ class DomainIntelAsync(ServiceBaseAsync):
|
|
317
315
|
provider="whoisxml",
|
318
316
|
)
|
319
317
|
"""
|
320
|
-
input = m.DomainWhoIsRequest(domain=domain, verbose=verbose, provider=provider, raw=raw)
|
318
|
+
input = m.DomainWhoIsRequest(domain=domain, verbose=verbose, provider=provider, raw=raw)
|
321
319
|
return await self.request.post("v1/whois", m.DomainWhoIsResult, data=input.model_dump(exclude_none=True))
|
322
320
|
|
323
321
|
|
@@ -1,5 +1,9 @@
|
|
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 io
|
@@ -116,7 +120,7 @@ class SanitizeAsync(ServiceBaseAsync):
|
|
116
120
|
files: Optional[List[Tuple]] = None
|
117
121
|
if file or file_path:
|
118
122
|
if file_path:
|
119
|
-
file = open(file_path, "rb")
|
123
|
+
file = open(file_path, "rb") # noqa: SIM115
|
120
124
|
if transfer_method == TransferMethod.POST_URL and (sha256 is None or crc32c is None or size is None):
|
121
125
|
params = get_file_upload_params(file) # type: ignore[arg-type]
|
122
126
|
crc32c = params.crc_hex if crc32c is None else crc32c
|
pangea/asyncio/services/share.py
CHANGED
@@ -1,5 +1,9 @@
|
|
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 io
|
@@ -194,7 +198,7 @@ class ShareAsync(ServiceBaseAsync):
|
|
194
198
|
|
195
199
|
async def get_archive(
|
196
200
|
self,
|
197
|
-
ids:
|
201
|
+
ids: list[str],
|
198
202
|
format: Optional[m.ArchiveFormat] = None,
|
199
203
|
transfer_method: Optional[TransferMethod] = None,
|
200
204
|
bucket_id: Optional[str] = None,
|
pangea/asyncio/services/vault.py
CHANGED
pangea/audit_logger.py
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
|
+
from __future__ import annotations
|
4
|
+
|
3
5
|
import logging
|
4
6
|
|
5
7
|
import pangea.exceptions as pe
|
@@ -20,7 +22,7 @@ class AuditLogger(logging.Logger):
|
|
20
22
|
"""
|
21
23
|
|
22
24
|
def __init__(self, *args, **kwargs):
|
23
|
-
super(
|
25
|
+
super().__init__(*args, **kwargs)
|
24
26
|
|
25
27
|
def set_auditor(self, auditor: Audit):
|
26
28
|
"""Sets the internal Pangea Audit Service client instance
|