pangea-sdk 3.8.0__py3-none-any.whl → 5.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 +2 -1
- pangea/asyncio/__init__.py +1 -0
- pangea/asyncio/file_uploader.py +39 -0
- pangea/asyncio/request.py +46 -23
- pangea/asyncio/services/__init__.py +2 -0
- pangea/asyncio/services/audit.py +46 -20
- pangea/asyncio/services/authn.py +123 -61
- pangea/asyncio/services/authz.py +57 -31
- pangea/asyncio/services/base.py +21 -2
- pangea/asyncio/services/embargo.py +2 -2
- pangea/asyncio/services/file_scan.py +24 -9
- pangea/asyncio/services/intel.py +104 -30
- pangea/asyncio/services/redact.py +52 -3
- pangea/asyncio/services/sanitize.py +217 -0
- pangea/asyncio/services/share.py +733 -0
- pangea/asyncio/services/vault.py +1709 -766
- pangea/crypto/rsa.py +135 -0
- pangea/deep_verify.py +7 -1
- pangea/dump_audit.py +9 -8
- pangea/file_uploader.py +35 -0
- pangea/request.py +70 -49
- pangea/response.py +36 -17
- pangea/services/__init__.py +2 -0
- pangea/services/audit/audit.py +57 -29
- pangea/services/audit/models.py +12 -3
- pangea/services/audit/signing.py +6 -5
- pangea/services/audit/util.py +3 -3
- pangea/services/authn/authn.py +120 -66
- pangea/services/authn/models.py +167 -11
- pangea/services/authz.py +53 -30
- pangea/services/base.py +16 -2
- pangea/services/embargo.py +2 -2
- pangea/services/file_scan.py +32 -15
- pangea/services/intel.py +155 -30
- pangea/services/redact.py +132 -3
- pangea/services/sanitize.py +388 -0
- pangea/services/share/file_format.py +170 -0
- pangea/services/share/share.py +1440 -0
- pangea/services/vault/models/asymmetric.py +120 -18
- pangea/services/vault/models/common.py +439 -141
- pangea/services/vault/models/keys.py +94 -0
- pangea/services/vault/models/secret.py +27 -3
- pangea/services/vault/models/symmetric.py +68 -22
- pangea/services/vault/vault.py +1690 -766
- pangea/tools.py +6 -7
- pangea/utils.py +94 -33
- pangea/verify_audit.py +270 -83
- {pangea_sdk-3.8.0.dist-info → pangea_sdk-5.3.0.dist-info}/METADATA +21 -29
- pangea_sdk-5.3.0.dist-info/RECORD +56 -0
- {pangea_sdk-3.8.0.dist-info → pangea_sdk-5.3.0.dist-info}/WHEEL +1 -1
- pangea_sdk-3.8.0.dist-info/RECORD +0 -46
pangea/asyncio/services/intel.py
CHANGED
@@ -67,7 +67,9 @@ class FileIntelAsync(ServiceBaseAsync):
|
|
67
67
|
|
68
68
|
"""
|
69
69
|
input = m.FileReputationRequest(hash=hash, hash_type=hash_type, verbose=verbose, raw=raw, provider=provider)
|
70
|
-
return await self.request.post(
|
70
|
+
return await self.request.post(
|
71
|
+
"v1/reputation", m.FileReputationResult, data=input.model_dump(exclude_none=True)
|
72
|
+
)
|
71
73
|
|
72
74
|
async def hash_reputation_bulk(
|
73
75
|
self,
|
@@ -103,7 +105,9 @@ class FileIntelAsync(ServiceBaseAsync):
|
|
103
105
|
input = m.FileReputationBulkRequest( # type: ignore[call-arg]
|
104
106
|
hashes=hashes, hash_type=hash_type, verbose=verbose, raw=raw, provider=provider
|
105
107
|
)
|
106
|
-
return await self.request.post(
|
108
|
+
return await self.request.post(
|
109
|
+
"v2/reputation", m.FileReputationBulkResult, data=input.model_dump(exclude_none=True)
|
110
|
+
)
|
107
111
|
|
108
112
|
async def filepath_reputation(
|
109
113
|
self,
|
@@ -144,7 +148,9 @@ class FileIntelAsync(ServiceBaseAsync):
|
|
144
148
|
hash = hashlib.sha256(data.read()).hexdigest()
|
145
149
|
|
146
150
|
input = m.FileReputationRequest(hash=hash, hash_type="sha256", verbose=verbose, raw=raw, provider=provider)
|
147
|
-
return await self.request.post(
|
151
|
+
return await self.request.post(
|
152
|
+
"v1/reputation", m.FileReputationResult, data=input.model_dump(exclude_none=True)
|
153
|
+
)
|
148
154
|
|
149
155
|
async def filepath_reputation_bulk(
|
150
156
|
self,
|
@@ -243,7 +249,9 @@ class DomainIntelAsync(ServiceBaseAsync):
|
|
243
249
|
)
|
244
250
|
"""
|
245
251
|
input = m.DomainReputationRequest(domain=domain, verbose=verbose, provider=provider, raw=raw)
|
246
|
-
return await self.request.post(
|
252
|
+
return await self.request.post(
|
253
|
+
"v1/reputation", m.DomainReputationResult, data=input.model_dump(exclude_none=True)
|
254
|
+
)
|
247
255
|
|
248
256
|
async def reputation_bulk(
|
249
257
|
self,
|
@@ -277,7 +285,7 @@ class DomainIntelAsync(ServiceBaseAsync):
|
|
277
285
|
"""
|
278
286
|
input = m.DomainReputationBulkRequest(domains=domains, verbose=verbose, provider=provider, raw=raw)
|
279
287
|
return await self.request.post(
|
280
|
-
"v2/reputation", m.DomainReputationBulkResult, data=input.
|
288
|
+
"v2/reputation", m.DomainReputationBulkResult, data=input.model_dump(exclude_none=True)
|
281
289
|
)
|
282
290
|
|
283
291
|
async def who_is(
|
@@ -310,7 +318,7 @@ class DomainIntelAsync(ServiceBaseAsync):
|
|
310
318
|
)
|
311
319
|
"""
|
312
320
|
input = m.DomainWhoIsRequest(domain=domain, verbose=verbose, provider=provider, raw=raw) # type: ignore[call-arg]
|
313
|
-
return await self.request.post("v1/whois", m.DomainWhoIsResult, data=input.
|
321
|
+
return await self.request.post("v1/whois", m.DomainWhoIsResult, data=input.model_dump(exclude_none=True))
|
314
322
|
|
315
323
|
|
316
324
|
class IpIntelAsync(ServiceBaseAsync):
|
@@ -353,7 +361,7 @@ class IpIntelAsync(ServiceBaseAsync):
|
|
353
361
|
ip (str): The IP to be looked up
|
354
362
|
verbose (bool, optional): Echo the API parameters in the response
|
355
363
|
raw (bool, optional): Include raw data from this provider
|
356
|
-
provider (str, optional): Use reputation data from this provider
|
364
|
+
provider (str, optional): Use reputation data from this provider
|
357
365
|
|
358
366
|
Raises:
|
359
367
|
PangeaAPIException: If an API Error happens
|
@@ -369,7 +377,7 @@ class IpIntelAsync(ServiceBaseAsync):
|
|
369
377
|
)
|
370
378
|
"""
|
371
379
|
input = m.IPReputationRequest(ip=ip, verbose=verbose, raw=raw, provider=provider)
|
372
|
-
return await self.request.post("v1/reputation", m.IPReputationResult, data=input.
|
380
|
+
return await self.request.post("v1/reputation", m.IPReputationResult, data=input.model_dump(exclude_none=True))
|
373
381
|
|
374
382
|
async def reputation_bulk(
|
375
383
|
self, ips: List[str], verbose: Optional[bool] = None, raw: Optional[bool] = None, provider: Optional[str] = None
|
@@ -385,7 +393,7 @@ class IpIntelAsync(ServiceBaseAsync):
|
|
385
393
|
ips (List[str]): The IP list to be looked up
|
386
394
|
verbose (bool, optional): Echo the API parameters in the response
|
387
395
|
raw (bool, optional): Include raw data from this provider
|
388
|
-
provider (str, optional): Use reputation data from this provider
|
396
|
+
provider (str, optional): Use reputation data from this provider
|
389
397
|
|
390
398
|
Raises:
|
391
399
|
PangeaAPIException: If an API Error happens
|
@@ -398,7 +406,9 @@ class IpIntelAsync(ServiceBaseAsync):
|
|
398
406
|
FIXME:
|
399
407
|
"""
|
400
408
|
input = m.IPReputationBulkRequest(ips=ips, verbose=verbose, raw=raw, provider=provider)
|
401
|
-
return await self.request.post(
|
409
|
+
return await self.request.post(
|
410
|
+
"v2/reputation", m.IPReputationBulkResult, data=input.model_dump(exclude_none=True)
|
411
|
+
)
|
402
412
|
|
403
413
|
async def geolocate(
|
404
414
|
self, ip: str, verbose: Optional[bool] = None, raw: Optional[bool] = None, provider: Optional[str] = None
|
@@ -430,7 +440,7 @@ class IpIntelAsync(ServiceBaseAsync):
|
|
430
440
|
)
|
431
441
|
"""
|
432
442
|
input = m.IPGeolocateRequest(ip=ip, verbose=verbose, raw=raw, provider=provider)
|
433
|
-
return await self.request.post("v1/geolocate", m.IPGeolocateResult, data=input.
|
443
|
+
return await self.request.post("v1/geolocate", m.IPGeolocateResult, data=input.model_dump(exclude_none=True))
|
434
444
|
|
435
445
|
async def geolocate_bulk(
|
436
446
|
self, ips: List[str], verbose: Optional[bool] = None, raw: Optional[bool] = None, provider: Optional[str] = None
|
@@ -459,7 +469,9 @@ class IpIntelAsync(ServiceBaseAsync):
|
|
459
469
|
FIXME:
|
460
470
|
"""
|
461
471
|
input = m.IPGeolocateBulkRequest(ips=ips, verbose=verbose, raw=raw, provider=provider)
|
462
|
-
return await self.request.post(
|
472
|
+
return await self.request.post(
|
473
|
+
"v2/geolocate", m.IPGeolocateBulkResult, data=input.model_dump(exclude_none=True)
|
474
|
+
)
|
463
475
|
|
464
476
|
async def get_domain(
|
465
477
|
self, ip: str, verbose: Optional[bool] = None, raw: Optional[bool] = None, provider: Optional[str] = None
|
@@ -491,7 +503,7 @@ class IpIntelAsync(ServiceBaseAsync):
|
|
491
503
|
)
|
492
504
|
"""
|
493
505
|
input = m.IPDomainRequest(ip=ip, verbose=verbose, raw=raw, provider=provider)
|
494
|
-
return await self.request.post("v1/domain", m.IPDomainResult, data=input.
|
506
|
+
return await self.request.post("v1/domain", m.IPDomainResult, data=input.model_dump(exclude_none=True))
|
495
507
|
|
496
508
|
async def get_domain_bulk(
|
497
509
|
self, ips: List[str], verbose: Optional[bool] = None, raw: Optional[bool] = None, provider: Optional[str] = None
|
@@ -520,7 +532,7 @@ class IpIntelAsync(ServiceBaseAsync):
|
|
520
532
|
FIXME:
|
521
533
|
"""
|
522
534
|
input = m.IPDomainBulkRequest(ips=ips, verbose=verbose, raw=raw, provider=provider)
|
523
|
-
return await self.request.post("v2/domain", m.IPDomainBulkResult, data=input.
|
535
|
+
return await self.request.post("v2/domain", m.IPDomainBulkResult, data=input.model_dump(exclude_none=True))
|
524
536
|
|
525
537
|
async def is_vpn(
|
526
538
|
self, ip: str, verbose: Optional[bool] = None, raw: Optional[bool] = None, provider: Optional[str] = None
|
@@ -552,7 +564,7 @@ class IpIntelAsync(ServiceBaseAsync):
|
|
552
564
|
)
|
553
565
|
"""
|
554
566
|
input = m.IPVPNRequest(ip=ip, verbose=verbose, raw=raw, provider=provider)
|
555
|
-
return await self.request.post("v1/vpn", m.IPVPNResult, data=input.
|
567
|
+
return await self.request.post("v1/vpn", m.IPVPNResult, data=input.model_dump(exclude_none=True))
|
556
568
|
|
557
569
|
async def is_vpn_bulk(
|
558
570
|
self, ips: List[str], verbose: Optional[bool] = None, raw: Optional[bool] = None, provider: Optional[str] = None
|
@@ -581,7 +593,7 @@ class IpIntelAsync(ServiceBaseAsync):
|
|
581
593
|
FIXME:
|
582
594
|
"""
|
583
595
|
input = m.IPVPNBulkRequest(ips=ips, verbose=verbose, raw=raw, provider=provider)
|
584
|
-
return await self.request.post("v2/vpn", m.IPVPNBulkResult, data=input.
|
596
|
+
return await self.request.post("v2/vpn", m.IPVPNBulkResult, data=input.model_dump(exclude_none=True))
|
585
597
|
|
586
598
|
async def is_proxy(
|
587
599
|
self, ip: str, verbose: Optional[bool] = None, raw: Optional[bool] = None, provider: Optional[str] = None
|
@@ -613,7 +625,7 @@ class IpIntelAsync(ServiceBaseAsync):
|
|
613
625
|
)
|
614
626
|
"""
|
615
627
|
input = m.IPProxyRequest(ip=ip, verbose=verbose, raw=raw, provider=provider)
|
616
|
-
return await self.request.post("v1/proxy", m.IPProxyResult, data=input.
|
628
|
+
return await self.request.post("v1/proxy", m.IPProxyResult, data=input.model_dump(exclude_none=True))
|
617
629
|
|
618
630
|
async def is_proxy_bulk(
|
619
631
|
self, ips: List[str], verbose: Optional[bool] = None, raw: Optional[bool] = None, provider: Optional[str] = None
|
@@ -642,7 +654,7 @@ class IpIntelAsync(ServiceBaseAsync):
|
|
642
654
|
FIXME:
|
643
655
|
"""
|
644
656
|
input = m.IPProxyBulkRequest(ips=ips, verbose=verbose, raw=raw, provider=provider)
|
645
|
-
return await self.request.post("v2/proxy", m.IPProxyBulkResult, data=input.
|
657
|
+
return await self.request.post("v2/proxy", m.IPProxyBulkResult, data=input.model_dump(exclude_none=True))
|
646
658
|
|
647
659
|
|
648
660
|
class UrlIntelAsync(ServiceBaseAsync):
|
@@ -685,7 +697,7 @@ class UrlIntelAsync(ServiceBaseAsync):
|
|
685
697
|
url (str): The URL to be looked up
|
686
698
|
verbose (bool, optional): Echo the API parameters in the response
|
687
699
|
raw (bool, optional): Include raw data from this provider
|
688
|
-
provider (str, optional): Use reputation data from this provider
|
700
|
+
provider (str, optional): Use reputation data from this provider
|
689
701
|
|
690
702
|
Raises:
|
691
703
|
PangeaAPIException: If an API Error happens
|
@@ -702,7 +714,7 @@ class UrlIntelAsync(ServiceBaseAsync):
|
|
702
714
|
"""
|
703
715
|
|
704
716
|
input = m.URLReputationRequest(url=url, provider=provider, verbose=verbose, raw=raw)
|
705
|
-
return await self.request.post("v1/reputation", m.URLReputationResult, data=input.
|
717
|
+
return await self.request.post("v1/reputation", m.URLReputationResult, data=input.model_dump(exclude_none=True))
|
706
718
|
|
707
719
|
async def reputation_bulk(
|
708
720
|
self,
|
@@ -722,7 +734,7 @@ class UrlIntelAsync(ServiceBaseAsync):
|
|
722
734
|
urls (List[str]): The URL list to be looked up
|
723
735
|
verbose (bool, optional): Echo the API parameters in the response
|
724
736
|
raw (bool, optional): Include raw data from this provider
|
725
|
-
provider (str, optional): Use reputation data from this provider
|
737
|
+
provider (str, optional): Use reputation data from this provider
|
726
738
|
|
727
739
|
Raises:
|
728
740
|
PangeaAPIException: If an API Error happens
|
@@ -736,7 +748,9 @@ class UrlIntelAsync(ServiceBaseAsync):
|
|
736
748
|
"""
|
737
749
|
|
738
750
|
input = m.URLReputationBulkRequest(urls=urls, provider=provider, verbose=verbose, raw=raw)
|
739
|
-
return await self.request.post(
|
751
|
+
return await self.request.post(
|
752
|
+
"v2/reputation", m.URLReputationBulkResult, data=input.model_dump(exclude_none=True)
|
753
|
+
)
|
740
754
|
|
741
755
|
|
742
756
|
class UserIntelAsync(ServiceBaseAsync):
|
@@ -776,6 +790,7 @@ class UserIntelAsync(ServiceBaseAsync):
|
|
776
790
|
verbose: Optional[bool] = None,
|
777
791
|
raw: Optional[bool] = None,
|
778
792
|
provider: Optional[str] = None,
|
793
|
+
cursor: Optional[str] = None,
|
779
794
|
) -> PangeaResponse[m.UserBreachedResult]:
|
780
795
|
"""
|
781
796
|
Look up breached users
|
@@ -793,7 +808,8 @@ class UserIntelAsync(ServiceBaseAsync):
|
|
793
808
|
end (str): Latest date for search
|
794
809
|
verbose (bool, optional): Echo the API parameters in the response
|
795
810
|
raw (bool, optional): Include raw data from this provider
|
796
|
-
provider (str, optional): Use reputation data from this provider
|
811
|
+
provider (str, optional): Use reputation data from this provider
|
812
|
+
cursor (str, optional): A token given in the raw response from SpyCloud. Post this back to paginate results
|
797
813
|
|
798
814
|
Raises:
|
799
815
|
PangeaAPIException: If an API Error happens
|
@@ -821,8 +837,11 @@ class UserIntelAsync(ServiceBaseAsync):
|
|
821
837
|
end=end,
|
822
838
|
verbose=verbose,
|
823
839
|
raw=raw,
|
840
|
+
cursor=cursor,
|
841
|
+
)
|
842
|
+
return await self.request.post(
|
843
|
+
"v1/user/breached", m.UserBreachedResult, data=input.model_dump(exclude_none=True)
|
824
844
|
)
|
825
|
-
return await self.request.post("v1/user/breached", m.UserBreachedResult, data=input.dict(exclude_none=True))
|
826
845
|
|
827
846
|
async def user_breached_bulk(
|
828
847
|
self,
|
@@ -830,6 +849,7 @@ class UserIntelAsync(ServiceBaseAsync):
|
|
830
849
|
usernames: Optional[List[str]] = None,
|
831
850
|
ips: Optional[List[str]] = None,
|
832
851
|
phone_numbers: Optional[List[str]] = None,
|
852
|
+
domains: Optional[List[str]] = None,
|
833
853
|
start: Optional[str] = None,
|
834
854
|
end: Optional[str] = None,
|
835
855
|
verbose: Optional[bool] = None,
|
@@ -848,11 +868,12 @@ class UserIntelAsync(ServiceBaseAsync):
|
|
848
868
|
usernames (List[str]): An username's list to search for
|
849
869
|
ips (List[str]): An ip's list to search for
|
850
870
|
phone_numbers (List[str]): A phone number's list to search for. minLength: 7, maxLength: 15.
|
871
|
+
domains (List[str]): Search for user under these domains.
|
851
872
|
start (str): Earliest date for search
|
852
873
|
end (str): Latest date for search
|
853
874
|
verbose (bool, optional): Echo the API parameters in the response
|
854
875
|
raw (bool, optional): Include raw data from this provider
|
855
|
-
provider (str, optional): Use reputation data from this provider
|
876
|
+
provider (str, optional): Use reputation data from this provider
|
856
877
|
|
857
878
|
Raises:
|
858
879
|
PangeaAPIException: If an API Error happens
|
@@ -870,13 +891,16 @@ class UserIntelAsync(ServiceBaseAsync):
|
|
870
891
|
phone_numbers=phone_numbers,
|
871
892
|
usernames=usernames,
|
872
893
|
ips=ips,
|
894
|
+
domains=domains,
|
873
895
|
provider=provider,
|
874
896
|
start=start,
|
875
897
|
end=end,
|
876
898
|
verbose=verbose,
|
877
899
|
raw=raw,
|
878
900
|
)
|
879
|
-
return await self.request.post(
|
901
|
+
return await self.request.post(
|
902
|
+
"v2/user/breached", m.UserBreachedBulkResult, data=input.model_dump(exclude_none=True)
|
903
|
+
)
|
880
904
|
|
881
905
|
async def password_breached(
|
882
906
|
self,
|
@@ -898,7 +922,7 @@ class UserIntelAsync(ServiceBaseAsync):
|
|
898
922
|
hash_prefix (str): The prefix of the hash to be looked up.
|
899
923
|
verbose (bool, optional): Echo the API parameters in the response
|
900
924
|
raw (bool, optional): Include raw data from this provider
|
901
|
-
provider (str, optional): Use reputation data from this provider
|
925
|
+
provider (str, optional): Use reputation data from this provider
|
902
926
|
|
903
927
|
Raises:
|
904
928
|
PangeaAPIException: If an API Error happens
|
@@ -919,7 +943,7 @@ class UserIntelAsync(ServiceBaseAsync):
|
|
919
943
|
hash_type=hash_type, hash_prefix=hash_prefix, provider=provider, verbose=verbose, raw=raw
|
920
944
|
)
|
921
945
|
return await self.request.post(
|
922
|
-
"v1/password/breached", m.UserPasswordBreachedResult, data=input.
|
946
|
+
"v1/password/breached", m.UserPasswordBreachedResult, data=input.model_dump(exclude_none=True)
|
923
947
|
)
|
924
948
|
|
925
949
|
async def password_breached_bulk(
|
@@ -942,7 +966,7 @@ class UserIntelAsync(ServiceBaseAsync):
|
|
942
966
|
hash_prefixes (List[str]): The list of prefixes of the hash to be looked up.
|
943
967
|
verbose (bool, optional): Echo the API parameters in the response
|
944
968
|
raw (bool, optional): Include raw data from this provider
|
945
|
-
provider (str, optional): Use reputation data from this provider
|
969
|
+
provider (str, optional): Use reputation data from this provider
|
946
970
|
|
947
971
|
Raises:
|
948
972
|
PangeaAPIException: If an API Error happens
|
@@ -959,5 +983,55 @@ class UserIntelAsync(ServiceBaseAsync):
|
|
959
983
|
hash_type=hash_type, hash_prefixes=hash_prefixes, provider=provider, verbose=verbose, raw=raw
|
960
984
|
)
|
961
985
|
return await self.request.post(
|
962
|
-
"v2/password/breached", m.UserPasswordBreachedBulkResult, data=input.
|
986
|
+
"v2/password/breached", m.UserPasswordBreachedBulkResult, data=input.model_dump(exclude_none=True)
|
987
|
+
)
|
988
|
+
|
989
|
+
async def breach(
|
990
|
+
self,
|
991
|
+
breach_id: str,
|
992
|
+
verbose: Optional[bool] = None,
|
993
|
+
provider: Optional[str] = None,
|
994
|
+
cursor: Optional[str] = None,
|
995
|
+
start: Optional[str] = None,
|
996
|
+
end: Optional[str] = None,
|
997
|
+
severity: Optional[List[int]] = None,
|
998
|
+
) -> PangeaResponse[m.BreachResult]:
|
999
|
+
"""
|
1000
|
+
Look up information about a specific breach
|
1001
|
+
|
1002
|
+
Given a provider specific breach ID, find details about the breach.
|
1003
|
+
|
1004
|
+
OperationId: user_intel_post_v1_breach
|
1005
|
+
|
1006
|
+
Args:
|
1007
|
+
breach_id (str, optional): The ID of a breach returned by a provider
|
1008
|
+
verbose (bool, optional): Echo the API parameters in the response
|
1009
|
+
provider (str, optional): Use reputation data from this provider
|
1010
|
+
cursor (str, optional): A token given in the raw response from SpyCloud. Post this back to paginate results
|
1011
|
+
start (str, optional): This parameter allows you to define the starting point for a date range query on the spycloud_publish_date field
|
1012
|
+
end (str, optional): This parameter allows you to define the ending point for a date range query on the spycloud_publish_date field
|
1013
|
+
severity (List[int], optional): Filter for records that match one of the given severities
|
1014
|
+
|
1015
|
+
Raises:
|
1016
|
+
PangeaAPIException: If an API Error happens
|
1017
|
+
|
1018
|
+
Returns:
|
1019
|
+
A PangeaResponse where the breach details are in the
|
1020
|
+
response.result field. Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/user-intel)
|
1021
|
+
|
1022
|
+
Examples:
|
1023
|
+
response = await user_intel.breach(
|
1024
|
+
breach_id="66111",
|
1025
|
+
)
|
1026
|
+
"""
|
1027
|
+
|
1028
|
+
input = m.BreachRequest(
|
1029
|
+
breach_id=breach_id,
|
1030
|
+
provider=provider,
|
1031
|
+
verbose=verbose,
|
1032
|
+
cursor=cursor,
|
1033
|
+
start=start,
|
1034
|
+
end=end,
|
1035
|
+
severity=severity,
|
963
1036
|
)
|
1037
|
+
return await self.request.post("v1/breach", m.BreachResult, data=input.model_dump(exclude_none=True))
|
@@ -64,6 +64,9 @@ class RedactAsync(ServiceBaseAsync):
|
|
64
64
|
rules: Optional[List[str]] = None,
|
65
65
|
rulesets: Optional[List[str]] = None,
|
66
66
|
return_result: Optional[bool] = None,
|
67
|
+
redaction_method_overrides: Optional[m.RedactionMethodOverrides] = None,
|
68
|
+
llm_request: Optional[bool] = None,
|
69
|
+
vault_parameters: Optional[m.VaultParameters] = None,
|
67
70
|
) -> PangeaResponse[m.RedactResult]:
|
68
71
|
"""
|
69
72
|
Redact
|
@@ -79,6 +82,9 @@ class RedactAsync(ServiceBaseAsync):
|
|
79
82
|
rules (list[str], optional): An array of redact rule short names
|
80
83
|
rulesets (list[str], optional): An array of redact rulesets short names
|
81
84
|
return_result(bool, optional): Setting this value to false will omit the redacted result only returning count
|
85
|
+
redaction_method_overrides: A set of redaction method overrides for any enabled rule. These methods override the config declared methods
|
86
|
+
llm_request: Boolean flag to enable FPE redaction for LLM requests
|
87
|
+
vault_parameters: A set of vault parameters to use for redaction
|
82
88
|
|
83
89
|
Raises:
|
84
90
|
PangeaAPIException: If an API Error happens
|
@@ -92,8 +98,17 @@ class RedactAsync(ServiceBaseAsync):
|
|
92
98
|
response = redact.redact(text="Jenny Jenny... 555-867-5309")
|
93
99
|
"""
|
94
100
|
|
95
|
-
input = m.RedactRequest(
|
96
|
-
|
101
|
+
input = m.RedactRequest(
|
102
|
+
text=text,
|
103
|
+
debug=debug,
|
104
|
+
rules=rules,
|
105
|
+
rulesets=rulesets,
|
106
|
+
return_result=return_result,
|
107
|
+
redaction_method_overrides=redaction_method_overrides,
|
108
|
+
llm_request=llm_request,
|
109
|
+
vault_parameters=vault_parameters,
|
110
|
+
)
|
111
|
+
return await self.request.post("v1/redact", m.RedactResult, data=input.model_dump(exclude_none=True))
|
97
112
|
|
98
113
|
async def redact_structured(
|
99
114
|
self,
|
@@ -104,6 +119,9 @@ class RedactAsync(ServiceBaseAsync):
|
|
104
119
|
rules: Optional[List[str]] = None,
|
105
120
|
rulesets: Optional[List[str]] = None,
|
106
121
|
return_result: Optional[bool] = None,
|
122
|
+
redaction_method_overrides: Optional[m.RedactionMethodOverrides] = None,
|
123
|
+
llm_request: bool | None = None,
|
124
|
+
vault_parameters: m.VaultParameters | None = None,
|
107
125
|
) -> PangeaResponse[m.StructuredResult]:
|
108
126
|
"""
|
109
127
|
Redact structured
|
@@ -123,6 +141,9 @@ class RedactAsync(ServiceBaseAsync):
|
|
123
141
|
rules (list[str], optional): An array of redact rule short names
|
124
142
|
rulesets (list[str], optional): An array of redact rulesets short names
|
125
143
|
return_result(bool, optional): Setting this value to false will omit the redacted result only returning count
|
144
|
+
redaction_method_overrides: A set of redaction method overrides for any enabled rule. These methods override the config declared methods
|
145
|
+
llm_request: Boolean flag to enable FPE redaction for LLM requests
|
146
|
+
vault_parameters: A set of vault parameters to use for redaction
|
126
147
|
|
127
148
|
Raises:
|
128
149
|
PangeaAPIException: If an API Error happens
|
@@ -149,5 +170,33 @@ class RedactAsync(ServiceBaseAsync):
|
|
149
170
|
rules=rules,
|
150
171
|
rulesets=rulesets,
|
151
172
|
return_result=return_result,
|
173
|
+
redaction_method_overrides=redaction_method_overrides,
|
174
|
+
llm_request=llm_request,
|
175
|
+
vault_parameters=vault_parameters,
|
152
176
|
)
|
153
|
-
return await self.request.post(
|
177
|
+
return await self.request.post(
|
178
|
+
"v1/redact_structured", m.StructuredResult, data=input.model_dump(exclude_none=True)
|
179
|
+
)
|
180
|
+
|
181
|
+
async def unredact(self, redacted_data: m.RedactedData, fpe_context: str) -> PangeaResponse[m.UnredactResult]:
|
182
|
+
"""
|
183
|
+
Unredact
|
184
|
+
|
185
|
+
Decrypt or unredact fpe redactions
|
186
|
+
|
187
|
+
OperationId: redact_post_v1_unredact
|
188
|
+
|
189
|
+
Args:
|
190
|
+
redacted_data: Data to unredact
|
191
|
+
fpe_context (base64): FPE context used to decrypt and unredact data
|
192
|
+
|
193
|
+
Raises:
|
194
|
+
PangeaAPIException: If an API Error happens
|
195
|
+
|
196
|
+
Returns:
|
197
|
+
Pangea Response with redacted data in the response.result field,
|
198
|
+
available response fields can be found in our
|
199
|
+
[API Documentation](https://pangea.cloud/docs/api/redact#unredact)
|
200
|
+
"""
|
201
|
+
input = m.UnredactRequest(redacted_data=redacted_data, fpe_context=fpe_context)
|
202
|
+
return await self.request.post("v1/unredact", m.UnredactResult, data=input.model_dump(exclude_none=True))
|
@@ -0,0 +1,217 @@
|
|
1
|
+
# Copyright 2022 Pangea Cyber Corporation
|
2
|
+
# Author: Pangea Cyber Corporation
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
import io
|
6
|
+
from typing import List, Optional, Tuple
|
7
|
+
|
8
|
+
import pangea.services.sanitize as m
|
9
|
+
from pangea.asyncio.services.base import ServiceBaseAsync
|
10
|
+
from pangea.config import PangeaConfig
|
11
|
+
from pangea.response import PangeaResponse, TransferMethod
|
12
|
+
from pangea.utils import FileUploadParams, get_file_upload_params
|
13
|
+
|
14
|
+
|
15
|
+
class SanitizeAsync(ServiceBaseAsync):
|
16
|
+
"""Sanitize service client.
|
17
|
+
|
18
|
+
Examples:
|
19
|
+
import os
|
20
|
+
|
21
|
+
# Pangea SDK
|
22
|
+
from pangea.asyncio.services import SanitizeAsync
|
23
|
+
from pangea.config import PangeaConfig
|
24
|
+
|
25
|
+
PANGEA_SANITIZE_TOKEN = os.getenv("PANGEA_SANITIZE_TOKEN")
|
26
|
+
config = PangeaConfig(domain="pangea.cloud")
|
27
|
+
|
28
|
+
sanitize = SanitizeAsync(token=PANGEA_SANITIZE_TOKEN, config=config)
|
29
|
+
"""
|
30
|
+
|
31
|
+
service_name = "sanitize"
|
32
|
+
|
33
|
+
def __init__(
|
34
|
+
self, token: str, config: PangeaConfig | None = None, logger_name: str = "pangea", config_id: str | None = None
|
35
|
+
) -> None:
|
36
|
+
"""
|
37
|
+
Sanitize client
|
38
|
+
|
39
|
+
Initializes a new Sanitize client.
|
40
|
+
|
41
|
+
Args:
|
42
|
+
token: Pangea API token.
|
43
|
+
config: Configuration.
|
44
|
+
logger_name: Logger name.
|
45
|
+
config_id: Configuration ID.
|
46
|
+
|
47
|
+
Examples:
|
48
|
+
config = PangeaConfig(domain="aws.us.pangea.cloud")
|
49
|
+
authz = SanitizeAsync(token="pangea_token", config=config)
|
50
|
+
"""
|
51
|
+
|
52
|
+
super().__init__(token, config, logger_name, config_id=config_id)
|
53
|
+
|
54
|
+
async def sanitize(
|
55
|
+
self,
|
56
|
+
transfer_method: TransferMethod = TransferMethod.POST_URL,
|
57
|
+
file_path: Optional[str] = None,
|
58
|
+
file: Optional[io.BufferedReader] = None,
|
59
|
+
source_url: Optional[str] = None,
|
60
|
+
share_id: Optional[str] = None,
|
61
|
+
file_scan: Optional[m.SanitizeFile] = None,
|
62
|
+
content: Optional[m.SanitizeContent] = None,
|
63
|
+
share_output: Optional[m.SanitizeShareOutput] = None,
|
64
|
+
size: Optional[int] = None,
|
65
|
+
crc32c: Optional[str] = None,
|
66
|
+
sha256: Optional[str] = None,
|
67
|
+
uploaded_file_name: Optional[str] = None,
|
68
|
+
sync_call: bool = True,
|
69
|
+
) -> PangeaResponse[m.SanitizeResult]:
|
70
|
+
"""
|
71
|
+
Sanitize
|
72
|
+
|
73
|
+
Apply file sanitization actions according to specified rules.
|
74
|
+
|
75
|
+
OperationId: sanitize_post_v1_sanitize
|
76
|
+
|
77
|
+
Args:
|
78
|
+
transfer_method: The transfer method used to upload the file data.
|
79
|
+
file_path: Path to file to sanitize.
|
80
|
+
file: File to sanitize.
|
81
|
+
source_url: A URL where the file to be sanitized can be downloaded.
|
82
|
+
share_id: A Pangea Secure Share ID where the file to be sanitized is stored.
|
83
|
+
file_scan: Options for File Scan.
|
84
|
+
content: Options for how the file should be sanitized.
|
85
|
+
share_output: Integration with Secure Share.
|
86
|
+
size: The size (in bytes) of the file. If the upload doesn't match, the call will fail.
|
87
|
+
crc32c: The CRC32C hash of the file data, which will be verified by the server if provided.
|
88
|
+
sha256: The hexadecimal-encoded SHA256 hash of the file data, which will be verified by the server if provided.
|
89
|
+
uploaded_file_name: Name of the user-uploaded file, required for `TransferMethod.PUT_URL` and `TransferMethod.POST_URL`.
|
90
|
+
sync_call: Whether or not to poll on HTTP/202.
|
91
|
+
|
92
|
+
Raises:
|
93
|
+
PangeaAPIException: If an API error happens.
|
94
|
+
|
95
|
+
Returns:
|
96
|
+
The sanitized file and information on the sanitization that was
|
97
|
+
performed.
|
98
|
+
|
99
|
+
Examples:
|
100
|
+
with open("/path/to/file.pdf", "rb") as f:
|
101
|
+
response = await sanitize.sanitize(
|
102
|
+
file=f,
|
103
|
+
transfer_method=TransferMethod.POST_URL,
|
104
|
+
uploaded_file_name="uploaded_file",
|
105
|
+
)
|
106
|
+
"""
|
107
|
+
|
108
|
+
if transfer_method == TransferMethod.SOURCE_URL and source_url is None:
|
109
|
+
raise ValueError("`source_url` argument is required when using `TransferMethod.SOURCE_URL`.")
|
110
|
+
|
111
|
+
if source_url is not None and transfer_method != TransferMethod.SOURCE_URL:
|
112
|
+
raise ValueError(
|
113
|
+
"`transfer_method` should be `TransferMethod.SOURCE_URL` when using the `source_url` argument."
|
114
|
+
)
|
115
|
+
|
116
|
+
files: Optional[List[Tuple]] = None
|
117
|
+
if file or file_path:
|
118
|
+
if file_path:
|
119
|
+
file = open(file_path, "rb")
|
120
|
+
if transfer_method == TransferMethod.POST_URL and (sha256 is None or crc32c is None or size is None):
|
121
|
+
params = get_file_upload_params(file) # type: ignore[arg-type]
|
122
|
+
crc32c = params.crc_hex if crc32c is None else crc32c
|
123
|
+
sha256 = params.sha256_hex if sha256 is None else sha256
|
124
|
+
size = params.size if size is None else size
|
125
|
+
else:
|
126
|
+
crc32c, sha256, size = None, None, None
|
127
|
+
files = [("upload", ("filename", file, "application/octet-stream"))]
|
128
|
+
elif source_url is None:
|
129
|
+
raise ValueError("Need to set one of `file_path`, `file`, or `source_url` arguments.")
|
130
|
+
|
131
|
+
input = m.SanitizeRequest(
|
132
|
+
transfer_method=transfer_method,
|
133
|
+
source_url=source_url,
|
134
|
+
share_id=share_id,
|
135
|
+
file=file_scan,
|
136
|
+
content=content,
|
137
|
+
share_output=share_output,
|
138
|
+
crc32c=crc32c,
|
139
|
+
sha256=sha256,
|
140
|
+
size=size,
|
141
|
+
uploaded_file_name=uploaded_file_name,
|
142
|
+
)
|
143
|
+
data = input.model_dump(exclude_none=True)
|
144
|
+
try:
|
145
|
+
return await self.request.post(
|
146
|
+
"v1/sanitize", m.SanitizeResult, data=data, files=files, poll_result=sync_call
|
147
|
+
)
|
148
|
+
finally:
|
149
|
+
if file_path and file is not None:
|
150
|
+
file.close()
|
151
|
+
|
152
|
+
async def request_upload_url(
|
153
|
+
self,
|
154
|
+
transfer_method: TransferMethod = TransferMethod.PUT_URL,
|
155
|
+
params: Optional[FileUploadParams] = None,
|
156
|
+
file_scan: Optional[m.SanitizeFile] = None,
|
157
|
+
content: Optional[m.SanitizeContent] = None,
|
158
|
+
share_output: Optional[m.SanitizeShareOutput] = None,
|
159
|
+
size: Optional[int] = None,
|
160
|
+
crc32c: Optional[str] = None,
|
161
|
+
sha256: Optional[str] = None,
|
162
|
+
uploaded_file_name: Optional[str] = None,
|
163
|
+
) -> PangeaResponse[m.SanitizeResult]:
|
164
|
+
"""
|
165
|
+
Sanitize via presigned URL
|
166
|
+
|
167
|
+
Apply file sanitization actions according to specified rules via a
|
168
|
+
[presigned URL](https://pangea.cloud/docs/api/transfer-methods).
|
169
|
+
|
170
|
+
OperationId: sanitize_post_v1_sanitize 2
|
171
|
+
|
172
|
+
Args:
|
173
|
+
transfer_method: The transfer method used to upload the file data.
|
174
|
+
params: File upload parameters.
|
175
|
+
file_scan: Options for File Scan.
|
176
|
+
content: Options for how the file should be sanitized.
|
177
|
+
share_output: Integration with Secure Share.
|
178
|
+
size: The size (in bytes) of the file. If the upload doesn't match, the call will fail.
|
179
|
+
crc32c: The CRC32C hash of the file data, which will be verified by the server if provided.
|
180
|
+
sha256: The hexadecimal-encoded SHA256 hash of the file data, which will be verified by the server if provided.
|
181
|
+
uploaded_file_name: Name of the user-uploaded file, required for `TransferMethod.PUT_URL` and `TransferMethod.POST_URL`.
|
182
|
+
|
183
|
+
Raises:
|
184
|
+
PangeaAPIException: If an API error happens.
|
185
|
+
|
186
|
+
Returns:
|
187
|
+
A presigned URL.
|
188
|
+
|
189
|
+
Examples:
|
190
|
+
presignedUrl = await sanitize.request_upload_url(
|
191
|
+
transfer_method=TransferMethod.PUT_URL,
|
192
|
+
uploaded_file_name="uploaded_file",
|
193
|
+
)
|
194
|
+
|
195
|
+
# Upload file to `presignedUrl.accepted_result.put_url`.
|
196
|
+
|
197
|
+
# Poll for Sanitize's result.
|
198
|
+
response: PangeaResponse[SanitizeResult] = await sanitize.poll_result(response=presignedUrl)
|
199
|
+
"""
|
200
|
+
|
201
|
+
input = m.SanitizeRequest(
|
202
|
+
transfer_method=transfer_method,
|
203
|
+
file=file_scan,
|
204
|
+
content=content,
|
205
|
+
share_output=share_output,
|
206
|
+
crc32c=crc32c,
|
207
|
+
sha256=sha256,
|
208
|
+
size=size,
|
209
|
+
uploaded_file_name=uploaded_file_name,
|
210
|
+
)
|
211
|
+
if params is not None and (transfer_method == TransferMethod.POST_URL):
|
212
|
+
input.crc32c = params.crc_hex
|
213
|
+
input.sha256 = params.sha256_hex
|
214
|
+
input.size = params.size
|
215
|
+
|
216
|
+
data = input.model_dump(exclude_none=True)
|
217
|
+
return await self.request.request_presigned_url("v1/sanitize", m.SanitizeResult, data=data)
|