pangea-sdk 4.2.0__py3-none-any.whl → 4.3.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 CHANGED
@@ -1,6 +1,7 @@
1
- __version__ = "4.2.0"
1
+ __version__ = "4.3.0"
2
2
 
3
3
  from pangea.asyncio.request import PangeaRequestAsync
4
4
  from pangea.config import PangeaConfig
5
+ from pangea.file_uploader import FileUploader
5
6
  from pangea.request import PangeaRequest
6
7
  from pangea.response import PangeaResponse
@@ -0,0 +1 @@
1
+ from .file_uploader import FileUploaderAsync
@@ -0,0 +1,39 @@
1
+ # Copyright 2022 Pangea Cyber Corporation
2
+ # Author: Pangea Cyber Corporation
3
+ import io
4
+ import logging
5
+ from typing import Dict, Optional
6
+
7
+ from pangea.asyncio.request import PangeaRequestAsync
8
+ from pangea.request import PangeaConfig
9
+ from pangea.response import TransferMethod
10
+
11
+
12
+ class FileUploaderAsync:
13
+ def __init__(self) -> None:
14
+ self.logger = logging.getLogger("pangea")
15
+ self._request: PangeaRequestAsync = PangeaRequestAsync(
16
+ config=PangeaConfig(),
17
+ token="",
18
+ service="FileUploader",
19
+ logger=self.logger,
20
+ )
21
+
22
+ async def upload_file(
23
+ self,
24
+ url: str,
25
+ file: io.BufferedReader,
26
+ transfer_method: TransferMethod = TransferMethod.PUT_URL,
27
+ file_details: Optional[Dict] = None,
28
+ ) -> None:
29
+ if transfer_method == TransferMethod.PUT_URL:
30
+ files = [("file", ("filename", file, "application/octet-stream"))]
31
+ await self._request.put_presigned_url(url=url, files=files)
32
+ elif transfer_method == TransferMethod.POST_URL:
33
+ files = [("file", ("filename", file, "application/octet-stream"))]
34
+ await self._request.post_presigned_url(url=url, data=file_details, files=files) # type: ignore[arg-type]
35
+ else:
36
+ raise ValueError(f"Transfer method not supported: {transfer_method}")
37
+
38
+ async def close(self) -> None:
39
+ await self._request.session.close()
pangea/asyncio/request.py CHANGED
@@ -32,7 +32,7 @@ class PangeaRequestAsync(PangeaRequestBase):
32
32
  endpoint: str,
33
33
  result_class: Type[TResult],
34
34
  data: Union[str, Dict] = {},
35
- files: List[Tuple] = [],
35
+ files: Optional[List[Tuple]] = None,
36
36
  poll_result: bool = True,
37
37
  url: Optional[str] = None,
38
38
  ) -> PangeaResponse[TResult]:
@@ -250,7 +250,7 @@ class PangeaRequestAsync(PangeaRequestBase):
250
250
  url: str,
251
251
  headers: Dict = {},
252
252
  data: Union[str, Dict] = {},
253
- files: List[Tuple] = [],
253
+ files: Optional[List[Tuple]] = [],
254
254
  presigned_url_post: bool = False,
255
255
  ) -> aiohttp.ClientResponse:
256
256
  if files:
@@ -5,4 +5,5 @@ from .embargo import EmbargoAsync
5
5
  from .file_scan import FileScanAsync
6
6
  from .intel import DomainIntelAsync, FileIntelAsync, IpIntelAsync, UrlIntelAsync, UserIntelAsync
7
7
  from .redact import RedactAsync
8
+ from .sanitize import SanitizeAsync
8
9
  from .vault import VaultAsync
@@ -1,7 +1,7 @@
1
1
  # Copyright 2022 Pangea Cyber Corporation
2
2
  # Author: Pangea Cyber Corporation
3
3
 
4
- from typing import Dict, List, Optional, Union
4
+ from typing import Any, Dict, List, Optional
5
5
 
6
6
  from pangea.asyncio.services.base import ServiceBaseAsync
7
7
  from pangea.response import PangeaResponse
@@ -162,7 +162,7 @@ class AuthZAsync(ServiceBaseAsync):
162
162
  action: str,
163
163
  subject: Subject,
164
164
  debug: Optional[bool] = None,
165
- attributes: Optional[Dict[str, Union[int, str]]] = None,
165
+ attributes: Optional[Dict[str, Any]] = None,
166
166
  ) -> PangeaResponse[CheckResult]:
167
167
  """Perform a check request.
168
168
 
@@ -173,7 +173,7 @@ class AuthZAsync(ServiceBaseAsync):
173
173
  action (str): The action to check.
174
174
  subject (Subject): The subject to check.
175
175
  debug (Optional[bool]): Setting this value to True will provide a detailed analysis of the check.
176
- attributes (Optional[Dict[str, Union[int, str]]]): Additional attributes for the check.
176
+ attributes (Optional[Dict[str, Any]]): Additional attributes for the check.
177
177
 
178
178
  Raises:
179
179
  PangeaAPIException: If an API Error happens.
@@ -195,8 +195,10 @@ class AuthZAsync(ServiceBaseAsync):
195
195
  input_data = CheckRequest(resource=resource, action=action, subject=subject, debug=debug, attributes=attributes)
196
196
  return await self.request.post("v1/check", CheckResult, data=input_data.model_dump(exclude_none=True))
197
197
 
198
- async def list_resources(self, type: str, action: str, subject: Subject) -> PangeaResponse[ListResourcesResult]:
199
- """List resources.
198
+ async def list_resources(
199
+ self, type: str, action: str, subject: Subject, attributes: Optional[Dict[str, Any]] = None
200
+ ) -> PangeaResponse[ListResourcesResult]:
201
+ """List resources. (Beta)
200
202
 
201
203
  Given a type, action, and subject, list all the resources in the
202
204
  type that the subject has access to the action with.
@@ -205,6 +207,7 @@ class AuthZAsync(ServiceBaseAsync):
205
207
  type (str): The type to filter resources.
206
208
  action (str): The action to filter resources.
207
209
  subject (Subject): The subject to filter resources.
210
+ attributes (Optional[Dict[str, Any]]): A JSON object of attribute data.
208
211
 
209
212
  Raises:
210
213
  PangeaAPIException: If an API Error happens.
@@ -222,13 +225,15 @@ class AuthZAsync(ServiceBaseAsync):
222
225
  )
223
226
  """
224
227
 
225
- input_data = ListResourcesRequest(type=type, action=action, subject=subject)
228
+ input_data = ListResourcesRequest(type=type, action=action, subject=subject, attributes=attributes)
226
229
  return await self.request.post(
227
230
  "v1/list-resources", ListResourcesResult, data=input_data.model_dump(exclude_none=True)
228
231
  )
229
232
 
230
- async def list_subjects(self, resource: Resource, action: str) -> PangeaResponse[ListSubjectsResult]:
231
- """List subjects.
233
+ async def list_subjects(
234
+ self, resource: Resource, action: str, attributes: Optional[Dict[str, Any]] = None
235
+ ) -> PangeaResponse[ListSubjectsResult]:
236
+ """List subjects. (Beta)
232
237
 
233
238
  Given a resource and an action, return the list of subjects who have
234
239
  access to the action for the given resource.
@@ -236,6 +241,7 @@ class AuthZAsync(ServiceBaseAsync):
236
241
  Args:
237
242
  resource (Resource): The resource to filter subjects.
238
243
  action (str): The action to filter subjects.
244
+ attributes (Optional[Dict[str, Any]]): A JSON object of attribute data.
239
245
 
240
246
  Raises:
241
247
  PangeaAPIException: If an API Error happens.
@@ -252,7 +258,7 @@ class AuthZAsync(ServiceBaseAsync):
252
258
  )
253
259
  """
254
260
 
255
- input_data = ListSubjectsRequest(resource=resource, action=action)
261
+ input_data = ListSubjectsRequest(resource=resource, action=action, attributes=attributes)
256
262
  return await self.request.post(
257
263
  "v1/list-subjects", ListSubjectsResult, data=input_data.model_dump(exclude_none=True)
258
264
  )
@@ -59,12 +59,14 @@ class FileScanAsync(ServiceBaseAsync):
59
59
  OperationId: file_scan_post_v1_scan
60
60
 
61
61
  Args:
62
- file (io.BufferedReader, optional): file to be scanned (should be opened with read permissions and in binary format)
63
62
  file_path (str, optional): filepath to be opened and scanned
63
+ file (io.BufferedReader, optional): file to be scanned (should be opened with read permissions and in binary format)
64
64
  verbose (bool, optional): Echo the API parameters in the response
65
65
  raw (bool, optional): Include raw data from this provider
66
66
  provider (str, optional): Scan file using this provider
67
67
  sync_call (bool, optional): True to wait until server returns a result, False to return immediately and retrieve result asynchronously
68
+ transfer_method (TransferMethod, optional): Transfer method used to upload the file data.
69
+ source_url (str, optional): A URL where the Pangea APIs can fetch the contents of the input file.
68
70
 
69
71
  Raises:
70
72
  PangeaAPIException: If an API Error happens
@@ -77,7 +79,7 @@ class FileScanAsync(ServiceBaseAsync):
77
79
  Examples:
78
80
  try:
79
81
  with open("./path/to/file.pdf", "rb") as f:
80
- response = client.file_scan(file=f, verbose=True, provider="crowdstrike")
82
+ response = await client.file_scan(file=f, verbose=True, provider="crowdstrike")
81
83
  print(f"Response: {response.result}")
82
84
  except pe.PangeaAPIException as e:
83
85
  print(f"Request Error: {e.response.summary}")
@@ -85,6 +87,15 @@ class FileScanAsync(ServiceBaseAsync):
85
87
  print(f"\\t{err.detail} \\n")
86
88
  """
87
89
 
90
+ if transfer_method == TransferMethod.SOURCE_URL and source_url is None:
91
+ raise ValueError("`source_url` argument is required when using `TransferMethod.SOURCE_URL`.")
92
+
93
+ if source_url is not None and transfer_method != TransferMethod.SOURCE_URL:
94
+ raise ValueError(
95
+ "`transfer_method` should be `TransferMethod.SOURCE_URL` when using the `source_url` argument."
96
+ )
97
+
98
+ files: Optional[List[Tuple]] = None
88
99
  if file or file_path:
89
100
  if file_path:
90
101
  file = open(file_path, "rb")
@@ -95,9 +106,9 @@ class FileScanAsync(ServiceBaseAsync):
95
106
  size = params.size
96
107
  else:
97
108
  crc, sha, size = None, None, None
98
- files: List[Tuple] = [("upload", ("filename", file, "application/octet-stream"))]
99
- else:
100
- raise ValueError("Need to set file_path or file arguments")
109
+ files = [("upload", ("filename", file, "application/octet-stream"))]
110
+ elif source_url is None:
111
+ raise ValueError("Need to set one of `file_path`, `file`, or `source_url` arguments.")
101
112
 
102
113
  input = m.FileScanRequest(
103
114
  verbose=verbose,
@@ -110,7 +121,11 @@ class FileScanAsync(ServiceBaseAsync):
110
121
  source_url=source_url,
111
122
  )
112
123
  data = input.model_dump(exclude_none=True)
113
- return await self.request.post("v1/scan", m.FileScanResult, data=data, files=files, poll_result=sync_call)
124
+ try:
125
+ return await self.request.post("v1/scan", m.FileScanResult, data=data, files=files, poll_result=sync_call)
126
+ finally:
127
+ if file_path and file:
128
+ file.close()
114
129
 
115
130
  async def request_upload_url(
116
131
  self,
@@ -0,0 +1,193 @@
1
+ # Copyright 2022 Pangea Cyber Corporation
2
+ # Author: Pangea Cyber Corporation
3
+ import io
4
+ from typing import List, Optional, Tuple
5
+
6
+ import pangea.services.sanitize as m
7
+ from pangea.asyncio.services.base import ServiceBaseAsync
8
+ from pangea.response import PangeaResponse, TransferMethod
9
+ from pangea.utils import FileUploadParams, get_file_upload_params
10
+
11
+
12
+ class SanitizeAsync(ServiceBaseAsync):
13
+ """Sanitize service client.
14
+
15
+ Examples:
16
+ import os
17
+
18
+ # Pangea SDK
19
+ from pangea.config import PangeaConfig
20
+ from pangea.asyncio.services import Sanitize
21
+
22
+ PANGEA_SANITIZE_TOKEN = os.getenv("PANGEA_SANITIZE_TOKEN")
23
+ config = PangeaConfig(domain="pangea.cloud")
24
+
25
+ sanitize = Sanitize(token=PANGEA_SANITIZE_TOKEN, config=config)
26
+ """
27
+
28
+ service_name = "sanitize"
29
+
30
+ async def sanitize(
31
+ self,
32
+ transfer_method: TransferMethod = TransferMethod.POST_URL,
33
+ file_path: Optional[str] = None,
34
+ file: Optional[io.BufferedReader] = None,
35
+ source_url: Optional[str] = None,
36
+ share_id: Optional[str] = None,
37
+ file_scan: Optional[m.SanitizeFile] = None,
38
+ content: Optional[m.SanitizeContent] = None,
39
+ share_output: Optional[m.SanitizeShareOutput] = None,
40
+ size: Optional[int] = None,
41
+ crc32c: Optional[str] = None,
42
+ sha256: Optional[str] = None,
43
+ uploaded_file_name: Optional[str] = None,
44
+ sync_call: bool = True,
45
+ ) -> PangeaResponse[m.SanitizeResult]:
46
+ """
47
+ Sanitize
48
+
49
+ Apply file sanitization actions according to specified rules.
50
+
51
+ OperationId: sanitize_post_v1_sanitize
52
+
53
+ Args:
54
+ transfer_method: The transfer method used to upload the file data.
55
+ file_path: Path to file to sanitize.
56
+ file: File to sanitize.
57
+ source_url: A URL where the file to be sanitized can be downloaded.
58
+ share_id: A Pangea Secure Share ID where the file to be sanitized is stored.
59
+ file_scan: Options for File Scan.
60
+ content: Options for how the file should be sanitized.
61
+ share_output: Integration with Secure Share.
62
+ size: The size (in bytes) of the file. If the upload doesn't match, the call will fail.
63
+ crc32c: The CRC32C hash of the file data, which will be verified by the server if provided.
64
+ sha256: The hexadecimal-encoded SHA256 hash of the file data, which will be verified by the server if provided.
65
+ uploaded_file_name: Name of the user-uploaded file, required for `TransferMethod.PUT_URL` and `TransferMethod.POST_URL`.
66
+ sync_call: Whether or not to poll on HTTP/202.
67
+
68
+ Raises:
69
+ PangeaAPIException: If an API error happens.
70
+
71
+ Returns:
72
+ The sanitized file and information on the sanitization that was
73
+ performed.
74
+
75
+ Examples:
76
+ with open("/path/to/file.pdf", "rb") as f:
77
+ response = await sanitize.sanitize(
78
+ file=f,
79
+ transfer_method=TransferMethod.POST_URL,
80
+ uploaded_file_name="uploaded_file",
81
+ )
82
+ """
83
+
84
+ if transfer_method == TransferMethod.SOURCE_URL and source_url is None:
85
+ raise ValueError("`source_url` argument is required when using `TransferMethod.SOURCE_URL`.")
86
+
87
+ if source_url is not None and transfer_method != TransferMethod.SOURCE_URL:
88
+ raise ValueError(
89
+ "`transfer_method` should be `TransferMethod.SOURCE_URL` when using the `source_url` argument."
90
+ )
91
+
92
+ files: Optional[List[Tuple]] = None
93
+ if file or file_path:
94
+ if file_path:
95
+ file = open(file_path, "rb")
96
+ if transfer_method == TransferMethod.POST_URL and (sha256 is None or crc32c is None or size is None):
97
+ params = get_file_upload_params(file) # type: ignore[arg-type]
98
+ crc32c = params.crc_hex if crc32c is None else crc32c
99
+ sha256 = params.sha256_hex if sha256 is None else sha256
100
+ size = params.size if size is None else size
101
+ else:
102
+ crc32c, sha256, size = None, None, None
103
+ files = [("upload", ("filename", file, "application/octet-stream"))]
104
+ elif source_url is None:
105
+ raise ValueError("Need to set one of `file_path`, `file`, or `source_url` arguments.")
106
+
107
+ input = m.SanitizeRequest(
108
+ transfer_method=transfer_method,
109
+ source_url=source_url,
110
+ share_id=share_id,
111
+ file=file_scan,
112
+ content=content,
113
+ share_output=share_output,
114
+ crc32c=crc32c,
115
+ sha256=sha256,
116
+ size=size,
117
+ uploaded_file_name=uploaded_file_name,
118
+ )
119
+ data = input.model_dump(exclude_none=True)
120
+ try:
121
+ return await self.request.post(
122
+ "v1/sanitize", m.SanitizeResult, data=data, files=files, poll_result=sync_call
123
+ )
124
+ finally:
125
+ if file_path and file is not None:
126
+ file.close()
127
+
128
+ async def request_upload_url(
129
+ self,
130
+ transfer_method: TransferMethod = TransferMethod.PUT_URL,
131
+ params: Optional[FileUploadParams] = None,
132
+ file_scan: Optional[m.SanitizeFile] = None,
133
+ content: Optional[m.SanitizeContent] = None,
134
+ share_output: Optional[m.SanitizeShareOutput] = None,
135
+ size: Optional[int] = None,
136
+ crc32c: Optional[str] = None,
137
+ sha256: Optional[str] = None,
138
+ uploaded_file_name: Optional[str] = None,
139
+ ) -> PangeaResponse[m.SanitizeResult]:
140
+ """
141
+ Sanitize via presigned URL
142
+
143
+ Apply file sanitization actions according to specified rules via a
144
+ [presigned URL](https://pangea.cloud/docs/api/transfer-methods).
145
+
146
+ OperationId: sanitize_post_v1_sanitize 2
147
+
148
+ Args:
149
+ transfer_method: The transfer method used to upload the file data.
150
+ params: File upload parameters.
151
+ file_scan: Options for File Scan.
152
+ content: Options for how the file should be sanitized.
153
+ share_output: Integration with Secure Share.
154
+ size: The size (in bytes) of the file. If the upload doesn't match, the call will fail.
155
+ crc32c: The CRC32C hash of the file data, which will be verified by the server if provided.
156
+ sha256: The hexadecimal-encoded SHA256 hash of the file data, which will be verified by the server if provided.
157
+ uploaded_file_name: Name of the user-uploaded file, required for `TransferMethod.PUT_URL` and `TransferMethod.POST_URL`.
158
+
159
+ Raises:
160
+ PangeaAPIException: If an API error happens.
161
+
162
+ Returns:
163
+ A presigned URL.
164
+
165
+ Examples:
166
+ presignedUrl = await sanitize.request_upload_url(
167
+ transfer_method=TransferMethod.PUT_URL,
168
+ uploaded_file_name="uploaded_file",
169
+ )
170
+
171
+ # Upload file to `presignedUrl.accepted_result.put_url`.
172
+
173
+ # Poll for Sanitize's result.
174
+ response: PangeaResponse[SanitizeResult] = await sanitize.poll_result(response=presignedUrl)
175
+ """
176
+
177
+ input = m.SanitizeRequest(
178
+ transfer_method=transfer_method,
179
+ file=file_scan,
180
+ content=content,
181
+ share_output=share_output,
182
+ crc32c=crc32c,
183
+ sha256=sha256,
184
+ size=size,
185
+ uploaded_file_name=uploaded_file_name,
186
+ )
187
+ if params is not None and (transfer_method == TransferMethod.POST_URL):
188
+ input.crc32c = params.crc_hex
189
+ input.sha256 = params.sha256_hex
190
+ input.size = params.size
191
+
192
+ data = input.model_dump(exclude_none=True)
193
+ return await self.request.request_presigned_url("v1/sanitize", m.SanitizeResult, data=data)
@@ -0,0 +1,35 @@
1
+ # Copyright 2022 Pangea Cyber Corporation
2
+ # Author: Pangea Cyber Corporation
3
+ import io
4
+ import logging
5
+ from typing import Dict, Optional
6
+
7
+ from pangea.request import PangeaConfig, PangeaRequest
8
+ from pangea.response import TransferMethod
9
+
10
+
11
+ class FileUploader:
12
+ def __init__(self):
13
+ self.logger = logging.getLogger("pangea")
14
+ self._request = PangeaRequest(
15
+ config=PangeaConfig(),
16
+ token="",
17
+ service="FileUploader",
18
+ logger=self.logger,
19
+ )
20
+
21
+ def upload_file(
22
+ self,
23
+ url: str,
24
+ file: io.BufferedReader,
25
+ transfer_method: TransferMethod = TransferMethod.PUT_URL,
26
+ file_details: Optional[Dict] = None,
27
+ ):
28
+ if transfer_method == TransferMethod.PUT_URL:
29
+ files = [("file", ("filename", file, "application/octet-stream"))]
30
+ self._request.put_presigned_url(url=url, files=files)
31
+ elif transfer_method == TransferMethod.POST_URL:
32
+ files = [("file", ("filename", file, "application/octet-stream"))]
33
+ self._request.post_presigned_url(url=url, data=file_details, files=files)
34
+ else:
35
+ raise ValueError(f"Transfer method not supported: {transfer_method}")
pangea/request.py CHANGED
@@ -182,7 +182,7 @@ class PangeaRequestBase(object):
182
182
  elif status == ResponseStatus.VAULT_ITEM_NOT_FOUND.value:
183
183
  raise pe.VaultItemNotFound(summary, response)
184
184
  elif status == ResponseStatus.NOT_FOUND.value:
185
- raise pe.NotFound(str(response.raw_response.url) if response.raw_response is not None else "", response) # type: ignore[arg-type]
185
+ raise pe.NotFound(str(response.raw_response.url) if response.raw_response is not None else "", response)
186
186
  elif status == ResponseStatus.INTERNAL_SERVER_ERROR.value:
187
187
  raise pe.InternalServerError(response)
188
188
  elif status == ResponseStatus.ACCEPTED.value:
pangea/response.py CHANGED
@@ -37,10 +37,24 @@ class AttachedFile(object):
37
37
 
38
38
 
39
39
  class TransferMethod(str, enum.Enum):
40
+ """Transfer methods for uploading file data."""
41
+
40
42
  MULTIPART = "multipart"
41
43
  POST_URL = "post-url"
42
44
  PUT_URL = "put-url"
43
45
  SOURCE_URL = "source-url"
46
+ """
47
+ A `source-url` is a caller-specified URL where the Pangea APIs can fetch the
48
+ contents of the input file. When calling a Pangea API with a
49
+ `transfer_method` of `source-url`, you must also specify a `source_url`
50
+ input parameter that provides a URL to the input file. The source URL can be
51
+ a presigned URL created by the caller, and it will be used to download the
52
+ content of the input file. The `source-url` transfer method is useful when
53
+ you already have a file in your storage and can provide a URL from which
54
+ Pangea API can fetch the input file—there is no need to transfer it to
55
+ Pangea with a separate POST or PUT request.
56
+ """
57
+
44
58
  DEST_URL = "dest-url"
45
59
 
46
60
  def __str__(self):
@@ -222,4 +236,4 @@ class PangeaResponse(ResponseHeader, Generic[T]):
222
236
 
223
237
  @property
224
238
  def url(self) -> str:
225
- return str(self.raw_response.url) # type: ignore[arg-type,union-attr]
239
+ return str(self.raw_response.url) # type: ignore[union-attr]
@@ -5,4 +5,5 @@ from .embargo import Embargo
5
5
  from .file_scan import FileScan
6
6
  from .intel import DomainIntel, FileIntel, IpIntel, UrlIntel, UserIntel
7
7
  from .redact import Redact
8
+ from .sanitize import Sanitize
8
9
  from .vault.vault import Vault
@@ -96,7 +96,7 @@ class Signer:
96
96
 
97
97
  for func in (serialization.load_pem_private_key, serialization.load_ssh_private_key):
98
98
  try:
99
- return func(private_key, None) # type: ignore[operator]
99
+ return func(private_key, None)
100
100
  except exceptions.UnsupportedAlgorithm as e:
101
101
  raise e
102
102
  except ValueError:
pangea/services/authz.py CHANGED
@@ -132,6 +132,7 @@ class ListResourcesRequest(APIRequestModel):
132
132
  type: str
133
133
  action: str
134
134
  subject: Subject
135
+ attributes: Optional[Dict[str, Any]] = None
135
136
 
136
137
 
137
138
  class ListResourcesResult(PangeaResponseResult):
@@ -141,6 +142,7 @@ class ListResourcesResult(PangeaResponseResult):
141
142
  class ListSubjectsRequest(APIRequestModel):
142
143
  resource: Resource
143
144
  action: str
145
+ attributes: Optional[Dict[str, Any]] = None
144
146
 
145
147
 
146
148
  class ListSubjectsResult(PangeaResponseResult):
@@ -278,7 +280,7 @@ class AuthZ(ServiceBase):
278
280
  action: str,
279
281
  subject: Subject,
280
282
  debug: Optional[bool] = None,
281
- attributes: Optional[Dict[str, Union[int, str]]] = None,
283
+ attributes: Optional[Dict[str, Any]] = None,
282
284
  ) -> PangeaResponse[CheckResult]:
283
285
  """Perform a check request.
284
286
 
@@ -289,7 +291,7 @@ class AuthZ(ServiceBase):
289
291
  action (str): The action to check.
290
292
  subject (Subject): The subject to check.
291
293
  debug (Optional[bool]): Setting this value to True will provide a detailed analysis of the check.
292
- attributes (Optional[Dict[str, Union[int, str]]]): Additional attributes for the check.
294
+ attributes (Optional[Dict[str, Any]]): Additional attributes for the check.
293
295
 
294
296
  Raises:
295
297
  PangeaAPIException: If an API Error happens.
@@ -311,8 +313,10 @@ class AuthZ(ServiceBase):
311
313
  input_data = CheckRequest(resource=resource, action=action, subject=subject, debug=debug, attributes=attributes)
312
314
  return self.request.post("v1/check", CheckResult, data=input_data.model_dump(exclude_none=True))
313
315
 
314
- def list_resources(self, type: str, action: str, subject: Subject) -> PangeaResponse[ListResourcesResult]:
315
- """List resources.
316
+ def list_resources(
317
+ self, type: str, action: str, subject: Subject, attributes: Optional[Dict[str, Any]] = None
318
+ ) -> PangeaResponse[ListResourcesResult]:
319
+ """List resources. (Beta)
316
320
 
317
321
  Given a type, action, and subject, list all the resources in the
318
322
  type that the subject has access to the action with.
@@ -321,6 +325,7 @@ class AuthZ(ServiceBase):
321
325
  type (str): The type to filter resources.
322
326
  action (str): The action to filter resources.
323
327
  subject (Subject): The subject to filter resources.
328
+ attributes (Optional[Dict[str, Any]]): A JSON object of attribute data.
324
329
 
325
330
  Raises:
326
331
  PangeaAPIException: If an API Error happens.
@@ -338,13 +343,15 @@ class AuthZ(ServiceBase):
338
343
  )
339
344
  """
340
345
 
341
- input_data = ListResourcesRequest(type=type, action=action, subject=subject)
346
+ input_data = ListResourcesRequest(type=type, action=action, subject=subject, attributes=attributes)
342
347
  return self.request.post(
343
348
  "v1/list-resources", ListResourcesResult, data=input_data.model_dump(exclude_none=True)
344
349
  )
345
350
 
346
- def list_subjects(self, resource: Resource, action: str) -> PangeaResponse[ListSubjectsResult]:
347
- """List subjects.
351
+ def list_subjects(
352
+ self, resource: Resource, action: str, attributes: Optional[Dict[str, Any]] = None
353
+ ) -> PangeaResponse[ListSubjectsResult]:
354
+ """List subjects. (Beta)
348
355
 
349
356
  Given a resource and an action, return the list of subjects who have
350
357
  access to the action for the given resource.
@@ -352,6 +359,7 @@ class AuthZ(ServiceBase):
352
359
  Args:
353
360
  resource (Resource): The resource to filter subjects.
354
361
  action (str): The action to filter subjects.
362
+ attributes (Optional[Dict[str, Any]]): A JSON object of attribute data.
355
363
 
356
364
  Raises:
357
365
  PangeaAPIException: If an API Error happens.
@@ -368,5 +376,5 @@ class AuthZ(ServiceBase):
368
376
  )
369
377
  """
370
378
 
371
- input_data = ListSubjectsRequest(resource=resource, action=action)
379
+ input_data = ListSubjectsRequest(resource=resource, action=action, attributes=attributes)
372
380
  return self.request.post("v1/list-subjects", ListSubjectsResult, data=input_data.model_dump(exclude_none=True))
@@ -11,22 +11,25 @@ from pangea.utils import FileUploadParams, get_file_upload_params
11
11
 
12
12
 
13
13
  class FileScanRequest(APIRequestModel):
14
- """
15
- File Scan request data
16
-
17
- provider (str, optional): Provider of the information. Default provider defined by the configuration.
18
- verbose (bool, optional): Echo back the parameters of the API in the response
19
- raw (bool, optional): Return additional details from the provider.
20
- """
14
+ """File Scan request data."""
21
15
 
22
16
  verbose: Optional[bool] = None
17
+ """Echo back the parameters of the API in the response."""
18
+
23
19
  raw: Optional[bool] = None
20
+ """Return additional details from the provider."""
21
+
24
22
  provider: Optional[str] = None
23
+ """Provider of the information. Default provider defined by the configuration."""
24
+
25
25
  size: Optional[int] = None
26
26
  crc32c: Optional[str] = None
27
27
  sha256: Optional[str] = None
28
28
  source_url: Optional[str] = None
29
+ """A URL where the file to be scanned can be downloaded."""
30
+
29
31
  transfer_method: TransferMethod = TransferMethod.POST_URL
32
+ """The transfer method used to upload the file data."""
30
33
 
31
34
 
32
35
  class FileScanData(PangeaResponseResult):
@@ -71,7 +74,6 @@ class FileScan(ServiceBase):
71
74
  """
72
75
 
73
76
  service_name = "file-scan"
74
- version = "v1"
75
77
 
76
78
  def file_scan(
77
79
  self,
@@ -92,12 +94,14 @@ class FileScan(ServiceBase):
92
94
  OperationId: file_scan_post_v1_scan
93
95
 
94
96
  Args:
95
- file (io.BufferedReader, optional): file to be scanned (should be opened with read permissions and in binary format)
96
97
  file_path (str, optional): filepath to be opened and scanned
98
+ file (io.BufferedReader, optional): file to be scanned (should be opened with read permissions and in binary format)
97
99
  verbose (bool, optional): Echo the API parameters in the response
98
100
  raw (bool, optional): Include raw data from this provider
99
101
  provider (str, optional): Scan file using this provider
100
102
  sync_call (bool, optional): True to wait until server returns a result, False to return immediately and retrieve result asynchronously
103
+ transfer_method (TransferMethod, optional): Transfer method used to upload the file data.
104
+ source_url (str, optional): A URL where the Pangea APIs can fetch the contents of the input file.
101
105
 
102
106
  Raises:
103
107
  PangeaAPIException: If an API Error happens
@@ -118,6 +122,15 @@ class FileScan(ServiceBase):
118
122
  print(f"\\t{err.detail} \\n")
119
123
  """
120
124
 
125
+ if transfer_method == TransferMethod.SOURCE_URL and source_url is None:
126
+ raise ValueError("`source_url` argument is required when using `TransferMethod.SOURCE_URL`.")
127
+
128
+ if source_url is not None and transfer_method != TransferMethod.SOURCE_URL:
129
+ raise ValueError(
130
+ "`transfer_method` should be `TransferMethod.SOURCE_URL` when using the `source_url` argument."
131
+ )
132
+
133
+ files: Optional[List[Tuple]] = None
121
134
  if file or file_path:
122
135
  if file_path:
123
136
  file = open(file_path, "rb")
@@ -128,9 +141,9 @@ class FileScan(ServiceBase):
128
141
  size = params.size
129
142
  else:
130
143
  crc, sha, size = None, None, None
131
- files: List[Tuple] = [("upload", ("filename", file, "application/octet-stream"))]
132
- else:
133
- raise ValueError("Need to set file_path or file arguments")
144
+ files = [("upload", ("filename", file, "application/octet-stream"))]
145
+ elif source_url is None:
146
+ raise ValueError("Need to set one of `file_path`, `file`, or `source_url` arguments.")
134
147
 
135
148
  input = FileScanRequest(
136
149
  verbose=verbose,
@@ -143,7 +156,11 @@ class FileScan(ServiceBase):
143
156
  source_url=source_url,
144
157
  )
145
158
  data = input.model_dump(exclude_none=True)
146
- return self.request.post("v1/scan", FileScanResult, data=data, files=files, poll_result=sync_call)
159
+ try:
160
+ return self.request.post("v1/scan", FileScanResult, data=data, files=files, poll_result=sync_call)
161
+ finally:
162
+ if file_path and file:
163
+ file.close()
147
164
 
148
165
  def request_upload_url(
149
166
  self,
@@ -0,0 +1,335 @@
1
+ # Copyright 2022 Pangea Cyber Corporation
2
+ # Author: Pangea Cyber Corporation
3
+ from __future__ import annotations
4
+
5
+ import io
6
+ from typing import Dict, List, Optional, Tuple
7
+
8
+ from pangea.response import APIRequestModel, PangeaResponse, PangeaResponseResult, TransferMethod
9
+ from pangea.services.base import ServiceBase
10
+ from pangea.utils import FileUploadParams, get_file_upload_params
11
+
12
+
13
+ class SanitizeFile(APIRequestModel):
14
+ scan_provider: Optional[str] = None
15
+ """Provider to use for File Scan."""
16
+
17
+
18
+ class SanitizeContent(APIRequestModel):
19
+ url_intel: Optional[bool] = None
20
+ """Perform URL Intel lookup."""
21
+
22
+ url_intel_provider: Optional[str] = None
23
+ """Provider to use for URL Intel."""
24
+
25
+ domain_intel: Optional[bool] = None
26
+ """Perform Domain Intel lookup."""
27
+
28
+ domain_intel_provider: Optional[str] = None
29
+ """Provider to use for Domain Intel lookup."""
30
+
31
+ defang: Optional[bool] = None
32
+ """Defang external links."""
33
+
34
+ defang_threshold: Optional[int] = None
35
+ """Defang risk threshold."""
36
+
37
+ redact: Optional[bool] = None
38
+ """Redact sensitive content."""
39
+
40
+ remove_attachments: Optional[bool] = None
41
+ """Remove file attachments (PDF only)."""
42
+
43
+ remove_interactive: Optional[bool] = None
44
+ """Remove interactive content (PDF only)."""
45
+
46
+
47
+ class SanitizeShareOutput(APIRequestModel):
48
+ enabled: Optional[bool] = None
49
+ """Store Sanitized files to Pangea Secure Share."""
50
+
51
+ output_folder: Optional[str] = None
52
+ """
53
+ Store Sanitized files to this Secure Share folder (will be auto-created if
54
+ it does not exist)
55
+ """
56
+
57
+
58
+ class SanitizeRequest(APIRequestModel):
59
+ transfer_method: TransferMethod = TransferMethod.POST_URL
60
+ """The transfer method used to upload the file data."""
61
+
62
+ source_url: Optional[str] = None
63
+ """A URL where the file to be sanitized can be downloaded."""
64
+
65
+ share_id: Optional[str] = None
66
+ """A Pangea Secure Share ID where the file to be Sanitized is stored."""
67
+
68
+ file: Optional[SanitizeFile] = None
69
+ """File."""
70
+
71
+ content: Optional[SanitizeContent] = None
72
+ """Content."""
73
+
74
+ share_output: Optional[SanitizeShareOutput] = None
75
+ """Share output."""
76
+
77
+ size: Optional[int] = None
78
+ """The size (in bytes) of the file. If the upload doesn't match, the call will fail."""
79
+
80
+ crc32c: Optional[str] = None
81
+ """The CRC32C hash of the file data, which will be verified by the server if provided."""
82
+
83
+ sha256: Optional[str] = None
84
+ """The hexadecimal-encoded SHA256 hash of the file data, which will be verified by the server if provided."""
85
+
86
+ uploaded_file_name: Optional[str] = None
87
+ """Name of the user-uploaded file, required for transfer-method 'put-url' and 'post-url'."""
88
+
89
+
90
+ class DefangData(PangeaResponseResult):
91
+ external_urls_count: Optional[int] = None
92
+ """Number of external links found."""
93
+
94
+ external_domains_count: Optional[int] = None
95
+ """Number of external domains found."""
96
+
97
+ defanged_count: Optional[int] = None
98
+ """Number of items defanged per provided rules and detections."""
99
+
100
+ url_intel_summary: Optional[str] = None
101
+ """Processed N URLs: X are malicious, Y are suspicious, Z are unknown."""
102
+
103
+ domain_intel_summary: Optional[str] = None
104
+ """Processed N Domains: X are malicious, Y are suspicious, Z are unknown."""
105
+
106
+
107
+ class RedactData(PangeaResponseResult):
108
+ redaction_count: Optional[int] = None
109
+ """Number of items redacted"""
110
+
111
+ summary_counts: Dict = {}
112
+ """Summary counts."""
113
+
114
+
115
+ class CDR(PangeaResponseResult):
116
+ file_attachments_removed: Optional[int] = None
117
+ """Number of file attachments removed."""
118
+
119
+ interactive_contents_removed: Optional[int] = None
120
+ """Number of interactive content items removed."""
121
+
122
+
123
+ class SanitizeData(PangeaResponseResult):
124
+ defang: Optional[DefangData] = None
125
+ """Defang."""
126
+
127
+ redact: Optional[RedactData] = None
128
+ """Redact."""
129
+
130
+ malicious_file: Optional[bool] = None
131
+ """If the file scanned was malicious."""
132
+
133
+ cdr: Optional[CDR] = None
134
+ """Content Disarm and Reconstruction."""
135
+
136
+
137
+ class SanitizeResult(PangeaResponseResult):
138
+ dest_url: Optional[str] = None
139
+ """A URL where the Sanitized file can be downloaded."""
140
+
141
+ dest_share_id: Optional[str] = None
142
+ """Pangea Secure Share ID of the Sanitized file."""
143
+
144
+ data: SanitizeData
145
+ """Sanitize data."""
146
+
147
+ parameters: Dict = {}
148
+ """The parameters, which were passed in the request, echoed back."""
149
+
150
+
151
+ class Sanitize(ServiceBase):
152
+ """Sanitize service client.
153
+
154
+ Examples:
155
+ import os
156
+
157
+ # Pangea SDK
158
+ from pangea.config import PangeaConfig
159
+ from pangea.services import Sanitize
160
+
161
+ PANGEA_SANITIZE_TOKEN = os.getenv("PANGEA_SANITIZE_TOKEN")
162
+ config = PangeaConfig(domain="pangea.cloud")
163
+
164
+ sanitize = Sanitize(token=PANGEA_SANITIZE_TOKEN, config=config)
165
+ """
166
+
167
+ service_name = "sanitize"
168
+
169
+ def sanitize(
170
+ self,
171
+ transfer_method: TransferMethod = TransferMethod.POST_URL,
172
+ file_path: Optional[str] = None,
173
+ file: Optional[io.BufferedReader] = None,
174
+ source_url: Optional[str] = None,
175
+ share_id: Optional[str] = None,
176
+ file_scan: Optional[SanitizeFile] = None,
177
+ content: Optional[SanitizeContent] = None,
178
+ share_output: Optional[SanitizeShareOutput] = None,
179
+ size: Optional[int] = None,
180
+ crc32c: Optional[str] = None,
181
+ sha256: Optional[str] = None,
182
+ uploaded_file_name: Optional[str] = None,
183
+ sync_call: bool = True,
184
+ ) -> PangeaResponse[SanitizeResult]:
185
+ """
186
+ Sanitize
187
+
188
+ Apply file sanitization actions according to specified rules.
189
+
190
+ OperationId: sanitize_post_v1_sanitize
191
+
192
+ Args:
193
+ transfer_method: The transfer method used to upload the file data.
194
+ file_path: Path to file to sanitize.
195
+ file: File to sanitize.
196
+ source_url: A URL where the file to be sanitized can be downloaded.
197
+ share_id: A Pangea Secure Share ID where the file to be sanitized is stored.
198
+ file_scan: Options for File Scan.
199
+ content: Options for how the file should be sanitized.
200
+ share_output: Integration with Secure Share.
201
+ size: The size (in bytes) of the file. If the upload doesn't match, the call will fail.
202
+ crc32c: The CRC32C hash of the file data, which will be verified by the server if provided.
203
+ sha256: The hexadecimal-encoded SHA256 hash of the file data, which will be verified by the server if provided.
204
+ uploaded_file_name: Name of the user-uploaded file, required for `TransferMethod.PUT_URL` and `TransferMethod.POST_URL`.
205
+ sync_call: Whether or not to poll on HTTP/202.
206
+
207
+ Raises:
208
+ PangeaAPIException: If an API error happens.
209
+
210
+ Returns:
211
+ The sanitized file and information on the sanitization that was
212
+ performed.
213
+
214
+ Examples:
215
+ with open("/path/to/file.pdf", "rb") as f:
216
+ response = sanitize.sanitize(
217
+ file=f,
218
+ transfer_method=TransferMethod.POST_URL,
219
+ uploaded_file_name="uploaded_file",
220
+ )
221
+ """
222
+
223
+ if transfer_method == TransferMethod.SOURCE_URL and source_url is None:
224
+ raise ValueError("`source_url` argument is required when using `TransferMethod.SOURCE_URL`.")
225
+
226
+ if source_url is not None and transfer_method != TransferMethod.SOURCE_URL:
227
+ raise ValueError(
228
+ "`transfer_method` should be `TransferMethod.SOURCE_URL` when using the `source_url` argument."
229
+ )
230
+
231
+ files: Optional[List[Tuple]] = None
232
+ if file or file_path:
233
+ if file_path:
234
+ file = open(file_path, "rb")
235
+ if (
236
+ transfer_method == TransferMethod.POST_URL
237
+ and file
238
+ and (sha256 is None or crc32c is None or size is None)
239
+ ):
240
+ params = get_file_upload_params(file)
241
+ crc32c = params.crc_hex if crc32c is None else crc32c
242
+ sha256 = params.sha256_hex if sha256 is None else sha256
243
+ size = params.size if size is None else size
244
+ else:
245
+ crc32c, sha256, size = None, None, None
246
+ files = [("upload", ("filename", file, "application/octet-stream"))]
247
+ elif source_url is None:
248
+ raise ValueError("Need to set one of `file_path`, `file`, or `source_url` arguments.")
249
+
250
+ input = SanitizeRequest(
251
+ transfer_method=transfer_method,
252
+ source_url=source_url,
253
+ share_id=share_id,
254
+ file=file_scan,
255
+ content=content,
256
+ share_output=share_output,
257
+ crc32c=crc32c,
258
+ sha256=sha256,
259
+ size=size,
260
+ uploaded_file_name=uploaded_file_name,
261
+ )
262
+ data = input.model_dump(exclude_none=True)
263
+ try:
264
+ response = self.request.post("v1/sanitize", SanitizeResult, data=data, files=files, poll_result=sync_call)
265
+ finally:
266
+ if file_path and file is not None:
267
+ file.close()
268
+ return response
269
+
270
+ def request_upload_url(
271
+ self,
272
+ transfer_method: TransferMethod = TransferMethod.PUT_URL,
273
+ params: Optional[FileUploadParams] = None,
274
+ file_scan: Optional[SanitizeFile] = None,
275
+ content: Optional[SanitizeContent] = None,
276
+ share_output: Optional[SanitizeShareOutput] = None,
277
+ size: Optional[int] = None,
278
+ crc32c: Optional[str] = None,
279
+ sha256: Optional[str] = None,
280
+ uploaded_file_name: Optional[str] = None,
281
+ ) -> PangeaResponse[SanitizeResult]:
282
+ """
283
+ Sanitize via presigned URL
284
+
285
+ Apply file sanitization actions according to specified rules via a
286
+ [presigned URL](https://pangea.cloud/docs/api/transfer-methods).
287
+
288
+ OperationId: sanitize_post_v1_sanitize 2
289
+
290
+ Args:
291
+ transfer_method: The transfer method used to upload the file data.
292
+ params: File upload parameters.
293
+ file_scan: Options for File Scan.
294
+ content: Options for how the file should be sanitized.
295
+ share_output: Integration with Secure Share.
296
+ size: The size (in bytes) of the file. If the upload doesn't match, the call will fail.
297
+ crc32c: The CRC32C hash of the file data, which will be verified by the server if provided.
298
+ sha256: The hexadecimal-encoded SHA256 hash of the file data, which will be verified by the server if provided.
299
+ uploaded_file_name: Name of the user-uploaded file, required for `TransferMethod.PUT_URL` and `TransferMethod.POST_URL`.
300
+
301
+ Raises:
302
+ PangeaAPIException: If an API error happens.
303
+
304
+ Returns:
305
+ A presigned URL.
306
+
307
+ Examples:
308
+ presignedUrl = sanitize.request_upload_url(
309
+ transfer_method=TransferMethod.PUT_URL,
310
+ uploaded_file_name="uploaded_file",
311
+ )
312
+
313
+ # Upload file to `presignedUrl.accepted_result.put_url`.
314
+
315
+ # Poll for Sanitize's result.
316
+ response: PangeaResponse[SanitizeResult] = sanitize.poll_result(response=presignedUrl)
317
+ """
318
+
319
+ input = SanitizeRequest(
320
+ transfer_method=transfer_method,
321
+ file=file_scan,
322
+ content=content,
323
+ share_output=share_output,
324
+ crc32c=crc32c,
325
+ sha256=sha256,
326
+ size=size,
327
+ uploaded_file_name=uploaded_file_name,
328
+ )
329
+ if params is not None and (transfer_method == TransferMethod.POST_URL):
330
+ input.crc32c = params.crc_hex
331
+ input.sha256 = params.sha256_hex
332
+ input.size = params.size
333
+
334
+ data = input.model_dump(exclude_none=True)
335
+ return self.request.request_presigned_url("v1/sanitize", SanitizeResult, data=data)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pangea-sdk
3
- Version: 4.2.0
3
+ Version: 4.3.0
4
4
  Summary: Pangea API SDK
5
5
  Home-page: https://pangea.cloud/docs/sdk/python/
6
6
  License: MIT
@@ -17,12 +17,11 @@ Classifier: Programming Language :: Python :: 3.11
17
17
  Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Topic :: Software Development
19
19
  Classifier: Topic :: Software Development :: Libraries
20
- Requires-Dist: aiohttp (>=3.9.3,<4.0.0)
21
- Requires-Dist: asyncio (>=3.4.3,<4.0.0)
22
- Requires-Dist: cryptography (>=42.0.8,<43.0.0)
20
+ Requires-Dist: aiohttp (>=3.10.3,<4.0.0)
21
+ Requires-Dist: cryptography (>=43.0.1,<44.0.0)
23
22
  Requires-Dist: deprecated (>=1.2.14,<2.0.0)
24
23
  Requires-Dist: google-crc32c (>=1.5.0,<2.0.0)
25
- Requires-Dist: pydantic (>=2.8.2,<3.0.0)
24
+ Requires-Dist: pydantic (>=2.9.2,<3.0.0)
26
25
  Requires-Dist: python-dateutil (>=2.9.0,<3.0.0)
27
26
  Requires-Dist: requests (>=2.31.0,<3.0.0)
28
27
  Requires-Dist: requests-toolbelt (>=1.0.0,<2.0.0)
@@ -1,14 +1,17 @@
1
- pangea/__init__.py,sha256=LU-w4iaLZ2cv9Z05TnO50JrxjnAYBIF410gDqtx3Hgw,200
2
- pangea/asyncio/request.py,sha256=ysCT-SB1nkAr64O6JkI64xZt-Za1TQGt8Jp9gfqeLxE,17077
3
- pangea/asyncio/services/__init__.py,sha256=hmySN2LoXYpHzSKLVSdVjMbpGXi5Z5iNx-fJ2Fc8W6I,320
1
+ pangea/__init__.py,sha256=jRQKKZlq7PuMhIGECYLfchmOlGy0e03sZhjL-MrXrO0,246
2
+ pangea/asyncio/__init__.py,sha256=kjEMkqMQ521LlMSu5jn3_WgweyArwVZ2C-s3x7mR6Pk,45
3
+ pangea/asyncio/file_uploader.py,sha256=wI7epib7Rc5jtZw4eJ1L1SlmutDG6CPv59C8N2UPhtY,1436
4
+ pangea/asyncio/request.py,sha256=PkyVJQdE9kEKNQsOzuDZWIDbDzmj4oXebr6mgcV5S_o,17099
5
+ pangea/asyncio/services/__init__.py,sha256=hMeTMnksGimg-fS_XMpRgh02a7BNcLYCt56tnChYeC4,356
4
6
  pangea/asyncio/services/audit.py,sha256=bZ7gdkVWkzqLqUVc1Wnf3oDAaCLg97-zTWhY8UdX0_Y,26549
5
7
  pangea/asyncio/services/authn.py,sha256=rPeLJweL8mYH_t4ebcQn4n_Wglr3kClKNnCXNCimZU4,46622
6
- pangea/asyncio/services/authz.py,sha256=DcPn5FpdWMunim6Qtsk2vFLXyUbFb3lgl1XpX3w0hew,9176
8
+ pangea/asyncio/services/authz.py,sha256=gBdzlNwckDx5ozYPRagOymZJxX1B614fxyQwySW2MCU,9496
7
9
  pangea/asyncio/services/base.py,sha256=4FtKtlq74NmE9myrgIt9HMA6JDnP4mPZ6krafWr286o,2663
8
10
  pangea/asyncio/services/embargo.py,sha256=ctzj3kip6xos-Eu3JuOskrCGYC8T3JlsgAopZHiPSXM,3068
9
- pangea/asyncio/services/file_scan.py,sha256=MSNyRdyEfNiYglfxvEohOYO0kyo-FuSJRVxvgeYQ4l4,6287
11
+ pangea/asyncio/services/file_scan.py,sha256=PLG1O-PL4Yk9uY9D6NbMrZ5LHg70Z311s7bFe46UMZA,7108
10
12
  pangea/asyncio/services/intel.py,sha256=ph-Kor2CyKWtVaYVx3OD7pRjb4MGCHrZnrgqMkfMu4I,37977
11
13
  pangea/asyncio/services/redact.py,sha256=jRNtXr_DZ_cY7guhut-eZmOEhy2uN_VCXrjGH6bkh74,7265
14
+ pangea/asyncio/services/sanitize.py,sha256=bf98J-s-P51oSKqNBgR0wj5mlHOCBwpjWz7k0NdXCKQ,7899
12
15
  pangea/asyncio/services/vault.py,sha256=gFch7dVFZzjcTryn68AR4Xbj37U4A_LxfCMrX2mhtSk,53271
13
16
  pangea/audit_logger.py,sha256=gRkCfUUT5LDNaycwxkhZUySgY47jDfn1ZeKOul4XCQI,3842
14
17
  pangea/config.py,sha256=mQUu8GX_6weIuv3vjNdG5plppXskXYASmxMWtFQh-hc,1662
@@ -17,23 +20,25 @@ pangea/deep_verify.py,sha256=mocaGbC6XLbMTVWxTpMv4oJtXGPWpT-SbFqT3obpiZs,8443
17
20
  pangea/deprecated.py,sha256=IjFYEVvY1E0ld0SMkEYC1o62MAleX3nnT1If2dFVbHo,608
18
21
  pangea/dump_audit.py,sha256=1Je8D2fXwU4PWcZ-ZD4icfO3DNFvWqJkwsac4qFEhOo,7025
19
22
  pangea/exceptions.py,sha256=OBtzUECpNa6vNp8ySkHC-tm4QjFRCOAHBkMHqzAlOu8,5656
23
+ pangea/file_uploader.py,sha256=4RQ44xt-faApC61nn2PlwHT7XYrJ4GeQA8Ug4tySEAg,1227
20
24
  pangea/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
- pangea/request.py,sha256=ADMbnSlU-vrQQTlDp6w0b352ZDA_t4srZAihjaciQ5E,24285
22
- pangea/response.py,sha256=w-jTsCiNxDVt_qP27DQXbtqxPucSOAAYVlI7CnA4aiU,6597
23
- pangea/services/__init__.py,sha256=iAIa1kk_C0EHBsSn2XP3QT-bOZNGwMBPUcO2JoI-9cE,278
25
+ pangea/request.py,sha256=fKMuf2tuAguK2TnSSMpmdcBdHlgsYFQoYCBVh3mUkBY,24259
26
+ pangea/response.py,sha256=rjQuTAHLSAB0m2uAsdbt11xUVL5cCGT4uTRzQhUz9hE,7321
27
+ pangea/services/__init__.py,sha256=YPXqGGUlAm2oHQNArhDVfr6Y1Gq2o4nhwx_dpdKe74c,309
24
28
  pangea/services/audit/audit.py,sha256=IFv7jANA8S2SypQVS47x94_Cr5Z9zSsL9Dp9eXw9RHk,39593
25
29
  pangea/services/audit/exceptions.py,sha256=bhVuYe4ammacOVxwg98CChxvwZf5FKgR2DcgqILOcwc,471
26
30
  pangea/services/audit/models.py,sha256=1h1B9eSYQMYG3f8WNi1UcDX2-impRrET_ErjJYUnj7M,14678
27
- pangea/services/audit/signing.py,sha256=pOjw60BIYDcg3_5YKDCMWZUaapsEZpCHaFhyFu7TEgc,5567
31
+ pangea/services/audit/signing.py,sha256=EYuZN6pcFOjDJBG6S65jesE_8xOz5SNms6qHZ1qambQ,5541
28
32
  pangea/services/audit/util.py,sha256=Zq1qvfeplYfhCP_ud5YMvntSB0UvnCdsuYbOzZkHbjg,7620
29
33
  pangea/services/authn/authn.py,sha256=cZKl2Ixc6HwHnkRecpSaAGTQUgaZUtxfLa0T3S03HMs,45478
30
34
  pangea/services/authn/models.py,sha256=HH5su6jx3O9AwVGzASXZ99-eIWjgXEP5LhIVdewM13s,22394
31
- pangea/services/authz.py,sha256=kgJ4iWytqm2PqLB2n2AQy1lHR8TI8tdYBuOYAfY0fY0,11818
35
+ pangea/services/authz.py,sha256=lZVf2CE5kQkxWBqribQKM86lnfJbnvh5xaFRopGvmWc,12236
32
36
  pangea/services/base.py,sha256=lwhHoe5Juy28Ir3Mfj2lHdM58gxZRaxa2SRFi4_DBRw,3453
33
37
  pangea/services/embargo.py,sha256=9Wfku4td5ORaIENKmnGmS5jxJJIRfWp6Q51L36Jsy0I,3897
34
- pangea/services/file_scan.py,sha256=lWXiD4lHpagQ7O-nJeqwRQJKyP4BtauUdU19HZ3zcQM,6954
38
+ pangea/services/file_scan.py,sha256=QiO80uKqB_BnAOiYQKznXfxpa5j40qqETE3-zBRT_QE,7813
35
39
  pangea/services/intel.py,sha256=H7mo5r5pUDuYFphG4eWfI86twsOMAM3Qlf6dsAZ8UKM,51793
36
40
  pangea/services/redact.py,sha256=ZYXkzEoriLJyCqaj5dqmgsC56mIz4T3pPToZ7TcNfhg,11465
41
+ pangea/services/sanitize.py,sha256=FEMSjulya6Z6TqPVlNE80DMU7nCs-_3lgGVB2YZXc90,11940
37
42
  pangea/services/vault/models/asymmetric.py,sha256=xr8oZnjzExMYcbzPJRG3xPXSmhumKDKn7RO90RvdrwU,1526
38
43
  pangea/services/vault/models/common.py,sha256=FOmi2UN5cEgSsrM-aDT1KWLK4TTgrtMukqNczrnWH6w,15491
39
44
  pangea/services/vault/models/secret.py,sha256=cLgEj-_BeGkB4-pmSeTkWVyasFbaJwcEltIEcOyf1U8,481
@@ -42,6 +47,6 @@ pangea/services/vault/vault.py,sha256=pv52dpZM0yicdtNra0Yd4AdkWUZC91Yk4rATthu1bs
42
47
  pangea/tools.py,sha256=sa2pSz-L8tB6GcZg6lghsmm8w0qMQAIkzqcv7dilU6Q,6429
43
48
  pangea/utils.py,sha256=pMSwL8B-owtrjeWYRjxuyaTQN4V-HsCT669KtOLU3Sw,3195
44
49
  pangea/verify_audit.py,sha256=rvni5akz_P2kYLAGAeA1A5gY6XGpXpAQpbIa7V1PoRY,17458
45
- pangea_sdk-4.2.0.dist-info/METADATA,sha256=SwojBSSbANVBCo4FOmE0qWZqYjW46tI6yNIFNrz3HlM,7532
46
- pangea_sdk-4.2.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
47
- pangea_sdk-4.2.0.dist-info/RECORD,,
50
+ pangea_sdk-4.3.0.dist-info/METADATA,sha256=Bv7469TC0tPNL2Knf-YMxANUIimaDiTvSn3U1hdpvcA,7493
51
+ pangea_sdk-4.3.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
52
+ pangea_sdk-4.3.0.dist-info/RECORD,,