pangea-sdk 3.8.0__py3-none-any.whl → 5.3.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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)