domaintools-api 2.8.1__tar.gz → 2.9.0__tar.gz

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 (49) hide show
  1. {domaintools_api-2.8.1/domaintools_api.egg-info → domaintools_api-2.9.0}/PKG-INFO +1 -1
  2. domaintools_api-2.9.0/VERSION +1 -0
  3. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/_version.py +1 -1
  4. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/api.py +68 -0
  5. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/cli/commands/feeds.py +167 -7
  6. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/cli/constants.py +2 -0
  7. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/cli/utils.py +3 -1
  8. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/constants.py +8 -0
  9. {domaintools_api-2.8.1 → domaintools_api-2.9.0/domaintools_api.egg-info}/PKG-INFO +1 -1
  10. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/tests/test_api.py +38 -0
  11. domaintools_api-2.8.1/VERSION +0 -1
  12. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/LICENSE +0 -0
  13. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/README.md +0 -0
  14. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/__init__.py +0 -0
  15. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/base_results.py +0 -0
  16. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/cli/__init__.py +0 -0
  17. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/cli/api.py +0 -0
  18. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/cli/commands/__init__.py +0 -0
  19. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/cli/commands/accounts.py +0 -0
  20. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/cli/commands/detects.py +0 -0
  21. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/cli/commands/domains.py +0 -0
  22. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/cli/commands/ips.py +0 -0
  23. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/cli/commands/iris.py +0 -0
  24. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/cli/main.py +0 -0
  25. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/decorators.py +0 -0
  26. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/docstring_patcher.py +0 -0
  27. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/exceptions.py +0 -0
  28. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/filters.py +0 -0
  29. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/request_validator.py +0 -0
  30. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/results.py +0 -0
  31. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/specs/iris-openapi.yaml +0 -0
  32. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools/utils.py +0 -0
  33. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools_api.egg-info/SOURCES.txt +0 -0
  34. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools_api.egg-info/dependency_links.txt +0 -0
  35. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools_api.egg-info/entry_points.txt +0 -0
  36. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools_api.egg-info/requires.txt +0 -0
  37. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools_api.egg-info/top_level.txt +0 -0
  38. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/domaintools_async/__init__.py +0 -0
  39. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/pyproject.toml +0 -0
  40. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/setup.cfg +0 -0
  41. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/setup.py +0 -0
  42. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/tests/test_async.py +0 -0
  43. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/tests/test_cli.py +0 -0
  44. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/tests/test_decorators.py +0 -0
  45. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/tests/test_docstring_patcher.py +0 -0
  46. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/tests/test_filters.py +0 -0
  47. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/tests/test_iris_enrich_account_info.py +0 -0
  48. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/tests/test_request_validator.py +0 -0
  49. {domaintools_api-2.8.1 → domaintools_api-2.9.0}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: domaintools_api
3
- Version: 2.8.1
3
+ Version: 2.9.0
4
4
  Summary: DomainTools Official Python API
5
5
  Author-email: DomainTools <integrations@domaintools.com>
6
6
  License: The MIT License (MIT)
@@ -0,0 +1 @@
1
+ 2.9.0
@@ -20,4 +20,4 @@ OTHER DEALINGS IN THE SOFTWARE.
20
20
 
21
21
  """
22
22
 
23
- current = "2.8.1"
23
+ current = "2.9.0"
@@ -1412,3 +1412,71 @@ class API(object):
1412
1412
  cls=FeedsResults,
1413
1413
  **kwargs,
1414
1414
  )
1415
+
1416
+ def iphotlist(self, **kwargs) -> FeedsResults:
1417
+ """Returns back list of ip hotlist feed.
1418
+ Captures IP addresses that meet strict criteria for both risk level and recent activity, making it ideal for immediate blocking and threat response.
1419
+
1420
+ before: str: Filter for records before the given time value inclusive or time offset relative to now
1421
+
1422
+ after: str: Filter for records after the given time value inclusive or time offset relative to now
1423
+
1424
+ headers: bool: Use in combination with Accept: text/csv headers to control if headers are sent or not
1425
+
1426
+ sessionID: str: A custom string to distinguish between different sessions
1427
+
1428
+ fromBeginning: bool: Requires a sessionID. When used with a new session ID, returns the first hour of data in the time window (rather than the last). Returns an error if the session ID already exists
1429
+
1430
+ top: int: Limits the number of results in the response payload. Primarily intended for testing. When you apply this parameter to risk feeds, results are sorted by all_threats_combined_percent (descending).
1431
+ """
1432
+ validate_feeds_parameters(kwargs)
1433
+ endpoint = kwargs.pop("endpoint", Endpoint.FEED.value)
1434
+ source = ENDPOINT_TO_SOURCE_MAP.get(endpoint).value
1435
+ if (
1436
+ endpoint == Endpoint.DOWNLOAD.value
1437
+ or kwargs.get("output_format", OutputFormat.JSONL.value) != OutputFormat.CSV.value
1438
+ ):
1439
+ # headers param is allowed only in Feed API and CSV format
1440
+ kwargs.pop("headers", None)
1441
+
1442
+ return self._results(
1443
+ f"real-time-ip-hotlist-({source})",
1444
+ f"v1/{endpoint}/iphotlist/",
1445
+ response_path=(),
1446
+ cls=FeedsResults,
1447
+ **kwargs,
1448
+ )
1449
+
1450
+ def iprisk(self, **kwargs) -> FeedsResults:
1451
+ """Returns back list of domain hotlist feed.
1452
+ Captures all IP addresses that actively host one or more domains, providing risk assessment and enrichment data for each IP address.
1453
+
1454
+ before: str: Filter for records before the given time value inclusive or time offset relative to now
1455
+
1456
+ after: str: Filter for records after the given time value inclusive or time offset relative to now
1457
+
1458
+ headers: bool: Use in combination with Accept: text/csv headers to control if headers are sent or not
1459
+
1460
+ sessionID: str: A custom string to distinguish between different sessions
1461
+
1462
+ fromBeginning: bool: Requires a sessionID. When used with a new session ID, returns the first hour of data in the time window (rather than the last). Returns an error if the session ID already exists
1463
+
1464
+ top: int: Limits the number of results in the response payload. Primarily intended for testing. When you apply this parameter to risk feeds, results are sorted by all_threats_combined_percent (descending).
1465
+ """
1466
+ validate_feeds_parameters(kwargs)
1467
+ endpoint = kwargs.pop("endpoint", Endpoint.FEED.value)
1468
+ source = ENDPOINT_TO_SOURCE_MAP.get(endpoint).value
1469
+ if (
1470
+ endpoint == Endpoint.DOWNLOAD.value
1471
+ or kwargs.get("output_format", OutputFormat.JSONL.value) != OutputFormat.CSV.value
1472
+ ):
1473
+ # headers param is allowed only in Feed API and CSV format
1474
+ kwargs.pop("headers", None)
1475
+
1476
+ return self._results(
1477
+ f"real-time-ip-risk-({source})",
1478
+ f"v1/{endpoint}/iprisk/",
1479
+ response_path=(),
1480
+ cls=FeedsResults,
1481
+ **kwargs,
1482
+ )
@@ -79,7 +79,7 @@ def feeds_nad(
79
79
  "--headers",
80
80
  help="Adds a header to the first line of response when text/csv is set in header parameters",
81
81
  ),
82
- top: str = typer.Option(
82
+ top: int = typer.Option(
83
83
  None,
84
84
  "--top",
85
85
  help="Number of results to return in the response payload. This is ignored in download endpoint",
@@ -159,7 +159,7 @@ def feeds_nod(
159
159
  "--headers",
160
160
  help="Adds a header to the first line of response when text/csv is set in header parameters",
161
161
  ),
162
- top: str = typer.Option(
162
+ top: int = typer.Option(
163
163
  None,
164
164
  "--top",
165
165
  help="Number of results to return in the response payload. This is ignored in download endpoint",
@@ -227,7 +227,7 @@ def feeds_domainrdap(
227
227
  "--domain",
228
228
  help="A string value used to filter feed results",
229
229
  ),
230
- top: str = typer.Option(
230
+ top: int = typer.Option(
231
231
  None,
232
232
  "--top",
233
233
  help="Number of results to return in the response payload",
@@ -307,7 +307,7 @@ def feeds_domaindiscovery(
307
307
  "--headers",
308
308
  help="Adds a header to the first line of response when text/csv is set in header parameters",
309
309
  ),
310
- top: str = typer.Option(
310
+ top: int = typer.Option(
311
311
  None,
312
312
  "--top",
313
313
  help="Number of results to return in the response payload. This is ignored in download endpoint",
@@ -387,7 +387,7 @@ def feeds_noh(
387
387
  "--headers",
388
388
  help="Adds a header to the first line of response when text/csv is set in header parameters",
389
389
  ),
390
- top: str = typer.Option(
390
+ top: int = typer.Option(
391
391
  None,
392
392
  "--top",
393
393
  help="Number of results to return in the response payload. This is ignored in download endpoint",
@@ -467,7 +467,7 @@ def feeds_domainhotlist(
467
467
  "--headers",
468
468
  help="Adds a header to the first line of response when text/csv is set in header parameters",
469
469
  ),
470
- top: str = typer.Option(
470
+ top: int = typer.Option(
471
471
  None,
472
472
  "--top",
473
473
  help="Number of results to return in the response payload. This is ignored in download endpoint",
@@ -547,10 +547,170 @@ def feeds_realtime_domain_risk(
547
547
  "--headers",
548
548
  help="Adds a header to the first line of response when text/csv is set in header parameters",
549
549
  ),
550
- top: str = typer.Option(
550
+ top: int = typer.Option(
551
551
  None,
552
552
  "--top",
553
553
  help="Number of results to return in the response payload. This is ignored in download endpoint",
554
554
  ),
555
555
  ):
556
556
  DTCLICommand.run(name=c.FEEDS_REALTIME_DOMAIN_RISK, params=ctx.params)
557
+
558
+
559
+ @dt_cli.command(
560
+ name=c.FEEDS_IPHOTLIST,
561
+ help=get_cli_helptext_by_name(command_name=c.FEEDS_IPHOTLIST),
562
+ )
563
+ def feeds_iphotlist(
564
+ ctx: typer.Context,
565
+ user: str = typer.Option(None, "-u", "--user", help="Domaintools API Username."),
566
+ key: str = typer.Option(None, "-k", "--key", help="DomainTools API key"),
567
+ creds_file: str = typer.Option(
568
+ "~/.dtapi",
569
+ "-c",
570
+ "--credfile",
571
+ help="Optional file with API username and API key, one per line.",
572
+ ),
573
+ no_verify_ssl: bool = typer.Option(
574
+ False,
575
+ "--no-verify-ssl",
576
+ help="Skip verification of SSL certificate when making HTTPs API calls",
577
+ ),
578
+ no_sign_api_key: bool = typer.Option(
579
+ False,
580
+ "--no-sign-api-key",
581
+ help="Skip signing of api key",
582
+ ),
583
+ no_header_authentication: bool = typer.Option(
584
+ False,
585
+ "--no-header-auth",
586
+ help="Don't use header authentication",
587
+ ),
588
+ output_format: str = typer.Option(
589
+ "jsonl",
590
+ "-f",
591
+ "--format",
592
+ help=f"Output format in [{OutputFormat.JSONL.value}, {OutputFormat.CSV.value}]",
593
+ callback=DTCLICommand.validate_feeds_format_input,
594
+ ),
595
+ endpoint: str = typer.Option(
596
+ Endpoint.FEED.value,
597
+ "-e",
598
+ "--endpoint",
599
+ help=f"Valid endpoints: [{Endpoint.FEED.value}, {Endpoint.DOWNLOAD.value}]",
600
+ callback=DTCLICommand.validate_endpoint_input,
601
+ ),
602
+ sessionID: str = typer.Option(
603
+ None,
604
+ "--session-id",
605
+ help="Unique identifier for the session",
606
+ ),
607
+ after: str = typer.Option(
608
+ None,
609
+ "--after",
610
+ help="Start of the time window, relative to the current time in seconds, for which data will be provided",
611
+ callback=DTCLICommand.validate_after_or_before_input,
612
+ ),
613
+ before: str = typer.Option(
614
+ None,
615
+ "--before",
616
+ help="The end of the query window in seconds, relative to the current time, inclusive",
617
+ callback=DTCLICommand.validate_after_or_before_input,
618
+ ),
619
+ fromBeginning: bool = typer.Option(
620
+ None,
621
+ "-fb",
622
+ "--frombeginning",
623
+ help="Requires a sessionID. When used with a new session ID, returns the first hour of data in the time window (rather than the last). Returns an error if the session ID already exists",
624
+ ),
625
+ top: int = typer.Option(
626
+ None,
627
+ "--top",
628
+ help="Number of results to return in the response payload. This is ignored in download endpoint",
629
+ ),
630
+ headers: bool = typer.Option(
631
+ False,
632
+ "--headers",
633
+ help="Adds a header to the first line of response when text/csv is set in header parameters",
634
+ ),
635
+ ):
636
+ DTCLICommand.run(name=c.FEEDS_IPHOTLIST, params=ctx.params)
637
+
638
+
639
+ @dt_cli.command(
640
+ name=c.FEEDS_IPRISK,
641
+ help=get_cli_helptext_by_name(command_name=c.FEEDS_IPRISK),
642
+ )
643
+ def feeds_iprisk(
644
+ ctx: typer.Context,
645
+ user: str = typer.Option(None, "-u", "--user", help="Domaintools API Username."),
646
+ key: str = typer.Option(None, "-k", "--key", help="DomainTools API key"),
647
+ creds_file: str = typer.Option(
648
+ "~/.dtapi",
649
+ "-c",
650
+ "--credfile",
651
+ help="Optional file with API username and API key, one per line.",
652
+ ),
653
+ no_verify_ssl: bool = typer.Option(
654
+ False,
655
+ "--no-verify-ssl",
656
+ help="Skip verification of SSL certificate when making HTTPs API calls",
657
+ ),
658
+ no_sign_api_key: bool = typer.Option(
659
+ False,
660
+ "--no-sign-api-key",
661
+ help="Skip signing of api key",
662
+ ),
663
+ no_header_authentication: bool = typer.Option(
664
+ False,
665
+ "--no-header-auth",
666
+ help="Don't use header authentication",
667
+ ),
668
+ output_format: str = typer.Option(
669
+ "jsonl",
670
+ "-f",
671
+ "--format",
672
+ help=f"Output format in [{OutputFormat.JSONL.value}, {OutputFormat.CSV.value}]",
673
+ callback=DTCLICommand.validate_feeds_format_input,
674
+ ),
675
+ endpoint: str = typer.Option(
676
+ Endpoint.FEED.value,
677
+ "-e",
678
+ "--endpoint",
679
+ help=f"Valid endpoints: [{Endpoint.FEED.value}, {Endpoint.DOWNLOAD.value}]",
680
+ callback=DTCLICommand.validate_endpoint_input,
681
+ ),
682
+ sessionID: str = typer.Option(
683
+ None,
684
+ "--session-id",
685
+ help="Unique identifier for the session",
686
+ ),
687
+ after: str = typer.Option(
688
+ None,
689
+ "--after",
690
+ help="Start of the time window, relative to the current time in seconds, for which data will be provided",
691
+ callback=DTCLICommand.validate_after_or_before_input,
692
+ ),
693
+ before: str = typer.Option(
694
+ None,
695
+ "--before",
696
+ help="The end of the query window in seconds, relative to the current time, inclusive",
697
+ callback=DTCLICommand.validate_after_or_before_input,
698
+ ),
699
+ fromBeginning: bool = typer.Option(
700
+ None,
701
+ "-fb",
702
+ "--frombeginning",
703
+ help="Requires a sessionID. When used with a new session ID, returns the first hour of data in the time window (rather than the last). Returns an error if the session ID already exists",
704
+ ),
705
+ top: int = typer.Option(
706
+ None,
707
+ "--top",
708
+ help="Number of results to return in the response payload. This is ignored in download endpoint",
709
+ ),
710
+ headers: bool = typer.Option(
711
+ False,
712
+ "--headers",
713
+ help="Adds a header to the first line of response when text/csv is set in header parameters",
714
+ ),
715
+ ):
716
+ DTCLICommand.run(name=c.FEEDS_IPRISK, params=ctx.params)
@@ -48,3 +48,5 @@ FEEDS_DOMAINHOTLIST = "domainhotlist"
48
48
  FEEDS_DOMAINRDAP = "domainrdap"
49
49
  FEEDS_DOMAINDISCOVERY = "domaindiscovery"
50
50
  FEEDS_REALTIME_DOMAIN_RISK = "realtime_domain_risk"
51
+ FEEDS_IPHOTLIST = "iphotlist"
52
+ FEEDS_IPRISK = "iprisk"
@@ -65,10 +65,12 @@ _command_help_mapper = {
65
65
  c.FEEDS_NAD: "Returns back newly active domains feed.",
66
66
  c.FEEDS_NOD: "Returns back newly observed domains feed.",
67
67
  c.FEEDS_NOH: "Returns back newly observed hosts feed.",
68
- c.FEEDS_DOMAINHOTLIST: "Returns domaint hotlist feed.",
68
+ c.FEEDS_DOMAINHOTLIST: "Returns domain hotlist feed.",
69
69
  c.FEEDS_DOMAINRDAP: "Returns changes to global domain registration information, populated by the Registration Data Access Protocol (RDAP).",
70
70
  c.FEEDS_DOMAINDISCOVERY: "Returns new domains as they are either discovered in domain registration information, observed by our global sensor network, or reported by trusted third parties.",
71
71
  c.FEEDS_REALTIME_DOMAIN_RISK: "Returns realtime domain risk information for apex-level domains, regardless of observed traffic.",
72
+ c.FEEDS_IPHOTLIST: "Returns ip hotlist feed.",
73
+ c.FEEDS_IPRISK: "Returns ip risk feed.",
72
74
  }
73
75
 
74
76
 
@@ -38,6 +38,10 @@ RTTF_PRODUCTS_LIST = [
38
38
  "real-time-domain-risk-(s3)",
39
39
  "real-time-domain-discovery-feed-(api)",
40
40
  "real-time-domain-discovery-feed-(s3)",
41
+ "real-time-ip-hotlist-(api)",
42
+ "real-time-ip-hotlist-(s3)",
43
+ "real-time-ip-risk-(api)",
44
+ "real-time-ip-risk-(s3)",
41
45
  ]
42
46
 
43
47
  RTTF_PRODUCTS_CMD_MAPPING = {
@@ -55,6 +59,10 @@ RTTF_PRODUCTS_CMD_MAPPING = {
55
59
  "real-time-domain-risk-(s3)": "realtime_domain_risk",
56
60
  "real-time-domain-discovery-feed-(api)": "domaindiscovery",
57
61
  "real-time-domain-discovery-feed-(s3)": "domaindiscovery",
62
+ "real-time-ip-hotlist-(api)": "iphotlist",
63
+ "real-time-ip-hotlist-(s3)": "iphotlist",
64
+ "real-time-ip-risk-(api)": "iprisk",
65
+ "real-time-ip-risk-(s3)": "iprisk",
58
66
  }
59
67
 
60
68
  SPECS_MAPPING = {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: domaintools_api
3
- Version: 2.8.1
3
+ Version: 2.9.0
4
4
  Summary: DomainTools Official Python API
5
5
  Author-email: DomainTools <integrations@domaintools.com>
6
6
  License: The MIT License (MIT)
@@ -827,6 +827,43 @@ def test_domain_hotlist():
827
827
  assert "proximity_risk" in feed_result.keys()
828
828
  assert "overall_risk" in feed_result.keys()
829
829
 
830
+ @vcr.use_cassette
831
+ def test_ip_hotlist():
832
+ results = feeds_api.iphotlist(after="-60", top=5)
833
+ for response in results.response():
834
+ assert results.status == 200
835
+
836
+ response = response.strip()
837
+ assert response is not None
838
+
839
+ feed_result = json.loads(response)
840
+ assert "timestamp" in feed_result.keys()
841
+ assert "ip" in feed_result.keys()
842
+ assert "asn" in feed_result.keys()
843
+ assert "organization" in feed_result.keys()
844
+ assert "total_domains" in feed_result.keys()
845
+ assert "all_threats_combined_count" in feed_result.keys()
846
+ assert "all_threats_combined_percent" in feed_result.keys()
847
+
848
+
849
+ @vcr.use_cassette
850
+ def test_ip_risk():
851
+ results = feeds_api.iprisk(after="-60", top=5)
852
+ for response in results.response():
853
+ assert results.status == 200
854
+
855
+ response = response.strip()
856
+ assert response is not None
857
+
858
+ feed_result = json.loads(response)
859
+ assert "timestamp" in feed_result.keys()
860
+ assert "ip" in feed_result.keys()
861
+ assert "asn" in feed_result.keys()
862
+ assert "organization" in feed_result.keys()
863
+ assert "total_domains" in feed_result.keys()
864
+ assert "all_threats_combined_count" in feed_result.keys()
865
+ assert "all_threats_combined_percent" in feed_result.keys()
866
+
830
867
 
831
868
  @vcr.use_cassette
832
869
  def test_feeds_endpoint_should_raise_error_if_signed_api_key_is_used():
@@ -835,3 +872,4 @@ def test_feeds_endpoint_should_raise_error_if_signed_api_key_is_used():
835
872
  feeds_api.domaindiscovery(after="-60")
836
873
 
837
874
  assert str(excinfo.value) == "Real Time Threat Feeds do not support signed API keys."
875
+
@@ -1 +0,0 @@
1
- 2.8.1
File without changes