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.
Files changed (51) hide show
  1. pangea/__init__.py +2 -1
  2. pangea/asyncio/__init__.py +1 -0
  3. pangea/asyncio/file_uploader.py +39 -0
  4. pangea/asyncio/request.py +46 -23
  5. pangea/asyncio/services/__init__.py +2 -0
  6. pangea/asyncio/services/audit.py +46 -20
  7. pangea/asyncio/services/authn.py +123 -61
  8. pangea/asyncio/services/authz.py +57 -31
  9. pangea/asyncio/services/base.py +21 -2
  10. pangea/asyncio/services/embargo.py +2 -2
  11. pangea/asyncio/services/file_scan.py +24 -9
  12. pangea/asyncio/services/intel.py +104 -30
  13. pangea/asyncio/services/redact.py +52 -3
  14. pangea/asyncio/services/sanitize.py +217 -0
  15. pangea/asyncio/services/share.py +733 -0
  16. pangea/asyncio/services/vault.py +1709 -766
  17. pangea/crypto/rsa.py +135 -0
  18. pangea/deep_verify.py +7 -1
  19. pangea/dump_audit.py +9 -8
  20. pangea/file_uploader.py +35 -0
  21. pangea/request.py +70 -49
  22. pangea/response.py +36 -17
  23. pangea/services/__init__.py +2 -0
  24. pangea/services/audit/audit.py +57 -29
  25. pangea/services/audit/models.py +12 -3
  26. pangea/services/audit/signing.py +6 -5
  27. pangea/services/audit/util.py +3 -3
  28. pangea/services/authn/authn.py +120 -66
  29. pangea/services/authn/models.py +167 -11
  30. pangea/services/authz.py +53 -30
  31. pangea/services/base.py +16 -2
  32. pangea/services/embargo.py +2 -2
  33. pangea/services/file_scan.py +32 -15
  34. pangea/services/intel.py +155 -30
  35. pangea/services/redact.py +132 -3
  36. pangea/services/sanitize.py +388 -0
  37. pangea/services/share/file_format.py +170 -0
  38. pangea/services/share/share.py +1440 -0
  39. pangea/services/vault/models/asymmetric.py +120 -18
  40. pangea/services/vault/models/common.py +439 -141
  41. pangea/services/vault/models/keys.py +94 -0
  42. pangea/services/vault/models/secret.py +27 -3
  43. pangea/services/vault/models/symmetric.py +68 -22
  44. pangea/services/vault/vault.py +1690 -766
  45. pangea/tools.py +6 -7
  46. pangea/utils.py +94 -33
  47. pangea/verify_audit.py +270 -83
  48. {pangea_sdk-3.8.0.dist-info → pangea_sdk-5.3.0.dist-info}/METADATA +21 -29
  49. pangea_sdk-5.3.0.dist-info/RECORD +56 -0
  50. {pangea_sdk-3.8.0.dist-info → pangea_sdk-5.3.0.dist-info}/WHEEL +1 -1
  51. pangea_sdk-3.8.0.dist-info/RECORD +0 -46
@@ -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("v1/reputation", m.FileReputationResult, data=input.dict(exclude_none=True))
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("v2/reputation", m.FileReputationBulkResult, data=input.dict(exclude_none=True))
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("v1/reputation", m.FileReputationResult, data=input.dict(exclude_none=True))
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("v1/reputation", m.DomainReputationResult, data=input.dict(exclude_none=True))
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.dict(exclude_none=True)
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.dict(exclude_none=True))
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: "crowdstrike"
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.dict(exclude_none=True))
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: "crowdstrike"
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("v2/reputation", m.IPReputationBulkResult, data=input.dict(exclude_none=True))
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.dict(exclude_none=True))
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("v2/geolocate", m.IPGeolocateBulkResult, data=input.dict(exclude_none=True))
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.dict(exclude_none=True))
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.dict(exclude_none=True))
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.dict(exclude_none=True))
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.dict(exclude_none=True))
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.dict(exclude_none=True))
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.dict(exclude_none=True))
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: "crowdstrike"
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.dict(exclude_none=True))
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: "crowdstrike"
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("v2/reputation", m.URLReputationBulkResult, data=input.dict(exclude_none=True))
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: "crowdstrike"
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: "crowdstrike"
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("v2/user/breached", m.UserBreachedBulkResult, data=input.dict(exclude_none=True))
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: "crowdstrike"
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.dict(exclude_none=True)
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: "crowdstrike"
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.dict(exclude_none=True)
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(text=text, debug=debug, rules=rules, rulesets=rulesets, return_result=return_result)
96
- return await self.request.post("v1/redact", m.RedactResult, data=input.dict(exclude_none=True))
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("v1/redact_structured", m.StructuredResult, data=input.dict(exclude_none=True))
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)