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
pangea/services/intel.py CHANGED
@@ -1,5 +1,7 @@
1
1
  # Copyright 2022 Pangea Cyber Corporation
2
2
  # Author: Pangea Cyber Corporation
3
+ from __future__ import annotations
4
+
3
5
  import enum
4
6
  import hashlib
5
7
  from typing import Dict, List, Optional
@@ -524,7 +526,7 @@ class FileIntel(ServiceBase):
524
526
  )
525
527
  """
526
528
  input = FileReputationRequest(hash=hash, hash_type=hash_type, verbose=verbose, raw=raw, provider=provider)
527
- return self.request.post("v1/reputation", FileReputationResult, data=input.dict(exclude_none=True))
529
+ return self.request.post("v1/reputation", FileReputationResult, data=input.model_dump(exclude_none=True))
528
530
 
529
531
  def hash_reputation_bulk(
530
532
  self,
@@ -563,7 +565,7 @@ class FileIntel(ServiceBase):
563
565
  input = FileReputationBulkRequest( # type: ignore[call-arg]
564
566
  hashes=hashes, hash_type=hash_type, verbose=verbose, raw=raw, provider=provider
565
567
  )
566
- return self.request.post("v2/reputation", FileReputationBulkResult, data=input.dict(exclude_none=True))
568
+ return self.request.post("v2/reputation", FileReputationBulkResult, data=input.model_dump(exclude_none=True))
567
569
 
568
570
  def filepath_reputation(
569
571
  self,
@@ -708,7 +710,7 @@ class DomainIntel(ServiceBase):
708
710
  )
709
711
  """
710
712
  input = DomainReputationRequest(domain=domain, verbose=verbose, provider=provider, raw=raw)
711
- return self.request.post("v1/reputation", DomainReputationResult, data=input.dict(exclude_none=True))
713
+ return self.request.post("v1/reputation", DomainReputationResult, data=input.model_dump(exclude_none=True))
712
714
 
713
715
  def reputation_bulk(
714
716
  self,
@@ -744,7 +746,7 @@ class DomainIntel(ServiceBase):
744
746
  )
745
747
  """
746
748
  input = DomainReputationBulkRequest(domains=domains, verbose=verbose, provider=provider, raw=raw)
747
- return self.request.post("v2/reputation", DomainReputationBulkResult, data=input.dict(exclude_none=True))
749
+ return self.request.post("v2/reputation", DomainReputationBulkResult, data=input.model_dump(exclude_none=True))
748
750
 
749
751
  def who_is(
750
752
  self, domain: str, verbose: Optional[bool] = None, raw: Optional[bool] = None, provider: Optional[str] = None
@@ -776,7 +778,7 @@ class DomainIntel(ServiceBase):
776
778
  )
777
779
  """
778
780
  input = DomainWhoIsRequest(domain=domain, verbose=verbose, provider=provider, raw=raw) # type: ignore[call-arg]
779
- return self.request.post("v1/whois", DomainWhoIsResult, data=input.dict(exclude_none=True))
781
+ return self.request.post("v1/whois", DomainWhoIsResult, data=input.model_dump(exclude_none=True))
780
782
 
781
783
 
782
784
  class IpIntel(ServiceBase):
@@ -819,7 +821,7 @@ class IpIntel(ServiceBase):
819
821
  ip (str): The IP to be looked up
820
822
  verbose (bool, optional): Echo the API parameters in the response
821
823
  raw (bool, optional): Include raw data from this provider
822
- provider (str, optional): Use reputation data from this provider: "crowdstrike"
824
+ provider (str, optional): Use reputation data from this provider
823
825
 
824
826
  Raises:
825
827
  PangeaAPIException: If an API Error happens
@@ -835,7 +837,7 @@ class IpIntel(ServiceBase):
835
837
  )
836
838
  """
837
839
  input = IPReputationRequest(ip=ip, verbose=verbose, raw=raw, provider=provider)
838
- return self.request.post("v1/reputation", IPReputationResult, data=input.dict(exclude_none=True))
840
+ return self.request.post("v1/reputation", IPReputationResult, data=input.model_dump(exclude_none=True))
839
841
 
840
842
  def reputation_bulk(
841
843
  self, ips: List[str], verbose: Optional[bool] = None, raw: Optional[bool] = None, provider: Optional[str] = None
@@ -851,7 +853,7 @@ class IpIntel(ServiceBase):
851
853
  ips (List[str]): The IP list to be looked up
852
854
  verbose (bool, optional): Echo the API parameters in the response
853
855
  raw (bool, optional): Include raw data from this provider
854
- provider (str, optional): Use reputation data from this provider: "crowdstrike"
856
+ provider (str, optional): Use reputation data from this provider
855
857
 
856
858
  Raises:
857
859
  PangeaAPIException: If an API Error happens
@@ -867,7 +869,7 @@ class IpIntel(ServiceBase):
867
869
  )
868
870
  """
869
871
  input = IPReputationBulkRequest(ips=ips, verbose=verbose, raw=raw, provider=provider)
870
- return self.request.post("v2/reputation", IPReputationBulkResult, data=input.dict(exclude_none=True))
872
+ return self.request.post("v2/reputation", IPReputationBulkResult, data=input.model_dump(exclude_none=True))
871
873
 
872
874
  def geolocate(
873
875
  self, ip: str, verbose: Optional[bool] = None, raw: Optional[bool] = None, provider: Optional[str] = None
@@ -899,7 +901,7 @@ class IpIntel(ServiceBase):
899
901
  )
900
902
  """
901
903
  input = IPGeolocateRequest(ip=ip, verbose=verbose, raw=raw, provider=provider)
902
- return self.request.post("v1/geolocate", IPGeolocateResult, data=input.dict(exclude_none=True))
904
+ return self.request.post("v1/geolocate", IPGeolocateResult, data=input.model_dump(exclude_none=True))
903
905
 
904
906
  def geolocate_bulk(
905
907
  self, ips: List[str], verbose: Optional[bool] = None, raw: Optional[bool] = None, provider: Optional[str] = None
@@ -931,7 +933,7 @@ class IpIntel(ServiceBase):
931
933
  )
932
934
  """
933
935
  input = IPGeolocateBulkRequest(ips=ips, verbose=verbose, raw=raw, provider=provider)
934
- return self.request.post("v2/geolocate", IPGeolocateBulkResult, data=input.dict(exclude_none=True))
936
+ return self.request.post("v2/geolocate", IPGeolocateBulkResult, data=input.model_dump(exclude_none=True))
935
937
 
936
938
  def get_domain(
937
939
  self, ip: str, verbose: Optional[bool] = None, raw: Optional[bool] = None, provider: Optional[str] = None
@@ -963,7 +965,7 @@ class IpIntel(ServiceBase):
963
965
  )
964
966
  """
965
967
  input = IPDomainRequest(ip=ip, verbose=verbose, raw=raw, provider=provider)
966
- return self.request.post("v1/domain", IPDomainResult, data=input.dict(exclude_none=True))
968
+ return self.request.post("v1/domain", IPDomainResult, data=input.model_dump(exclude_none=True))
967
969
 
968
970
  def get_domain_bulk(
969
971
  self, ips: List[str], verbose: Optional[bool] = None, raw: Optional[bool] = None, provider: Optional[str] = None
@@ -995,7 +997,7 @@ class IpIntel(ServiceBase):
995
997
  )
996
998
  """
997
999
  input = IPDomainBulkRequest(ips=ips, verbose=verbose, raw=raw, provider=provider)
998
- return self.request.post("v2/domain", IPDomainBulkResult, data=input.dict(exclude_none=True))
1000
+ return self.request.post("v2/domain", IPDomainBulkResult, data=input.model_dump(exclude_none=True))
999
1001
 
1000
1002
  def is_vpn(
1001
1003
  self, ip: str, verbose: Optional[bool] = None, raw: Optional[bool] = None, provider: Optional[str] = None
@@ -1027,7 +1029,7 @@ class IpIntel(ServiceBase):
1027
1029
  )
1028
1030
  """
1029
1031
  input = IPVPNRequest(ip=ip, verbose=verbose, raw=raw, provider=provider)
1030
- return self.request.post("v1/vpn", IPVPNResult, data=input.dict(exclude_none=True))
1032
+ return self.request.post("v1/vpn", IPVPNResult, data=input.model_dump(exclude_none=True))
1031
1033
 
1032
1034
  def is_vpn_bulk(
1033
1035
  self, ips: List[str], verbose: Optional[bool] = None, raw: Optional[bool] = None, provider: Optional[str] = None
@@ -1059,7 +1061,7 @@ class IpIntel(ServiceBase):
1059
1061
  )
1060
1062
  """
1061
1063
  input = IPVPNBulkRequest(ips=ips, verbose=verbose, raw=raw, provider=provider)
1062
- return self.request.post("v2/vpn", IPVPNBulkResult, data=input.dict(exclude_none=True))
1064
+ return self.request.post("v2/vpn", IPVPNBulkResult, data=input.model_dump(exclude_none=True))
1063
1065
 
1064
1066
  def is_proxy(
1065
1067
  self, ip: str, verbose: Optional[bool] = None, raw: Optional[bool] = None, provider: Optional[str] = None
@@ -1091,7 +1093,7 @@ class IpIntel(ServiceBase):
1091
1093
  )
1092
1094
  """
1093
1095
  input = IPProxyRequest(ip=ip, verbose=verbose, raw=raw, provider=provider)
1094
- return self.request.post("v1/proxy", IPProxyResult, data=input.dict(exclude_none=True))
1096
+ return self.request.post("v1/proxy", IPProxyResult, data=input.model_dump(exclude_none=True))
1095
1097
 
1096
1098
  def is_proxy_bulk(
1097
1099
  self, ips: List[str], verbose: Optional[bool] = None, raw: Optional[bool] = None, provider: Optional[str] = None
@@ -1123,7 +1125,7 @@ class IpIntel(ServiceBase):
1123
1125
  )
1124
1126
  """
1125
1127
  input = IPProxyBulkRequest(ips=ips, verbose=verbose, raw=raw, provider=provider)
1126
- return self.request.post("v2/proxy", IPProxyBulkResult, data=input.dict(exclude_none=True))
1128
+ return self.request.post("v2/proxy", IPProxyBulkResult, data=input.model_dump(exclude_none=True))
1127
1129
 
1128
1130
 
1129
1131
  class UrlIntel(ServiceBase):
@@ -1170,7 +1172,7 @@ class UrlIntel(ServiceBase):
1170
1172
  url (str): The URL to be looked up
1171
1173
  verbose (bool, optional): Echo the API parameters in the response
1172
1174
  raw (bool, optional): Include raw data from this provider
1173
- provider (str, optional): Use reputation data from this provider: "crowdstrike"
1175
+ provider (str, optional): Use reputation data from this provider
1174
1176
 
1175
1177
  Raises:
1176
1178
  PangeaAPIException: If an API Error happens
@@ -1187,7 +1189,7 @@ class UrlIntel(ServiceBase):
1187
1189
  """
1188
1190
 
1189
1191
  input = URLReputationRequest(url=url, provider=provider, verbose=verbose, raw=raw)
1190
- return self.request.post("v1/reputation", URLReputationResult, data=input.dict(exclude_none=True))
1192
+ return self.request.post("v1/reputation", URLReputationResult, data=input.model_dump(exclude_none=True))
1191
1193
 
1192
1194
  def reputation_bulk(
1193
1195
  self,
@@ -1207,7 +1209,7 @@ class UrlIntel(ServiceBase):
1207
1209
  urls (List[str]): The URL list to be looked up
1208
1210
  verbose (bool, optional): Echo the API parameters in the response
1209
1211
  raw (bool, optional): Include raw data from this provider
1210
- provider (str, optional): Use reputation data from this provider: "crowdstrike"
1212
+ provider (str, optional): Use reputation data from this provider
1211
1213
 
1212
1214
  Raises:
1213
1215
  PangeaAPIException: If an API Error happens
@@ -1224,7 +1226,7 @@ class UrlIntel(ServiceBase):
1224
1226
  """
1225
1227
 
1226
1228
  input = URLReputationBulkRequest(urls=urls, provider=provider, verbose=verbose, raw=raw)
1227
- return self.request.post("v2/reputation", URLReputationBulkResult, data=input.dict(exclude_none=True))
1229
+ return self.request.post("v2/reputation", URLReputationBulkResult, data=input.model_dump(exclude_none=True))
1228
1230
 
1229
1231
 
1230
1232
  class UserBreachedRequest(IntelCommonRequest):
@@ -1237,6 +1239,7 @@ class UserBreachedRequest(IntelCommonRequest):
1237
1239
  phone_number (str): A phone number to search for. minLength: 7, maxLength: 15.
1238
1240
  start (str): Earliest date for search
1239
1241
  end (str): Latest date for search
1242
+ cursor (str, optional): A token given in the raw response from SpyCloud. Post this back to paginate results
1240
1243
  """
1241
1244
 
1242
1245
  email: Optional[str] = None
@@ -1245,6 +1248,10 @@ class UserBreachedRequest(IntelCommonRequest):
1245
1248
  phone_number: Optional[str] = None
1246
1249
  start: Optional[str] = None
1247
1250
  end: Optional[str] = None
1251
+ cursor: Optional[str] = None
1252
+
1253
+ severity: Optional[List[int]] = None
1254
+ """Filter for records that match one of the given severities"""
1248
1255
 
1249
1256
 
1250
1257
  class UserBreachedBulkRequest(IntelCommonRequest):
@@ -1255,6 +1262,7 @@ class UserBreachedBulkRequest(IntelCommonRequest):
1255
1262
  usernames (List[str]): An username' list to search for
1256
1263
  ips (List[str]): An ip's list to search for
1257
1264
  phone_numbers (List[str]): A phone number's list to search for. minLength: 7, maxLength: 15.
1265
+ domains (List[str]): Search for user under these domains.
1258
1266
  start (str): Earliest date for search
1259
1267
  end (str): Latest date for search
1260
1268
  """
@@ -1263,9 +1271,13 @@ class UserBreachedBulkRequest(IntelCommonRequest):
1263
1271
  usernames: Optional[List[str]] = None
1264
1272
  ips: Optional[List[str]] = None
1265
1273
  phone_numbers: Optional[List[str]] = None
1274
+ domains: Optional[List[str]] = None
1266
1275
  start: Optional[str] = None
1267
1276
  end: Optional[str] = None
1268
1277
 
1278
+ severity: Optional[List[int]] = None
1279
+ """Filter for records that match one of the given severities"""
1280
+
1269
1281
 
1270
1282
  class UserBreachedCommonData(PangeaResponseResult):
1271
1283
  """
@@ -1314,7 +1326,7 @@ class UserPasswordBreachedRequest(IntelCommonRequest):
1314
1326
 
1315
1327
  class UserPasswordBreachedBulkRequest(IntelCommonRequest):
1316
1328
  """
1317
- User password breached common request data
1329
+ User password breached bulk request data
1318
1330
 
1319
1331
  hash_type (str): Hash type to be looked up
1320
1332
  hash_prefixes (List[str]): The list of prefixes of the hashes to be looked up.
@@ -1348,6 +1360,44 @@ class UserPasswordBreachedBulkResult(IntelCommonResult):
1348
1360
  data: Dict[str, UserPasswordBreachedData]
1349
1361
 
1350
1362
 
1363
+ class BreachRequest(APIRequestModel):
1364
+ """Breach request data"""
1365
+
1366
+ breach_id: Optional[str] = None
1367
+ """The ID of a breach returned by a provider."""
1368
+
1369
+ verbose: Optional[bool] = None
1370
+ """Echo back the parameters of the API in the response."""
1371
+
1372
+ provider: Optional[str] = None
1373
+ """Provider of the information. Default provider defined by the configuration."""
1374
+
1375
+ severity: Optional[List[int]] = None
1376
+ """Filter for records that match one of the given severities"""
1377
+
1378
+ start: Optional[str] = None
1379
+ """This parameter allows you to define the starting point for a date range query on the spycloud_publish_date field."""
1380
+
1381
+ end: Optional[str] = None
1382
+ """This parameter allows you to define the ending point for a date range query on the spycloud_publish_date field."""
1383
+
1384
+ cursor: Optional[str] = None
1385
+ """A token given in the raw response from SpyCloud. Post this back to paginate results"""
1386
+
1387
+
1388
+ class BreachResult(PangeaResponseResult):
1389
+ """Breach result"""
1390
+
1391
+ found: bool
1392
+ """A flag indicating if the lookup was successful."""
1393
+
1394
+ data: Optional[Dict] = None
1395
+ """Breach details given by the provider."""
1396
+
1397
+ parameters: Optional[Dict] = None
1398
+ """The parameters, which were passed in the request, echoed back."""
1399
+
1400
+
1351
1401
  class UserIntel(ServiceBase):
1352
1402
  """User Intel service client.
1353
1403
 
@@ -1385,6 +1435,8 @@ class UserIntel(ServiceBase):
1385
1435
  verbose: Optional[bool] = None,
1386
1436
  raw: Optional[bool] = None,
1387
1437
  provider: Optional[str] = None,
1438
+ cursor: Optional[str] = None,
1439
+ severity: Optional[List[int]] = None,
1388
1440
  ) -> PangeaResponse[UserBreachedResult]:
1389
1441
  """
1390
1442
  Look up breached users
@@ -1402,7 +1454,9 @@ class UserIntel(ServiceBase):
1402
1454
  end (str): Latest date for search
1403
1455
  verbose (bool, optional): Echo the API parameters in the response
1404
1456
  raw (bool, optional): Include raw data from this provider
1405
- provider (str, optional): Use reputation data from this provider: "spycloud"
1457
+ provider (str, optional): Use reputation data from this provider
1458
+ cursor (str, optional): A token given in the raw response from SpyCloud. Post this back to paginate results
1459
+ severity (List[int], optional): Filter for records that match one of the given severities
1406
1460
 
1407
1461
  Raises:
1408
1462
  PangeaAPIException: If an API Error happens
@@ -1430,8 +1484,10 @@ class UserIntel(ServiceBase):
1430
1484
  end=end,
1431
1485
  verbose=verbose,
1432
1486
  raw=raw,
1487
+ cursor=cursor,
1488
+ severity=severity,
1433
1489
  )
1434
- return self.request.post("v1/user/breached", UserBreachedResult, data=input.dict(exclude_none=True))
1490
+ return self.request.post("v1/user/breached", UserBreachedResult, data=input.model_dump(exclude_none=True))
1435
1491
 
1436
1492
  def user_breached_bulk(
1437
1493
  self,
@@ -1439,11 +1495,13 @@ class UserIntel(ServiceBase):
1439
1495
  usernames: Optional[List[str]] = None,
1440
1496
  ips: Optional[List[str]] = None,
1441
1497
  phone_numbers: Optional[List[str]] = None,
1498
+ domains: Optional[List[str]] = None,
1442
1499
  start: Optional[str] = None,
1443
1500
  end: Optional[str] = None,
1444
1501
  verbose: Optional[bool] = None,
1445
1502
  raw: Optional[bool] = None,
1446
1503
  provider: Optional[str] = None,
1504
+ severity: Optional[List[int]] = None,
1447
1505
  ) -> PangeaResponse[UserBreachedBulkResult]:
1448
1506
  """
1449
1507
  Look up breached users V2
@@ -1457,11 +1515,13 @@ class UserIntel(ServiceBase):
1457
1515
  usernames (List[str]): A list of usernames to search for
1458
1516
  ips (List[str]): A list of ips to search for
1459
1517
  phone_numbers (List[str]): A list of phone numbers to search for. minLength: 7, maxLength: 15.
1518
+ domains (List[str]): Search for user under these domains.
1460
1519
  start (str): Earliest date for search
1461
1520
  end (str): Latest date for search
1462
1521
  verbose (bool, optional): Echo the API parameters in the response
1463
1522
  raw (bool, optional): Include raw data from this provider
1464
- provider (str, optional): Use reputation data from this provider: "spycloud"
1523
+ provider (str, optional): Use reputation data from this provider
1524
+ severity (List[int], optional): Filter for records that match one of the given severities
1465
1525
 
1466
1526
  Raises:
1467
1527
  PangeaAPIException: If an API Error happens
@@ -1484,13 +1544,15 @@ class UserIntel(ServiceBase):
1484
1544
  phone_numbers=phone_numbers,
1485
1545
  usernames=usernames,
1486
1546
  ips=ips,
1547
+ domains=domains,
1487
1548
  provider=provider,
1488
1549
  start=start,
1489
1550
  end=end,
1490
1551
  verbose=verbose,
1491
1552
  raw=raw,
1553
+ severity=severity,
1492
1554
  )
1493
- return self.request.post("v2/user/breached", UserBreachedBulkResult, data=input.dict(exclude_none=True))
1555
+ return self.request.post("v2/user/breached", UserBreachedBulkResult, data=input.model_dump(exclude_none=True))
1494
1556
 
1495
1557
  def password_breached(
1496
1558
  self,
@@ -1512,7 +1574,7 @@ class UserIntel(ServiceBase):
1512
1574
  hash_prefix (str): The prefix of the hash to be looked up.
1513
1575
  verbose (bool, optional): Echo the API parameters in the response
1514
1576
  raw (bool, optional): Include raw data from this provider
1515
- provider (str, optional): Use reputation data from this provider: "crowdstrike"
1577
+ provider (str, optional): Use reputation data from this provider
1516
1578
 
1517
1579
  Raises:
1518
1580
  PangeaAPIException: If an API Error happens
@@ -1532,7 +1594,9 @@ class UserIntel(ServiceBase):
1532
1594
  input = UserPasswordBreachedRequest(
1533
1595
  hash_type=hash_type, hash_prefix=hash_prefix, provider=provider, verbose=verbose, raw=raw
1534
1596
  )
1535
- return self.request.post("v1/password/breached", UserPasswordBreachedResult, data=input.dict(exclude_none=True))
1597
+ return self.request.post(
1598
+ "v1/password/breached", UserPasswordBreachedResult, data=input.model_dump(exclude_none=True)
1599
+ )
1536
1600
 
1537
1601
  def password_breached_bulk(
1538
1602
  self,
@@ -1554,7 +1618,7 @@ class UserIntel(ServiceBase):
1554
1618
  hash_prefixes (List[str]): The list of prefixes of the hashes to be looked up.
1555
1619
  verbose (bool, optional): Echo the API parameters in the response
1556
1620
  raw (bool, optional): Include raw data from this provider
1557
- provider (str, optional): Use reputation data from this provider: "crowdstrike"
1621
+ provider (str, optional): Use reputation data from this provider
1558
1622
 
1559
1623
  Raises:
1560
1624
  PangeaAPIException: If an API Error happens
@@ -1575,9 +1639,59 @@ class UserIntel(ServiceBase):
1575
1639
  hash_type=hash_type, hash_prefixes=hash_prefixes, provider=provider, verbose=verbose, raw=raw
1576
1640
  )
1577
1641
  return self.request.post(
1578
- "v2/password/breached", UserPasswordBreachedBulkResult, data=input.dict(exclude_none=True)
1642
+ "v2/password/breached", UserPasswordBreachedBulkResult, data=input.model_dump(exclude_none=True)
1579
1643
  )
1580
1644
 
1645
+ def breach(
1646
+ self,
1647
+ breach_id: Optional[str] = None,
1648
+ verbose: Optional[bool] = None,
1649
+ provider: Optional[str] = None,
1650
+ cursor: Optional[str] = None,
1651
+ start: Optional[str] = None,
1652
+ end: Optional[str] = None,
1653
+ severity: Optional[List[int]] = None,
1654
+ ) -> PangeaResponse[BreachResult]:
1655
+ """
1656
+ Look up information about a specific breach
1657
+
1658
+ Given a provider specific breach ID, find details about the breach.
1659
+
1660
+ OperationId: user_intel_post_v1_breach
1661
+
1662
+ Args:
1663
+ breach_id (str, optional): The ID of a breach returned by a provider
1664
+ verbose (bool, optional): Echo the API parameters in the response
1665
+ provider (str, optional): Use reputation data from this provider
1666
+ cursor (str, optional): A token given in the raw response from SpyCloud. Post this back to paginate results
1667
+ start (str, optional): This parameter allows you to define the starting point for a date range query on the spycloud_publish_date field
1668
+ end (str, optional): This parameter allows you to define the ending point for a date range query on the spycloud_publish_date field
1669
+ severity (List[int], optional): Filter for records that match one of the given severities
1670
+
1671
+ Raises:
1672
+ PangeaAPIException: If an API Error happens
1673
+
1674
+ Returns:
1675
+ A PangeaResponse where the breach details are in the
1676
+ response.result field. Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/user-intel)
1677
+
1678
+ Examples:
1679
+ response = user_intel.breach(
1680
+ breach_id="66111",
1681
+ )
1682
+ """
1683
+
1684
+ input = BreachRequest(
1685
+ breach_id=breach_id,
1686
+ provider=provider,
1687
+ verbose=verbose,
1688
+ cursor=cursor,
1689
+ start=start,
1690
+ end=end,
1691
+ severity=severity,
1692
+ )
1693
+ return self.request.post("v1/breach", BreachResult, data=input.model_dump(exclude_none=True))
1694
+
1581
1695
  class PasswordStatus(enum.Enum):
1582
1696
  BREACHED = 0
1583
1697
  UNBREACHED = 1
@@ -1585,6 +1699,17 @@ class UserIntel(ServiceBase):
1585
1699
 
1586
1700
  @staticmethod
1587
1701
  def is_password_breached(response: PangeaResponse[UserBreachedResult], hash: str) -> PasswordStatus:
1702
+ """
1703
+ Check if a password was breached
1704
+
1705
+ Helper function that simplifies searching the response's raw data for
1706
+ the full hash.
1707
+
1708
+ Args:
1709
+ response: API response from an earlier request
1710
+ hash: Password hash
1711
+ """
1712
+
1588
1713
  if response.result.raw_data is None: # type: ignore[union-attr]
1589
1714
  raise PangeaException("Need raw data to check if hash is breached. Send request with raw=true")
1590
1715
 
pangea/services/redact.py CHANGED
@@ -17,6 +17,44 @@ class RedactFormat(str, enum.Enum):
17
17
  """JSON format."""
18
18
 
19
19
 
20
+ class RedactType(str, enum.Enum):
21
+ MASK = "mask"
22
+ PARTIAL_MASKING = "partial_masking"
23
+ REPLACEMENT = "replacement"
24
+ DETECT_ONLY = "detect_only"
25
+ HASH = "hash"
26
+ FPE = "fpe"
27
+
28
+
29
+ class FPEAlphabet(str, enum.Enum):
30
+ NUMERIC = "numeric"
31
+ ALPHANUMERICLOWER = "alphanumericlower"
32
+ ALPHANUMERIC = "alphanumeric"
33
+
34
+
35
+ class MaskingType(str, enum.Enum):
36
+ MASK = "mask"
37
+ UNMASK = "unmask"
38
+
39
+
40
+ class PartialMasking(APIRequestModel):
41
+ masking_type: Optional[MaskingType] = None
42
+ unmasked_from_left: Optional[int] = None
43
+ unmasked_from_right: Optional[int] = None
44
+ masked_from_left: Optional[int] = None
45
+ masked_from_right: Optional[int] = None
46
+ chars_to_ignore: Optional[List[str]] = None
47
+ masking_char: Optional[List[str]] = None
48
+
49
+
50
+ class RedactionMethodOverrides(APIRequestModel):
51
+ redaction_type: RedactType
52
+ hash: Optional[Dict] = None
53
+ fpe_alphabet: Optional[FPEAlphabet] = None
54
+ partial_masking: Optional[PartialMasking] = None
55
+ redaction_value: Optional[str] = None
56
+
57
+
20
58
  class RedactRequest(APIRequestModel):
21
59
  """
22
60
  Input class to make a redact request
@@ -27,6 +65,18 @@ class RedactRequest(APIRequestModel):
27
65
  rules: Optional[List[str]] = None
28
66
  rulesets: Optional[List[str]] = None
29
67
  return_result: Optional[bool] = None
68
+ redaction_method_overrides: Optional[RedactionMethodOverrides] = None
69
+ vault_parameters: Optional[VaultParameters] = None
70
+ llm_request: Optional[bool] = None
71
+ """Is this redact call going to be used in an LLM request?"""
72
+
73
+
74
+ class VaultParameters(APIRequestModel):
75
+ fpe_key_id: Optional[str] = None
76
+ """A vault key ID of an exportable key used to redact with FPE instead of using the service config default."""
77
+
78
+ salt_secret_id: Optional[str] = None
79
+ """A vault secret ID of a secret used to salt a hash instead of using the service config default."""
30
80
 
31
81
 
32
82
  class RecognizerResult(APIResponseModel):
@@ -69,11 +119,13 @@ class RedactResult(PangeaResponseResult):
69
119
  redact_text: Redacted text result
70
120
  count: Number of redactions present in the text
71
121
  report: Describes the decision process for redactions
122
+ fpe_context: FPE context used to encrypt and redact data
72
123
  """
73
124
 
74
125
  redacted_text: Optional[str] = None
75
126
  count: int
76
127
  report: Optional[DebugReport] = None
128
+ fpe_context: Optional[str] = None
77
129
 
78
130
 
79
131
  class StructuredRequest(APIRequestModel):
@@ -94,6 +146,10 @@ class StructuredRequest(APIRequestModel):
94
146
  rules: Optional[List[str]] = None
95
147
  rulesets: Optional[List[str]] = None
96
148
  return_result: Optional[bool] = None
149
+ redaction_method_overrides: Optional[RedactionMethodOverrides] = None
150
+ vault_parameters: Optional[VaultParameters] = None
151
+ llm_request: Optional[bool] = None
152
+ """Is this redact call going to be used in an LLM request?"""
97
153
 
98
154
 
99
155
  class StructuredResult(PangeaResponseResult):
@@ -107,6 +163,32 @@ class StructuredResult(PangeaResponseResult):
107
163
  report: Optional[DebugReport] = None
108
164
 
109
165
 
166
+ class UnredactRequest(APIRequestModel):
167
+ """
168
+ Class input to unredact data request
169
+
170
+ Arguments:
171
+ redacted_data: Data to unredact
172
+ fpe_context (base64): FPE context used to decrypt and unredact data
173
+
174
+ """
175
+
176
+ redacted_data: RedactedData
177
+ fpe_context: str
178
+
179
+
180
+ RedactedData = Union[str, Dict]
181
+
182
+
183
+ class UnredactResult(PangeaResponseResult):
184
+ """
185
+ Result class after an unredact request
186
+
187
+ """
188
+
189
+ data: RedactedData
190
+
191
+
110
192
  class Redact(ServiceBase):
111
193
  """Redact service client.
112
194
 
@@ -161,6 +243,9 @@ class Redact(ServiceBase):
161
243
  rules: Optional[List[str]] = None,
162
244
  rulesets: Optional[List[str]] = None,
163
245
  return_result: Optional[bool] = None,
246
+ redaction_method_overrides: Optional[RedactionMethodOverrides] = None,
247
+ llm_request: Optional[bool] = None,
248
+ vault_parameters: Optional[VaultParameters] = None,
164
249
  ) -> PangeaResponse[RedactResult]:
165
250
  """
166
251
  Redact
@@ -176,6 +261,9 @@ class Redact(ServiceBase):
176
261
  rules (list[str], optional): An array of redact rule short names
177
262
  rulesets (list[str], optional): An array of redact rulesets short names
178
263
  return_result(bool, optional): Setting this value to false will omit the redacted result only returning count
264
+ redaction_method_overrides: A set of redaction method overrides for any enabled rule. These methods override the config declared methods
265
+ llm_request: Boolean flag to enable FPE redaction for LLM requests
266
+ vault_parameters: A set of vault parameters to use for redaction
179
267
 
180
268
  Raises:
181
269
  PangeaAPIException: If an API Error happens
@@ -189,8 +277,17 @@ class Redact(ServiceBase):
189
277
  response = redact.redact(text="Jenny Jenny... 555-867-5309")
190
278
  """
191
279
 
192
- input = RedactRequest(text=text, debug=debug, rules=rules, rulesets=rulesets, return_result=return_result)
193
- return self.request.post("v1/redact", RedactResult, data=input.dict(exclude_none=True))
280
+ input = RedactRequest(
281
+ text=text,
282
+ debug=debug,
283
+ rules=rules,
284
+ rulesets=rulesets,
285
+ return_result=return_result,
286
+ redaction_method_overrides=redaction_method_overrides,
287
+ llm_request=llm_request,
288
+ vault_parameters=vault_parameters,
289
+ )
290
+ return self.request.post("v1/redact", RedactResult, data=input.model_dump(exclude_none=True))
194
291
 
195
292
  def redact_structured(
196
293
  self,
@@ -201,6 +298,9 @@ class Redact(ServiceBase):
201
298
  rules: Optional[List[str]] = None,
202
299
  rulesets: Optional[List[str]] = None,
203
300
  return_result: Optional[bool] = None,
301
+ redaction_method_overrides: Optional[RedactionMethodOverrides] = None,
302
+ llm_request: Optional[bool] = None,
303
+ vault_parameters: Optional[VaultParameters] = None,
204
304
  ) -> PangeaResponse[StructuredResult]:
205
305
  """
206
306
  Redact structured
@@ -220,6 +320,9 @@ class Redact(ServiceBase):
220
320
  rules (list[str], optional): An array of redact rule short names
221
321
  rulesets (list[str], optional): An array of redact rulesets short names
222
322
  return_result(bool, optional): Setting this value to false will omit the redacted result only returning count
323
+ redaction_method_overrides: A set of redaction method overrides for any enabled rule. These methods override the config declared methods
324
+ llm_request: Boolean flag to enable FPE redaction for LLM requests
325
+ vault_parameters: A set of vault parameters to use for redaction
223
326
 
224
327
  Raises:
225
328
  PangeaAPIException: If an API Error happens
@@ -246,5 +349,31 @@ class Redact(ServiceBase):
246
349
  rules=rules,
247
350
  rulesets=rulesets,
248
351
  return_result=return_result,
352
+ redaction_method_overrides=redaction_method_overrides,
353
+ llm_request=llm_request,
354
+ vault_parameters=vault_parameters,
249
355
  )
250
- return self.request.post("v1/redact_structured", StructuredResult, data=input.dict(exclude_none=True))
356
+ return self.request.post("v1/redact_structured", StructuredResult, data=input.model_dump(exclude_none=True))
357
+
358
+ def unredact(self, redacted_data: RedactedData, fpe_context: str) -> PangeaResponse[UnredactResult]:
359
+ """
360
+ Unredact
361
+
362
+ Decrypt or unredact fpe redactions
363
+
364
+ OperationId: redact_post_v1_unredact
365
+
366
+ Args:
367
+ redacted_data: Data to unredact
368
+ fpe_context (base64): FPE context used to decrypt and unredact data
369
+
370
+ Raises:
371
+ PangeaAPIException: If an API Error happens
372
+
373
+ Returns:
374
+ Pangea Response with redacted data in the response.result field,
375
+ available response fields can be found in our
376
+ [API Documentation](https://pangea.cloud/docs/api/redact#unredact)
377
+ """
378
+ input = UnredactRequest(redacted_data=redacted_data, fpe_context=fpe_context)
379
+ return self.request.post("v1/unredact", UnredactResult, data=input.model_dump(exclude_none=True))