From 8b112c9774d66549b220a5486f1e954183e08458 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Thu, 18 Jan 2024 13:19:43 -0500 Subject: [PATCH 01/35] baddns module initial (kindof) --- bbot/modules/baddns.py | 63 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 bbot/modules/baddns.py diff --git a/bbot/modules/baddns.py b/bbot/modules/baddns.py new file mode 100644 index 000000000..0a86bd978 --- /dev/null +++ b/bbot/modules/baddns.py @@ -0,0 +1,63 @@ +from baddns.base import get_all_modules +from .base import BaseModule + +import logging +from bbot.core.logger.logger import include_logger + +include_logger(logging.getLogger("baddns")) + + +class baddns(BaseModule): + watched_events = ["DNS_NAME", "DNS_NAME_UNRESOLVED"] + produced_events = ["FINDING", "VULNERABILITY"] + flags = ["active", "safe", "web-basic"] + meta = {"description": "Check subdomains for for subdomain takeovers and other DNS tomfoolery"} + options = {"custom_nameservers": []} + options_desc = { + "custom_nameservers": "Force BadDNS to use a list of custom nameservers", + } + max_event_handlers = 8 + deps_pip = ["baddns"] + + async def setup(self): + self.custom_nameservers = self.config.get("custom_nameservers", []) or None + if self.custom_namesevers: + self.custom_nameservers = self.helpers.chain_lists(self.custom_nameservers) + return True + + async def handle_event(self, event): + all_modules = get_all_modules() + for ModuleClass in all_modules: + module_instance = ModuleClass( + event.data, + http_client_class=self.scan.helpers.web.AsyncClient, + dns_client=self.scan.helpers.dns.resolver, + custom_nameservers=self.custom_nameservers, + ) + if await module_instance.dispatch(): + results = module_instance.analyze() + if results and len(results) > 0: + for r in results: + r_dict = r.to_dict() + + if r_dict["confidence"] in ["CONFIRMED", "PROBABLE"]: + data = { + "severity": "MEDIUM", + "description": f"{r_dict['description']}. Confidence: [{r_dict['confidence']}] Signature: [{r_dict['signature']}] Indicator: [{r_dict['indicator']}] Trigger: [{r_dict['trigger']}] baddns Module: [{r_dict['module']}]", + "host": str(event.host), + } + self.emit_event(data, "VULNERABILITY", event) + + elif r_dict["confidence"] in ["UNLIKELY", "POSSIBLE"]: + data = { + "description": f"{r_dict['description']} Confidence: [{r_dict['confidence']}] Signature: [{r_dict['signature']}] Indicator: [{r_dict['indicator']}] Trigger: [{r_dict['trigger']}] baddns Module: [{r_dict['module']}]", + "host": str(event.host), + } + self.emit_event(data, "FINDING", event) + else: + log.warning(f"Got unrecognized confidence level: {r['confidence']}") + + found_domains = r_dict.get("found_domains", None) + if found_domains: + for found_domain in found_domains: + self.emit_event(found_domain, "DNS_NAME", event) From 4facff8b99ce01c8f9c1323d0d425d7cd4f5be3e Mon Sep 17 00:00:00 2001 From: liquidsec Date: Thu, 18 Jan 2024 15:57:14 -0500 Subject: [PATCH 02/35] typo fix --- bbot/modules/baddns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/modules/baddns.py b/bbot/modules/baddns.py index 0a86bd978..c2abfcc38 100644 --- a/bbot/modules/baddns.py +++ b/bbot/modules/baddns.py @@ -21,7 +21,7 @@ class baddns(BaseModule): async def setup(self): self.custom_nameservers = self.config.get("custom_nameservers", []) or None - if self.custom_namesevers: + if self.custom_nameservers: self.custom_nameservers = self.helpers.chain_lists(self.custom_nameservers) return True From bd240076f3466c359fd3b86cc651bad33629329d Mon Sep 17 00:00:00 2001 From: liquidsec Date: Fri, 26 Jan 2024 17:32:29 -0500 Subject: [PATCH 03/35] adding MockResolver, baddns tests --- bbot/modules/baddns.py | 26 ++++-- bbot/test/conftest.py | 67 ++++++++++++++++ bbot/test/test_step_2/module_tests/base.py | 4 +- .../module_tests/test_module_baddns.py | 80 +++++++++++++++++++ .../module_tests/test_module_baddns_zone.py | 68 ++++++++++++++++ 5 files changed, 236 insertions(+), 9 deletions(-) create mode 100644 bbot/test/test_step_2/module_tests/test_module_baddns.py create mode 100644 bbot/test/test_step_2/module_tests/test_module_baddns_zone.py diff --git a/bbot/modules/baddns.py b/bbot/modules/baddns.py index c2abfcc38..c6a8359b1 100644 --- a/bbot/modules/baddns.py +++ b/bbot/modules/baddns.py @@ -10,24 +10,32 @@ class baddns(BaseModule): watched_events = ["DNS_NAME", "DNS_NAME_UNRESOLVED"] produced_events = ["FINDING", "VULNERABILITY"] - flags = ["active", "safe", "web-basic"] + flags = ["active", "safe", "web-basic", "baddns"] meta = {"description": "Check subdomains for for subdomain takeovers and other DNS tomfoolery"} - options = {"custom_nameservers": []} + options = {"custom_nameservers": [], "only_high_confidence": False} options_desc = { "custom_nameservers": "Force BadDNS to use a list of custom nameservers", + "only_high_confidence": "Do not emit low-confidence or generic detections", } max_event_handlers = 8 deps_pip = ["baddns"] + def select_modules(self): + selected_modules = [] + for m in get_all_modules(): + if m.name in ["CNAME", "NS", "MX", "references", "TXT"]: + selected_modules.append(m) + return selected_modules + async def setup(self): self.custom_nameservers = self.config.get("custom_nameservers", []) or None if self.custom_nameservers: self.custom_nameservers = self.helpers.chain_lists(self.custom_nameservers) + self.only_high_confidence = self.config.get("only_high_confidence", False) return True async def handle_event(self, event): - all_modules = get_all_modules() - for ModuleClass in all_modules: + for ModuleClass in self.select_modules(): module_instance = ModuleClass( event.data, http_client_class=self.scan.helpers.web.AsyncClient, @@ -46,18 +54,20 @@ async def handle_event(self, event): "description": f"{r_dict['description']}. Confidence: [{r_dict['confidence']}] Signature: [{r_dict['signature']}] Indicator: [{r_dict['indicator']}] Trigger: [{r_dict['trigger']}] baddns Module: [{r_dict['module']}]", "host": str(event.host), } - self.emit_event(data, "VULNERABILITY", event) + await self.emit_event(data, "VULNERABILITY", event, tags=[f"baddns-{ModuleClass.name}"]) - elif r_dict["confidence"] in ["UNLIKELY", "POSSIBLE"]: + elif r_dict["confidence"] in ["UNLIKELY", "POSSIBLE"] and not self.only_high_confidence: data = { "description": f"{r_dict['description']} Confidence: [{r_dict['confidence']}] Signature: [{r_dict['signature']}] Indicator: [{r_dict['indicator']}] Trigger: [{r_dict['trigger']}] baddns Module: [{r_dict['module']}]", "host": str(event.host), } - self.emit_event(data, "FINDING", event) + await self.emit_event(data, "FINDING", event, tags=[f"baddns-{ModuleClass.name}"]) else: log.warning(f"Got unrecognized confidence level: {r['confidence']}") found_domains = r_dict.get("found_domains", None) if found_domains: for found_domain in found_domains: - self.emit_event(found_domain, "DNS_NAME", event) + await self.emit_event( + found_domain, "DNS_NAME", event, tags=[f"baddns-{ModuleClass.name}"] + ) diff --git a/bbot/test/conftest.py b/bbot/test/conftest.py index b60a0633d..b7428f102 100644 --- a/bbot/test/conftest.py +++ b/bbot/test/conftest.py @@ -179,3 +179,70 @@ def proxy_server(): # Stop the server. server.shutdown() server_thread.join() + + +class MockResolver: + import dns + + def __init__(self, mock_data=None): + self.mock_data = mock_data if mock_data else {} + self.nameservers = ["127.0.0.1"] + + async def resolve_address(self, host): + try: + from dns.asyncresolver import resolve_address + + result = await resolve_address(host) + return result + except ImportError: + raise ImportError("dns.asyncresolver.Resolver.resolve_address not found") + + def create_dns_response(self, query_name, rdtype): + answers = self.mock_data.get(query_name, {}).get(rdtype, []) + if not answers: + raise self.dns.resolver.NXDOMAIN(f"No answer found for {query_name} {rdtype}") + + message_text = f"""id 1234 +opcode QUERY +rcode NOERROR +flags QR AA RD +;QUESTION +{query_name}. IN {rdtype} +;ANSWER""" + for answer in answers: + message_text += f"\n{query_name}. 1 IN {rdtype} {answer}" + + message_text += "\n;AUTHORITY\n;ADDITIONAL\n" + message = self.dns.message.from_text(message_text) + return message + + async def resolve(self, query_name, rdtype=None): + if rdtype is None: + rdtype = "A" + elif isinstance(rdtype, str): + rdtype = rdtype.upper() + else: + rdtype = str(rdtype.name).upper() + + domain_name = self.dns.name.from_text(query_name) + rdtype_obj = self.dns.rdatatype.from_text(rdtype) + + + if "_NXDOMAIN" in self.mock_data and query_name in self.mock_data["_NXDOMAIN"]: + # Simulate the NXDOMAIN exception + raise self.dns.resolver.NXDOMAIN + + try: + response = self.create_dns_response(query_name, rdtype) + answer = self.dns.resolver.Answer(domain_name, rdtype_obj, self.dns.rdataclass.IN, response) + return answer + except self.dns.resolver.NXDOMAIN as e: + return [] + + +@pytest.fixture() +def configure_mock_resolver(monkeypatch): + def _configure(mock_data): + mock_resolver = MockResolver(mock_data) + return mock_resolver + return _configure diff --git a/bbot/test/test_step_2/module_tests/base.py b/bbot/test/test_step_2/module_tests/base.py index a4562cfc7..e511d7256 100644 --- a/bbot/test/test_step_2/module_tests/base.py +++ b/bbot/test/test_step_2/module_tests/base.py @@ -101,7 +101,9 @@ def mock_record(self, *args, **kwargs): return MockRecord(*args, **kwargs) @pytest_asyncio.fixture - async def module_test(self, httpx_mock, bbot_httpserver, bbot_httpserver_ssl, monkeypatch, request): + async def module_test( + self, httpx_mock, bbot_httpserver, bbot_httpserver_ssl, monkeypatch, request, configure_mock_resolver + ): module_test = self.ModuleTest(self, httpx_mock, bbot_httpserver, bbot_httpserver_ssl, monkeypatch, request) module_test.log.info(f"Starting {self.name} module test") await self.setup_before_prep(module_test) diff --git a/bbot/test/test_step_2/module_tests/test_module_baddns.py b/bbot/test/test_step_2/module_tests/test_module_baddns.py new file mode 100644 index 000000000..789a167f1 --- /dev/null +++ b/bbot/test/test_step_2/module_tests/test_module_baddns.py @@ -0,0 +1,80 @@ +from .base import ModuleTestBase + +from bbot.modules import baddns + +class BaseTestBaddns(ModuleTestBase): + modules_overrides = ["baddns"] + targets = ["bad.dns"] + config_overrides = {"dns_resolution": True} + + async def dispatchWHOIS(x): + return None + + +class TestBaddns_cname_nxdomain(BaseTestBaddns): + async def setup_after_prep(self, module_test): + from baddns.lib.whoismanager import WhoisManager + from baddns.base import get_all_modules + + def select_modules(self): + selected_modules = [] + for m in get_all_modules(): + if m.name in ["CNAME"]: + selected_modules.append(m) + return selected_modules + + mock_data = {"bad.dns": {"CNAME": ["baddns.azurewebsites.net."]}, "_NXDOMAIN": ["baddns.azurewebsites.net"]} + configure_mock_resolver = module_test.request_fixture.getfixturevalue("configure_mock_resolver") + mock_resolver = configure_mock_resolver(mock_data) + module_test.monkeypatch.setattr(module_test.scan.helpers.dns, "resolver", mock_resolver) + module_test.monkeypatch.setattr(baddns.baddns, "select_modules", select_modules) + module_test.monkeypatch.setattr(WhoisManager, "dispatchWHOIS", self.dispatchWHOIS) + + + def check(self, module_test, events): + + assert any(e.data == "baddns.azurewebsites.net" for e in events), "CNAME detection failed" + assert any(e.type == "VULNERABILITY" for e in events), "Failed to emit VULNERABILITY" + assert any("baddns-cname" in e.tags for e in events), "Failed to add baddns tag" + +class TestBaddns_cname_signature(BaseTestBaddns): + + targets = ["bad.dns:8888"] + modules_overrides = ["baddns","speculate"] + + async def setup_after_prep(self, module_test): + + from baddns.base import get_all_modules + from baddns.lib.httpmanager import HttpManager + from baddns.lib.whoismanager import WhoisManager + + + def select_modules(self): + selected_modules = [] + for m in get_all_modules(): + if m.name in ["CNAME"]: + selected_modules.append(m) + return selected_modules + + + + expect_args = {"method": "GET", "uri": "/"} + respond_args = {"response_data": "

Oops! We couldn’t find that page.

", "status": 200} + module_test.set_expect_requests(expect_args=expect_args, respond_args=respond_args) + + + mock_data = {"bad.dns": {"CNAME": ["baddns.bigcartel.com."]}, "baddns.bigcartel.com": {"A": ["127.0.0.1"]}} + configure_mock_resolver = module_test.request_fixture.getfixturevalue("configure_mock_resolver") + mock_resolver = configure_mock_resolver(mock_data) + module_test.monkeypatch.setattr(module_test.scan.helpers.dns, "resolver", mock_resolver) + module_test.monkeypatch.setattr(baddns.baddns, "select_modules", select_modules) + module_test.monkeypatch.setattr(HttpManager, "urls_to_try", ["http://127.0.0.1:8888"]) + module_test.monkeypatch.setattr(WhoisManager, "dispatchWHOIS", self.dispatchWHOIS) + + def check(self, module_test, events): + + + assert any(e for e in events) + # assert any(e.data == "baddns.azurewebsites.net" for e in events), "CNAME detection failed" + assert any(e.type == "VULNERABILITY" and "bigcartel.com" in e.data["description"] for e in events), "Failed to emit VULNERABILITY" + assert any("baddns-cname" in e.tags for e in events), "Failed to add baddns tag" \ No newline at end of file diff --git a/bbot/test/test_step_2/module_tests/test_module_baddns_zone.py b/bbot/test/test_step_2/module_tests/test_module_baddns_zone.py new file mode 100644 index 000000000..26e6b3dbc --- /dev/null +++ b/bbot/test/test_step_2/module_tests/test_module_baddns_zone.py @@ -0,0 +1,68 @@ +import dns + +from .base import ModuleTestBase + + +class BaseTestBaddns_zone(ModuleTestBase): + modules_overrides = ["baddns_zone"] + targets = ["bad.dns"] + config_overrides = {"dns_resolution": True} + + async def dispatchWHOIS(x): + return None + + +class TestBaddns_zone_zonetransfer(BaseTestBaddns_zone): + async def setup_after_prep(self, module_test): + + from baddns.lib.whoismanager import WhoisManager + + def from_xfr(*args, **kwargs): + zone_text = """ +@ 600 IN SOA ns.bad.dns. admin.bad.dns. ( + 1 ; Serial + 3600 ; Refresh + 900 ; Retry + 604800 ; Expire + 86400 ) ; Minimum TTL +@ 600 IN NS ns.bad.dns. +@ 600 IN A 127.0.0.1 +asdf 600 IN A 127.0.0.1 +zzzz 600 IN AAAA dead::beef +""" + zone = dns.zone.from_text(zone_text, origin="bad.dns.") + return zone + + mock_data = {"bad.dns": {"NS": ["ns1.bad.dns."]}, "ns1.bad.dns": {"A": ["127.0.0.1"]}} + configure_mock_resolver = module_test.request_fixture.getfixturevalue("configure_mock_resolver") + + mock_resolver = configure_mock_resolver(mock_data) + module_test.monkeypatch.setattr("dns.zone.from_xfr", from_xfr) + module_test.monkeypatch.setattr(module_test.scan.helpers.dns, "resolver", mock_resolver) + module_test.monkeypatch.setattr(WhoisManager, "dispatchWHOIS", self.dispatchWHOIS) + + def check(self, module_test, events): + assert any(e.data == "zzzz.bad.dns" for e in events), "Zone transfer failed (1)" + assert any(e.data == "asdf.bad.dns" for e in events), "Zone transfer failed (2)" + assert any(e.type == "VULNERABILITY" for e in events), "Failed to emit VULNERABILITY" + assert any("baddns-zonetransfer" in e.tags for e in events), "Failed to add baddns tag" + + +class TestBaddns_zone_nsec(BaseTestBaddns_zone): + async def setup_after_prep(self, module_test): + from baddns.lib.whoismanager import WhoisManager + mock_data = { + "bad.dns": {"NSEC": ["asdf.bad.dns"]}, + "asdf.bad.dns": {"NSEC": ["zzzz.bad.dns"]}, + "zzzz.bad.dns": {"NSEC": ["xyz.bad.dns"]}, + } + configure_mock_resolver = module_test.request_fixture.getfixturevalue("configure_mock_resolver") + mock_resolver = configure_mock_resolver(mock_data) + module_test.monkeypatch.setattr(module_test.scan.helpers.dns, "resolver", mock_resolver) + module_test.monkeypatch.setattr(WhoisManager, "dispatchWHOIS", self.dispatchWHOIS) + + def check(self, module_test, events): + assert any(e.data == "zzzz.bad.dns" for e in events), "NSEC Walk Failed (1)" + assert any(e.data == "xyz.bad.dns" for e in events), "NSEC Walk Failed (2)" + assert any(e.type == "VULNERABILITY" for e in events), "Failed to emit VULNERABILITY" + assert any("baddns-nsec" in e.tags for e in events), "Failed to add baddns tag" From f480848f84e1d20872bd8670f821c182feb626a2 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Mon, 29 Jan 2024 09:53:43 -0500 Subject: [PATCH 04/35] The goodest boy --- bbot/core/helpers/names_generator.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bbot/core/helpers/names_generator.py b/bbot/core/helpers/names_generator.py index 16432cb0e..3e16b446a 100644 --- a/bbot/core/helpers/names_generator.py +++ b/bbot/core/helpers/names_generator.py @@ -93,6 +93,7 @@ "giddy", "glowering", "glutinous", + "golden", "gothic", "grievous", "gummy", @@ -431,6 +432,7 @@ "gollum", "grace", "gregory", + "gus", "hagrid", "hannah", "harold", From a9b7255396d88fcf673544c70a99ef80a466d55b Mon Sep 17 00:00:00 2001 From: liquidsec Date: Mon, 29 Jan 2024 11:20:04 -0500 Subject: [PATCH 05/35] test adjustments --- bbot/modules/baddns.py | 5 +- bbot/test/conftest.py | 2 +- .../module_tests/test_module_baddns.py | 53 ++++++++----------- .../module_tests/test_module_baddns_zone.py | 2 +- 4 files changed, 25 insertions(+), 37 deletions(-) diff --git a/bbot/modules/baddns.py b/bbot/modules/baddns.py index c6a8359b1..c264dcb45 100644 --- a/bbot/modules/baddns.py +++ b/bbot/modules/baddns.py @@ -67,7 +67,4 @@ async def handle_event(self, event): found_domains = r_dict.get("found_domains", None) if found_domains: - for found_domain in found_domains: - await self.emit_event( - found_domain, "DNS_NAME", event, tags=[f"baddns-{ModuleClass.name}"] - ) + await self.emit_event(found_domain, "DNS_NAME", event, tags=[f"baddns-{ModuleClass.name}"]) diff --git a/bbot/test/conftest.py b/bbot/test/conftest.py index b7428f102..e2e28b8dd 100644 --- a/bbot/test/conftest.py +++ b/bbot/test/conftest.py @@ -227,7 +227,6 @@ async def resolve(self, query_name, rdtype=None): domain_name = self.dns.name.from_text(query_name) rdtype_obj = self.dns.rdatatype.from_text(rdtype) - if "_NXDOMAIN" in self.mock_data and query_name in self.mock_data["_NXDOMAIN"]: # Simulate the NXDOMAIN exception raise self.dns.resolver.NXDOMAIN @@ -245,4 +244,5 @@ def configure_mock_resolver(monkeypatch): def _configure(mock_data): mock_resolver = MockResolver(mock_data) return mock_resolver + return _configure diff --git a/bbot/test/test_step_2/module_tests/test_module_baddns.py b/bbot/test/test_step_2/module_tests/test_module_baddns.py index 789a167f1..c4d1318a1 100644 --- a/bbot/test/test_step_2/module_tests/test_module_baddns.py +++ b/bbot/test/test_step_2/module_tests/test_module_baddns.py @@ -2,6 +2,7 @@ from bbot.modules import baddns + class BaseTestBaddns(ModuleTestBase): modules_overrides = ["baddns"] targets = ["bad.dns"] @@ -10,71 +11,61 @@ class BaseTestBaddns(ModuleTestBase): async def dispatchWHOIS(x): return None + def select_modules(self): + from baddns.base import get_all_modules + + selected_modules = [] + for m in get_all_modules(): + if m.name in ["CNAME"]: + selected_modules.append(m) + return selected_modules + class TestBaddns_cname_nxdomain(BaseTestBaddns): async def setup_after_prep(self, module_test): from baddns.lib.whoismanager import WhoisManager from baddns.base import get_all_modules - def select_modules(self): - selected_modules = [] - for m in get_all_modules(): - if m.name in ["CNAME"]: - selected_modules.append(m) - return selected_modules - mock_data = {"bad.dns": {"CNAME": ["baddns.azurewebsites.net."]}, "_NXDOMAIN": ["baddns.azurewebsites.net"]} configure_mock_resolver = module_test.request_fixture.getfixturevalue("configure_mock_resolver") mock_resolver = configure_mock_resolver(mock_data) module_test.monkeypatch.setattr(module_test.scan.helpers.dns, "resolver", mock_resolver) - module_test.monkeypatch.setattr(baddns.baddns, "select_modules", select_modules) + module_test.monkeypatch.setattr(baddns.baddns, "select_modules", self.select_modules) module_test.monkeypatch.setattr(WhoisManager, "dispatchWHOIS", self.dispatchWHOIS) - def check(self, module_test, events): - assert any(e.data == "baddns.azurewebsites.net" for e in events), "CNAME detection failed" assert any(e.type == "VULNERABILITY" for e in events), "Failed to emit VULNERABILITY" assert any("baddns-cname" in e.tags for e in events), "Failed to add baddns tag" -class TestBaddns_cname_signature(BaseTestBaddns): +class TestBaddns_cname_signature(BaseTestBaddns): targets = ["bad.dns:8888"] - modules_overrides = ["baddns","speculate"] + modules_overrides = ["baddns", "speculate"] async def setup_after_prep(self, module_test): - from baddns.base import get_all_modules - from baddns.lib.httpmanager import HttpManager + from baddns.base import BadDNS_base from baddns.lib.whoismanager import WhoisManager + def set_target(self, target): + return "127.0.0.1:8888" - def select_modules(self): - selected_modules = [] - for m in get_all_modules(): - if m.name in ["CNAME"]: - selected_modules.append(m) - return selected_modules - - - expect_args = {"method": "GET", "uri": "/"} respond_args = {"response_data": "

Oops! We couldn’t find that page.

", "status": 200} module_test.set_expect_requests(expect_args=expect_args, respond_args=respond_args) - mock_data = {"bad.dns": {"CNAME": ["baddns.bigcartel.com."]}, "baddns.bigcartel.com": {"A": ["127.0.0.1"]}} configure_mock_resolver = module_test.request_fixture.getfixturevalue("configure_mock_resolver") mock_resolver = configure_mock_resolver(mock_data) module_test.monkeypatch.setattr(module_test.scan.helpers.dns, "resolver", mock_resolver) - module_test.monkeypatch.setattr(baddns.baddns, "select_modules", select_modules) - module_test.monkeypatch.setattr(HttpManager, "urls_to_try", ["http://127.0.0.1:8888"]) + module_test.monkeypatch.setattr(baddns.baddns, "select_modules", self.select_modules) + module_test.monkeypatch.setattr(BadDNS_base, "set_target", set_target) module_test.monkeypatch.setattr(WhoisManager, "dispatchWHOIS", self.dispatchWHOIS) def check(self, module_test, events): - - assert any(e for e in events) - # assert any(e.data == "baddns.azurewebsites.net" for e in events), "CNAME detection failed" - assert any(e.type == "VULNERABILITY" and "bigcartel.com" in e.data["description"] for e in events), "Failed to emit VULNERABILITY" - assert any("baddns-cname" in e.tags for e in events), "Failed to add baddns tag" \ No newline at end of file + assert any( + e.type == "VULNERABILITY" and "bigcartel.com" in e.data["description"] for e in events + ), "Failed to emit VULNERABILITY" + assert any("baddns-cname" in e.tags for e in events), "Failed to add baddns tag" diff --git a/bbot/test/test_step_2/module_tests/test_module_baddns_zone.py b/bbot/test/test_step_2/module_tests/test_module_baddns_zone.py index 26e6b3dbc..75f65bef1 100644 --- a/bbot/test/test_step_2/module_tests/test_module_baddns_zone.py +++ b/bbot/test/test_step_2/module_tests/test_module_baddns_zone.py @@ -14,7 +14,6 @@ async def dispatchWHOIS(x): class TestBaddns_zone_zonetransfer(BaseTestBaddns_zone): async def setup_after_prep(self, module_test): - from baddns.lib.whoismanager import WhoisManager def from_xfr(*args, **kwargs): @@ -51,6 +50,7 @@ def check(self, module_test, events): class TestBaddns_zone_nsec(BaseTestBaddns_zone): async def setup_after_prep(self, module_test): from baddns.lib.whoismanager import WhoisManager + mock_data = { "bad.dns": {"NSEC": ["asdf.bad.dns"]}, "asdf.bad.dns": {"NSEC": ["zzzz.bad.dns"]}, From 733e2f88ae361fa711448836f04026df3bc76888 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Mon, 29 Jan 2024 14:14:10 -0500 Subject: [PATCH 06/35] misc cleanup --- bbot/modules/baddns.py | 7 ++++--- bbot/test/conftest.py | 2 +- bbot/test/test_step_2/module_tests/test_module_baddns.py | 2 -- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/bbot/modules/baddns.py b/bbot/modules/baddns.py index c264dcb45..0f3791a9c 100644 --- a/bbot/modules/baddns.py +++ b/bbot/modules/baddns.py @@ -18,7 +18,7 @@ class baddns(BaseModule): "only_high_confidence": "Do not emit low-confidence or generic detections", } max_event_handlers = 8 - deps_pip = ["baddns"] + deps_pip = ["baddns~=1.0.666"] def select_modules(self): selected_modules = [] @@ -63,8 +63,9 @@ async def handle_event(self, event): } await self.emit_event(data, "FINDING", event, tags=[f"baddns-{ModuleClass.name}"]) else: - log.warning(f"Got unrecognized confidence level: {r['confidence']}") + self.warning(f"Got unrecognized confidence level: {r['confidence']}") found_domains = r_dict.get("found_domains", None) if found_domains: - await self.emit_event(found_domain, "DNS_NAME", event, tags=[f"baddns-{ModuleClass.name}"]) + for found_domain in found_domains: + await self.emit_event(found_domain, "DNS_NAME", event, tags=[f"baddns-{ModuleClass.name}"]) diff --git a/bbot/test/conftest.py b/bbot/test/conftest.py index e2e28b8dd..dd59eac30 100644 --- a/bbot/test/conftest.py +++ b/bbot/test/conftest.py @@ -235,7 +235,7 @@ async def resolve(self, query_name, rdtype=None): response = self.create_dns_response(query_name, rdtype) answer = self.dns.resolver.Answer(domain_name, rdtype_obj, self.dns.rdataclass.IN, response) return answer - except self.dns.resolver.NXDOMAIN as e: + except self.dns.resolver.NXDOMAIN: return [] diff --git a/bbot/test/test_step_2/module_tests/test_module_baddns.py b/bbot/test/test_step_2/module_tests/test_module_baddns.py index c4d1318a1..1c50d6f6a 100644 --- a/bbot/test/test_step_2/module_tests/test_module_baddns.py +++ b/bbot/test/test_step_2/module_tests/test_module_baddns.py @@ -24,7 +24,6 @@ def select_modules(self): class TestBaddns_cname_nxdomain(BaseTestBaddns): async def setup_after_prep(self, module_test): from baddns.lib.whoismanager import WhoisManager - from baddns.base import get_all_modules mock_data = {"bad.dns": {"CNAME": ["baddns.azurewebsites.net."]}, "_NXDOMAIN": ["baddns.azurewebsites.net"]} configure_mock_resolver = module_test.request_fixture.getfixturevalue("configure_mock_resolver") @@ -44,7 +43,6 @@ class TestBaddns_cname_signature(BaseTestBaddns): modules_overrides = ["baddns", "speculate"] async def setup_after_prep(self, module_test): - from baddns.base import get_all_modules from baddns.base import BadDNS_base from baddns.lib.whoismanager import WhoisManager From 610a69edb66f88b74867e8d35d6afa70eba8dd72 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Mon, 29 Jan 2024 14:16:55 -0500 Subject: [PATCH 07/35] black --- bbot/modules/baddns.py | 4 +++- bbot/test/test_step_1/test_events.py | 8 +++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bbot/modules/baddns.py b/bbot/modules/baddns.py index 0f3791a9c..44e432f27 100644 --- a/bbot/modules/baddns.py +++ b/bbot/modules/baddns.py @@ -68,4 +68,6 @@ async def handle_event(self, event): found_domains = r_dict.get("found_domains", None) if found_domains: for found_domain in found_domains: - await self.emit_event(found_domain, "DNS_NAME", event, tags=[f"baddns-{ModuleClass.name}"]) + await self.emit_event( + found_domain, "DNS_NAME", event, tags=[f"baddns-{ModuleClass.name}"] + ) diff --git a/bbot/test/test_step_1/test_events.py b/bbot/test/test_step_1/test_events.py index 0f30f6498..1fc01a046 100644 --- a/bbot/test/test_step_1/test_events.py +++ b/bbot/test/test_step_1/test_events.py @@ -321,10 +321,7 @@ async def test_events(events, scan, helpers, bbot_config): assert scan.make_event("テスト@ドメイン.テスト", dummy=True).data == "テスト@xn--eckwd4c7c.xn--zckzah" assert scan.make_event("ドメイン.テスト:80", dummy=True).data == "xn--eckwd4c7c.xn--zckzah:80" assert scan.make_event("http://ドメイン.テスト:80", dummy=True).data == "http://xn--eckwd4c7c.xn--zckzah/" - assert ( - scan.make_event("http://ドメイン.テスト:80/テスト", dummy=True).data - == "http://xn--eckwd4c7c.xn--zckzah/テスト" - ) + assert scan.make_event("http://ドメイン.テスト:80/テスト", dummy=True).data == "http://xn--eckwd4c7c.xn--zckzah/テスト" # thai assert ( scan.make_event("xn--12c1bik6bbd8ab6hd1b5jc6jta.com", dummy=True).data == "xn--12c1bik6bbd8ab6hd1b5jc6jta.com" @@ -355,7 +352,8 @@ async def test_events(events, scan, helpers, bbot_config): assert scan.make_event("ทดสอบ@เราเที่ยวด้วยกัน.com", dummy=True).data == "ทดสอบ@xn--12c1bik6bbd8ab6hd1b5jc6jta.com" assert scan.make_event("เราเที่ยวด้วยกัน.com:80", dummy=True).data == "xn--12c1bik6bbd8ab6hd1b5jc6jta.com:80" assert ( - scan.make_event("http://เราเที่ยวด้วยกัน.com:80", dummy=True).data == "http://xn--12c1bik6bbd8ab6hd1b5jc6jta.com/" + scan.make_event("http://เราเที่ยวด้วยกัน.com:80", dummy=True).data + == "http://xn--12c1bik6bbd8ab6hd1b5jc6jta.com/" ) assert ( scan.make_event("http://เราเที่ยวด้วยกัน.com:80/ทดสอบ", dummy=True).data From 11bf57283f0abaa782467bf6683d5c18c6ada5d5 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Mon, 29 Jan 2024 14:26:51 -0500 Subject: [PATCH 08/35] black --- bbot/test/test_step_1/test_events.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bbot/test/test_step_1/test_events.py b/bbot/test/test_step_1/test_events.py index 1fc01a046..0f30f6498 100644 --- a/bbot/test/test_step_1/test_events.py +++ b/bbot/test/test_step_1/test_events.py @@ -321,7 +321,10 @@ async def test_events(events, scan, helpers, bbot_config): assert scan.make_event("テスト@ドメイン.テスト", dummy=True).data == "テスト@xn--eckwd4c7c.xn--zckzah" assert scan.make_event("ドメイン.テスト:80", dummy=True).data == "xn--eckwd4c7c.xn--zckzah:80" assert scan.make_event("http://ドメイン.テスト:80", dummy=True).data == "http://xn--eckwd4c7c.xn--zckzah/" - assert scan.make_event("http://ドメイン.テスト:80/テスト", dummy=True).data == "http://xn--eckwd4c7c.xn--zckzah/テスト" + assert ( + scan.make_event("http://ドメイン.テスト:80/テスト", dummy=True).data + == "http://xn--eckwd4c7c.xn--zckzah/テスト" + ) # thai assert ( scan.make_event("xn--12c1bik6bbd8ab6hd1b5jc6jta.com", dummy=True).data == "xn--12c1bik6bbd8ab6hd1b5jc6jta.com" @@ -352,8 +355,7 @@ async def test_events(events, scan, helpers, bbot_config): assert scan.make_event("ทดสอบ@เราเที่ยวด้วยกัน.com", dummy=True).data == "ทดสอบ@xn--12c1bik6bbd8ab6hd1b5jc6jta.com" assert scan.make_event("เราเที่ยวด้วยกัน.com:80", dummy=True).data == "xn--12c1bik6bbd8ab6hd1b5jc6jta.com:80" assert ( - scan.make_event("http://เราเที่ยวด้วยกัน.com:80", dummy=True).data - == "http://xn--12c1bik6bbd8ab6hd1b5jc6jta.com/" + scan.make_event("http://เราเที่ยวด้วยกัน.com:80", dummy=True).data == "http://xn--12c1bik6bbd8ab6hd1b5jc6jta.com/" ) assert ( scan.make_event("http://เราเที่ยวด้วยกัน.com:80/ทดสอบ", dummy=True).data From d4c4cf96f9de358f67e853df47cf247f8e770605 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Mon, 29 Jan 2024 14:50:23 -0500 Subject: [PATCH 09/35] add baddns_zone module --- bbot/modules/baddns_zone.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 bbot/modules/baddns_zone.py diff --git a/bbot/modules/baddns_zone.py b/bbot/modules/baddns_zone.py new file mode 100644 index 000000000..93d555d8c --- /dev/null +++ b/bbot/modules/baddns_zone.py @@ -0,0 +1,34 @@ +from baddns.base import get_all_modules +from .baddns import baddns as baddns_module + +import logging +from bbot.core.logger.logger import include_logger + +include_logger(logging.getLogger("baddns_zone")) + + +class baddns_zone(baddns_module): + watched_events = ["DNS_NAME"] + produced_events = ["FINDING", "VULNERABILITY"] + flags = ["active", "safe", "web-basic", "baddns"] + meta = {"description": "Check subdomains for for subdomain takeovers and other DNS tomfoolery"} + options = {"custom_nameservers": [], "only_high_confidence": False} + options_desc = { + "custom_nameservers": "Force BadDNS to use a list of custom nameservers", + "only_high_confidence": "Do not emit low-confidence or generic detections", + } + max_event_handlers = 8 + deps_pip = ["baddns~=1.0.666"] + + def select_modules(self): + selected_modules = [] + for m in get_all_modules(): + if m.name in ["NSEC", "zonetransfer"]: + selected_modules.append(m) + return selected_modules + + # minimize nsec records feeding back into themselves + async def filter_event(self, event): + if "baddns-nsec" in event.tags or "baddns-nsec" in event.source.tags: + return False + return True From 7a340d1f1b197a8233ccce41913d646e5d0dccec Mon Sep 17 00:00:00 2001 From: liquidsec Date: Mon, 29 Jan 2024 14:59:37 -0500 Subject: [PATCH 10/35] disambiguating test imports --- bbot/test/test_step_2/module_tests/test_module_baddns.py | 6 +++--- .../test_step_2/module_tests/test_module_baddns_zone.py | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/bbot/test/test_step_2/module_tests/test_module_baddns.py b/bbot/test/test_step_2/module_tests/test_module_baddns.py index 1c50d6f6a..6c088c634 100644 --- a/bbot/test/test_step_2/module_tests/test_module_baddns.py +++ b/bbot/test/test_step_2/module_tests/test_module_baddns.py @@ -1,6 +1,6 @@ from .base import ModuleTestBase -from bbot.modules import baddns +from bbot.modules import baddns as baddns_module class BaseTestBaddns(ModuleTestBase): @@ -29,7 +29,7 @@ async def setup_after_prep(self, module_test): configure_mock_resolver = module_test.request_fixture.getfixturevalue("configure_mock_resolver") mock_resolver = configure_mock_resolver(mock_data) module_test.monkeypatch.setattr(module_test.scan.helpers.dns, "resolver", mock_resolver) - module_test.monkeypatch.setattr(baddns.baddns, "select_modules", self.select_modules) + module_test.monkeypatch.setattr(baddns_module.baddns, "select_modules", self.select_modules) module_test.monkeypatch.setattr(WhoisManager, "dispatchWHOIS", self.dispatchWHOIS) def check(self, module_test, events): @@ -57,7 +57,7 @@ def set_target(self, target): configure_mock_resolver = module_test.request_fixture.getfixturevalue("configure_mock_resolver") mock_resolver = configure_mock_resolver(mock_data) module_test.monkeypatch.setattr(module_test.scan.helpers.dns, "resolver", mock_resolver) - module_test.monkeypatch.setattr(baddns.baddns, "select_modules", self.select_modules) + module_test.monkeypatch.setattr(baddns_module.baddns, "select_modules", self.select_modules) module_test.monkeypatch.setattr(BadDNS_base, "set_target", set_target) module_test.monkeypatch.setattr(WhoisManager, "dispatchWHOIS", self.dispatchWHOIS) diff --git a/bbot/test/test_step_2/module_tests/test_module_baddns_zone.py b/bbot/test/test_step_2/module_tests/test_module_baddns_zone.py index 75f65bef1..1a0ef9cf5 100644 --- a/bbot/test/test_step_2/module_tests/test_module_baddns_zone.py +++ b/bbot/test/test_step_2/module_tests/test_module_baddns_zone.py @@ -1,5 +1,4 @@ import dns - from .base import ModuleTestBase From 849cb1418ff2c2f15a6413e4f100444d99749190 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Mon, 29 Jan 2024 15:19:15 -0500 Subject: [PATCH 11/35] fixing import short-circuit --- bbot/test/test_step_2/module_tests/test_module_baddns.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bbot/test/test_step_2/module_tests/test_module_baddns.py b/bbot/test/test_step_2/module_tests/test_module_baddns.py index 6c088c634..5f1ad4d7a 100644 --- a/bbot/test/test_step_2/module_tests/test_module_baddns.py +++ b/bbot/test/test_step_2/module_tests/test_module_baddns.py @@ -1,7 +1,5 @@ from .base import ModuleTestBase -from bbot.modules import baddns as baddns_module - class BaseTestBaddns(ModuleTestBase): modules_overrides = ["baddns"] @@ -23,6 +21,7 @@ def select_modules(self): class TestBaddns_cname_nxdomain(BaseTestBaddns): async def setup_after_prep(self, module_test): + from bbot.modules import baddns as baddns_module from baddns.lib.whoismanager import WhoisManager mock_data = {"bad.dns": {"CNAME": ["baddns.azurewebsites.net."]}, "_NXDOMAIN": ["baddns.azurewebsites.net"]} @@ -43,6 +42,7 @@ class TestBaddns_cname_signature(BaseTestBaddns): modules_overrides = ["baddns", "speculate"] async def setup_after_prep(self, module_test): + from bbot.modules import baddns as baddns_module from baddns.base import BadDNS_base from baddns.lib.whoismanager import WhoisManager From f824ac5bc06fd96bb0eb85f7780abba4b70b5f63 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Mon, 29 Jan 2024 15:31:40 -0500 Subject: [PATCH 12/35] removing deprecated modules --- bbot/modules/dnszonetransfer.py | 70 --------- bbot/modules/nsec.py | 46 ------ bbot/modules/subdomain_hijack.py | 137 ------------------ .../test_module_dnszonetransfer.py | 43 ------ .../module_tests/test_module_nsec.py | 25 ---- .../test_module_subdomain_hijack.py | 24 --- 6 files changed, 345 deletions(-) delete mode 100644 bbot/modules/dnszonetransfer.py delete mode 100644 bbot/modules/nsec.py delete mode 100644 bbot/modules/subdomain_hijack.py delete mode 100644 bbot/test/test_step_2/module_tests/test_module_dnszonetransfer.py delete mode 100644 bbot/test/test_step_2/module_tests/test_module_nsec.py delete mode 100644 bbot/test/test_step_2/module_tests/test_module_subdomain_hijack.py diff --git a/bbot/modules/dnszonetransfer.py b/bbot/modules/dnszonetransfer.py deleted file mode 100644 index 0a48526dc..000000000 --- a/bbot/modules/dnszonetransfer.py +++ /dev/null @@ -1,70 +0,0 @@ -import dns.zone -import dns.query - -from bbot.modules.base import BaseModule - - -class dnszonetransfer(BaseModule): - flags = ["subdomain-enum", "active", "safe"] - watched_events = ["DNS_NAME"] - produced_events = ["DNS_NAME"] - meta = {"description": "Attempt DNS zone transfers"} - options = {"timeout": 10} - options_desc = {"timeout": "Max seconds to wait before timing out"} - _max_event_handlers = 5 - suppress_dupes = False - - async def setup(self): - self.timeout = self.config.get("timeout", 10) - return True - - async def filter_event(self, event): - if any([x in event.tags for x in ("ns-record", "soa-record")]): - return True - return False - - async def handle_event(self, event): - domain = event.data - self.debug("Finding nameservers with NS/SOA query") - nameservers = list(await self.helpers.resolve(event.data, type=("NS", "SOA"))) - nameserver_ips = set() - for n in nameservers: - nameserver_ips.update(await self.helpers.resolve(n)) - self.debug(f"Found {len(nameservers):} nameservers for domain {domain}") - for nameserver in nameserver_ips: - if self.scan.stopping: - break - try: - self.debug(f"Attempting zone transfer against {nameserver} for domain {domain}") - zone = await self.scan.run_in_executor(self.zone_transfer, nameserver, domain) - except Exception as e: - self.debug(f"Error retrieving zone for {domain}: {e}") - continue - self.hugesuccess(f"Successful zone transfer against {nameserver} for domain {domain}!") - finding_description = f"Successful DNS zone transfer against {nameserver} for {domain}" - await self.emit_event( - {"host": str(event.host), "description": finding_description}, "FINDING", source=event - ) - for name, ttl, rdata in zone.iterate_rdatas(): - if str(name) == "@": - parent_data = domain - else: - parent_data = f"{name}.{domain}" - parent_event = self.make_event(parent_data, "DNS_NAME", event) - if not parent_event or parent_event == event: - parent_event = event - else: - await self.emit_event(parent_event) - for rdtype, t in self.helpers.dns.extract_targets(rdata): - if not self.helpers.is_ip(t): - t = f"{t}.{domain}" - module = self.helpers.dns._get_dummy_module(rdtype) - child_event = self.scan.make_event(t, "DNS_NAME", parent_event, module=module) - await self.emit_event(child_event) - else: - self.debug(f"No data returned by {nameserver} for domain {domain}") - - def zone_transfer(self, nameserver, domain): - xfr_answer = dns.query.xfr(nameserver, domain, timeout=self.timeout, lifetime=self.timeout) - zone = dns.zone.from_xfr(xfr_answer) - return zone diff --git a/bbot/modules/nsec.py b/bbot/modules/nsec.py deleted file mode 100644 index 7d313c140..000000000 --- a/bbot/modules/nsec.py +++ /dev/null @@ -1,46 +0,0 @@ -from bbot.modules.base import BaseModule - - -class NSEC(BaseModule): - watched_events = ["DNS_NAME"] - produced_events = ["DNS_NAME"] - flags = ["subdomain-enum", "passive", "safe"] - meta = {"description": "Enumerate subdomains by NSEC-walking"} - _max_event_handlers = 5 - - async def filter_event(self, event): - if "ns-record" in event.tags: - return True - return False - - async def handle_event(self, event): - emitted_finding = False - async for result in self.nsec_walk(event.data): - if not emitted_finding: - emitted_finding = True - await self.emit_event( - {"host": event.data, "description": f"DNSSEC NSEC Zone Walking Enabled for domain: {event.data}"}, - "FINDING", - source=event, - ) - await self.emit_event(result, "DNS_NAME", source=event) - - async def get_nsec_record(self, domain): - domain = domain.replace("\\000.", "") - try: - for result in await self.helpers.resolve(domain, type="NSEC"): - return str(result) - except Exception as e: - self.warning(f"Error getting NSEC record for {domain}: {e}") - - async def nsec_walk(self, domain): - encountered = set() - current_domain = domain - while 1: - next_domain = await self.get_nsec_record(current_domain) - if next_domain is None or next_domain in encountered: - break - encountered.add(next_domain) - if not next_domain.startswith("\\"): - yield next_domain - current_domain = next_domain diff --git a/bbot/modules/subdomain_hijack.py b/bbot/modules/subdomain_hijack.py deleted file mode 100644 index 8c2611065..000000000 --- a/bbot/modules/subdomain_hijack.py +++ /dev/null @@ -1,137 +0,0 @@ -import re -import json -import httpx - -from bbot.modules.base import BaseModule -from bbot.core.helpers.misc import tldextract - - -class subdomain_hijack(BaseModule): - flags = ["subdomain-hijack", "subdomain-enum", "cloud-enum", "safe", "active", "web-basic", "web-thorough"] - watched_events = ["DNS_NAME", "DNS_NAME_UNRESOLVED"] - produced_events = ["FINDING"] - meta = {"description": "Detect hijackable subdomains"} - options = { - "fingerprints": "https://raw.githubusercontent.com/EdOverflow/can-i-take-over-xyz/master/fingerprints.json" - } - options_desc = {"fingerprints": "URL or path to fingerprints.json"} - scope_distance_modifier = 3 - _max_event_handlers = 5 - - async def setup(self): - fingerprints_url = self.config.get("fingerprints") - fingerprints_file = await self.helpers.wordlist(fingerprints_url) - with open(fingerprints_file) as f: - fingerprints = json.load(f) - self.fingerprints = [] - for f in fingerprints: - try: - f = Fingerprint(f) - except Exception as e: - self.warning(f"Error instantiating fingerprint: {e}") - continue - if not (f.domains and f.vulnerable and f.fingerprint and f.cicd_pass): - self.debug(f"Skipping fingerprint: {f}") - continue - self.debug(f"Processed fingerprint: {f}") - self.fingerprints.append(f) - if not self.fingerprints: - return None, "No valid fingerprints" - self.debug(f"Successfully processed {len(self.fingerprints):,} fingerprints") - return True - - async def handle_event(self, event): - hijackable, reason = await self.check_subdomain(event) - if hijackable: - source_hosts = [] - e = event - while 1: - host = getattr(e, "host", "") - if host: - if e not in source_hosts: - source_hosts.append(e) - e = e.get_source() - else: - break - - url = f"https://{event.host}" - description = f'Hijackable Subdomain "{event.data}": {reason}' - source_hosts = source_hosts[::-1] - if source_hosts: - source_hosts_str = str(source_hosts[0].host) - for e in source_hosts[1:]: - source_hosts_str += f" -[{e.module.name}]-> {e.host}" - description += f" ({source_hosts_str})" - await self.emit_event( - {"host": event.host, "url": url, "description": description}, "FINDING", source=event - ) - else: - self.debug(reason) - - async def check_subdomain(self, event): - for f in self.fingerprints: - for domain in f.domains: - self_matches = self.helpers.host_in_host(event.data, domain) - child_matches = any(self.helpers.host_in_host(domain, h) for h in event.resolved_hosts) - if event.type == "DNS_NAME_UNRESOLVED": - if self_matches and f.nxdomain: - return True, "NXDOMAIN" - else: - if self_matches or child_matches: - for scheme in ("https", "http"): - if self.scan.stopping: - return False, "Scan cancelled" - # first, try base request - url = f"{scheme}://{event.data}" - match, reason = await self._verify_fingerprint(f, url, cache_for=60 * 60 * 24) - if match: - return match, reason - # next, try subdomain -[CNAME]-> other_domain - url = f"{scheme}://{domain}" - headers = {"Host": event.data} - match, reason = await self._verify_fingerprint(f, url, headers=headers) - if match: - return match, reason - return False, f'Subdomain "{event.data}" not hijackable' - - async def _verify_fingerprint(self, fingerprint, *args, **kwargs): - kwargs["raise_error"] = True - kwargs["timeout"] = 10 - kwargs["retries"] = 0 - if fingerprint.http_status is not None: - kwargs["allow_redirects"] = False - try: - r = await self.helpers.request(*args, **kwargs) - if fingerprint.http_status is not None and r.status_code == fingerprint.http_status: - return True, f"HTTP status == {fingerprint.http_status}" - text = getattr(r, "text", "") - if ( - not fingerprint.nxdomain - and not fingerprint.http_status - and fingerprint.fingerprint_regex.findall(text) - ): - return True, "Fingerprint match" - except httpx.RequestError as e: - if fingerprint.nxdomain and "Name or service not known" in str(e): - return True, f"NXDOMAIN" - return False, "No match" - - -class Fingerprint: - def __init__(self, fingerprint): - assert isinstance(fingerprint, dict), "fingerprint must be a dictionary" - self.engine = fingerprint.get("service") - self.cnames = fingerprint.get("cname", []) - self.domains = list(set([tldextract(c).registered_domain for c in self.cnames])) - self.http_status = fingerprint.get("http_status", None) - self.nxdomain = fingerprint.get("nxdomain", False) - self.vulnerable = fingerprint.get("vulnerable", False) - self.fingerprint = fingerprint.get("fingerprint", "") - self.cicd_pass = fingerprint.get("cicd_pass", False) - try: - self.fingerprint_regex = re.compile(self.fingerprint, re.MULTILINE) - except re.error: - self.fingerprint_regex = re.compile(re.escape(self.fingerprint), re.MULTILINE) - - def __str__(self): - return f"{self.engine}: {self.fingerprint} (cnames: {self.cnames}, vulnerable: {self.vulnerable}, cicd_pass: {self.cicd_pass})" diff --git a/bbot/test/test_step_2/module_tests/test_module_dnszonetransfer.py b/bbot/test/test_step_2/module_tests/test_module_dnszonetransfer.py deleted file mode 100644 index c67d46f86..000000000 --- a/bbot/test/test_step_2/module_tests/test_module_dnszonetransfer.py +++ /dev/null @@ -1,43 +0,0 @@ -import dns.zone -import dns.query -import dns.message - -from .base import ModuleTestBase - - -class TestDNSZoneTransfer(ModuleTestBase): - targets = ["blacklanternsecurity.fakedomain"] - config_overrides = {"dns_resolution": True} - - async def setup_after_prep(self, module_test): - old_resolve_fn = module_test.scan.helpers.dns._resolve_hostname - - async def _resolve_hostname(query, **kwargs): - if query == "blacklanternsecurity.fakedomain" and kwargs.get("rdtype", "").upper() == "NS": - return [module_test.mock_record("ns01.blacklanternsecurity.fakedomain", "NS")], [] - if query == "ns01.blacklanternsecurity.fakedomain" and kwargs.get("rdtype", "").upper() == "A": - return [module_test.mock_record("127.0.0.1", "A")], [] - return await old_resolve_fn(query, **kwargs) - - def from_xfr(*args, **kwargs): - zone_text = """ -@ 600 IN SOA ns.blacklanternsecurity.fakedomain. admin.blacklanternsecurity.fakedomain. ( - 1 ; Serial - 3600 ; Refresh - 900 ; Retry - 604800 ; Expire - 86400 ) ; Minimum TTL -@ 600 IN NS ns.blacklanternsecurity.fakedomain. -@ 600 IN A 127.0.0.1 -asdf 600 IN A 127.0.0.1 -zzzz 600 IN AAAA dead::beef -""" - zone = dns.zone.from_text(zone_text, origin="blacklanternsecurity.fakedomain.") - return zone - - module_test.monkeypatch.setattr("dns.zone.from_xfr", from_xfr) - module_test.monkeypatch.setattr(module_test.scan.helpers.dns, "_resolve_hostname", _resolve_hostname) - - def check(self, module_test, events): - assert any(e.data == "asdf.blacklanternsecurity.fakedomain" for e in events), "Zone transfer failed" - assert any(e.data == "zzzz.blacklanternsecurity.fakedomain" for e in events), "Zone transfer failed" diff --git a/bbot/test/test_step_2/module_tests/test_module_nsec.py b/bbot/test/test_step_2/module_tests/test_module_nsec.py deleted file mode 100644 index 595601f39..000000000 --- a/bbot/test/test_step_2/module_tests/test_module_nsec.py +++ /dev/null @@ -1,25 +0,0 @@ -from .base import ModuleTestBase - - -class TestNSEC(ModuleTestBase): - targets = ["blacklanternsecurity.notreal"] - - async def setup_after_prep(self, module_test): - next(iter(module_test.scan.target.events)).add_tag("ns-record") - - old_resolve_fn = module_test.scan.helpers.dns.resolve - - async def resolve(query, **kwargs): - if query == "blacklanternsecurity.notreal" and kwargs.get("type", "").upper() == "NSEC": - return {"asdf.blacklanternsecurity.notreal"} - elif query == "asdf.blacklanternsecurity.notreal" and kwargs.get("type", "").upper() == "NSEC": - return {"zzzz.blacklanternsecurity.notreal"} - elif query == "zzzz.blacklanternsecurity.notreal" and kwargs.get("type", "").upper() == "NSEC": - return {"blacklanternsecurity.notreal"} - return await old_resolve_fn(query, **kwargs) - - module_test.monkeypatch.setattr(module_test.scan.helpers.dns, "resolve", resolve) - - def check(self, module_test, events): - assert any(e.data == "asdf.blacklanternsecurity.notreal" for e in events), "Failed to detect subdomain #1" - assert any(e.data == "zzzz.blacklanternsecurity.notreal" for e in events), "Failed to detect subdomain #2" diff --git a/bbot/test/test_step_2/module_tests/test_module_subdomain_hijack.py b/bbot/test/test_step_2/module_tests/test_module_subdomain_hijack.py deleted file mode 100644 index 7aa6a6680..000000000 --- a/bbot/test/test_step_2/module_tests/test_module_subdomain_hijack.py +++ /dev/null @@ -1,24 +0,0 @@ -from .base import ModuleTestBase - - -class TestSubdomain_Hijack(ModuleTestBase): - targets = ["http://127.0.0.1:8888"] - modules_overrides = ["httpx", "excavate", "subdomain_hijack"] - - async def setup_after_prep(self, module_test): - fingerprints = module_test.module.fingerprints - assert fingerprints, "No subdomain hijacking fingerprints available" - fingerprint = next(iter(fingerprints)) - rand_string = module_test.scan.helpers.rand_string(length=15, digits=False) - self.rand_subdomain = f"{rand_string}.{next(iter(fingerprint.domains))}" - respond_args = {"response_data": f''} - module_test.set_expect_requests(respond_args=respond_args) - - def check(self, module_test, events): - assert any( - event.type == "FINDING" - and event.data["description"].startswith("Hijackable Subdomain") - and self.rand_subdomain in event.data["description"] - and event.data["host"] == self.rand_subdomain - for event in events - ), f"No hijackable subdomains in {events}" From 55642765a778771fd5fad096a45561eb72e33a69 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Mon, 29 Jan 2024 15:32:11 -0500 Subject: [PATCH 13/35] adjusting baddns meta attributes --- bbot/modules/baddns.py | 4 ++-- bbot/modules/baddns_zone.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bbot/modules/baddns.py b/bbot/modules/baddns.py index 44e432f27..1bd423284 100644 --- a/bbot/modules/baddns.py +++ b/bbot/modules/baddns.py @@ -10,8 +10,8 @@ class baddns(BaseModule): watched_events = ["DNS_NAME", "DNS_NAME_UNRESOLVED"] produced_events = ["FINDING", "VULNERABILITY"] - flags = ["active", "safe", "web-basic", "baddns"] - meta = {"description": "Check subdomains for for subdomain takeovers and other DNS tomfoolery"} + flags = ["active", "safe", "web-basic", "baddns", "cloud-enum"] + meta = {"description": "Check hosts for domain/subdomain takeovers"} options = {"custom_nameservers": [], "only_high_confidence": False} options_desc = { "custom_nameservers": "Force BadDNS to use a list of custom nameservers", diff --git a/bbot/modules/baddns_zone.py b/bbot/modules/baddns_zone.py index 93d555d8c..db9da262f 100644 --- a/bbot/modules/baddns_zone.py +++ b/bbot/modules/baddns_zone.py @@ -10,8 +10,8 @@ class baddns_zone(baddns_module): watched_events = ["DNS_NAME"] produced_events = ["FINDING", "VULNERABILITY"] - flags = ["active", "safe", "web-basic", "baddns"] - meta = {"description": "Check subdomains for for subdomain takeovers and other DNS tomfoolery"} + flags = ["active", "safe", "subdomain-enum", "baddns", "cloud-enum"] + meta = {"description": "Check hosts for DNS zone transfers and NSEC walks"} options = {"custom_nameservers": [], "only_high_confidence": False} options_desc = { "custom_nameservers": "Force BadDNS to use a list of custom nameservers", From 05e20ca5ac1ea762ed6be31f8b46180b00a07e33 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Mon, 29 Jan 2024 15:36:56 -0500 Subject: [PATCH 14/35] you got me Joel --- bbot/core/flags.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bbot/core/flags.py b/bbot/core/flags.py index 63c90fa4c..ee47a0848 100644 --- a/bbot/core/flags.py +++ b/bbot/core/flags.py @@ -2,6 +2,7 @@ "active": "Makes active connections to target systems", "affiliates": "Discovers affiliated hostnames/domains", "aggressive": "Generates a large amount of network traffic", + "baddns": "Runs all modules from the DNS auditing tool BadDNS" "cloud-enum": "Enumerates cloud resources", "deadly": "Highly aggressive", "email-enum": "Enumerates email addresses", From 470be4c673cbb5b48aed81af1d4932db6976f2f8 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Mon, 29 Jan 2024 15:37:32 -0500 Subject: [PATCH 15/35] baddns meta adjustment --- bbot/modules/baddns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/modules/baddns.py b/bbot/modules/baddns.py index 1bd423284..84b6867d8 100644 --- a/bbot/modules/baddns.py +++ b/bbot/modules/baddns.py @@ -10,7 +10,7 @@ class baddns(BaseModule): watched_events = ["DNS_NAME", "DNS_NAME_UNRESOLVED"] produced_events = ["FINDING", "VULNERABILITY"] - flags = ["active", "safe", "web-basic", "baddns", "cloud-enum"] + flags = ["active", "safe", "web-basic", "baddns", "cloud-enum", "subdomain-hijack"] meta = {"description": "Check hosts for domain/subdomain takeovers"} options = {"custom_nameservers": [], "only_high_confidence": False} options_desc = { From 13d9582d3af41047aee2ad0507a908f13509988d Mon Sep 17 00:00:00 2001 From: liquidsec Date: Mon, 29 Jan 2024 15:38:32 -0500 Subject: [PATCH 16/35] typo --- bbot/core/flags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/core/flags.py b/bbot/core/flags.py index ee47a0848..d8dbf8566 100644 --- a/bbot/core/flags.py +++ b/bbot/core/flags.py @@ -2,7 +2,7 @@ "active": "Makes active connections to target systems", "affiliates": "Discovers affiliated hostnames/domains", "aggressive": "Generates a large amount of network traffic", - "baddns": "Runs all modules from the DNS auditing tool BadDNS" + "baddns": "Runs all modules from the DNS auditing tool BadDNS", "cloud-enum": "Enumerates cloud resources", "deadly": "Highly aggressive", "email-enum": "Enumerates email addresses", From 334d3fa9a383e1a4e080c9752bd40b1b2593723c Mon Sep 17 00:00:00 2001 From: liquidsec Date: Mon, 29 Jan 2024 16:18:32 -0500 Subject: [PATCH 17/35] fixing poetry --- poetry.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 61374ea00..1fbf7947e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2403,4 +2403,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "1d40c762ee4808a285f17096c5140f4e6fa7d236a56f97f0415aa81aef917ea1" +content-hash = "1d40c762ee4808a285f17096c5140f4e6fa7d236a56f97f0415aa81aef917ea1" \ No newline at end of file From 8c39aeee450b842103438c7a79fc1ee1e479b6ac Mon Sep 17 00:00:00 2001 From: liquidsec Date: Mon, 29 Jan 2024 17:48:59 -0500 Subject: [PATCH 18/35] forcing version of baddns to fix horrible dependency nightmare --- bbot/modules/baddns.py | 2 +- bbot/modules/baddns_zone.py | 2 +- poetry.lock | 661 ++++++++++++++++++------------------ 3 files changed, 330 insertions(+), 335 deletions(-) diff --git a/bbot/modules/baddns.py b/bbot/modules/baddns.py index 84b6867d8..8a3e6fd90 100644 --- a/bbot/modules/baddns.py +++ b/bbot/modules/baddns.py @@ -18,7 +18,7 @@ class baddns(BaseModule): "only_high_confidence": "Do not emit low-confidence or generic detections", } max_event_handlers = 8 - deps_pip = ["baddns~=1.0.666"] + deps_pip = ["baddns~=1.0.675"] def select_modules(self): selected_modules = [] diff --git a/bbot/modules/baddns_zone.py b/bbot/modules/baddns_zone.py index db9da262f..233ae3918 100644 --- a/bbot/modules/baddns_zone.py +++ b/bbot/modules/baddns_zone.py @@ -18,7 +18,7 @@ class baddns_zone(baddns_module): "only_high_confidence": "Do not emit low-confidence or generic detections", } max_event_handlers = 8 - deps_pip = ["baddns~=1.0.666"] + deps_pip = ["baddns~=1.0.675"] def select_modules(self): selected_modules = [] diff --git a/poetry.lock b/poetry.lock index 1fbf7947e..894b3ccdd 100644 --- a/poetry.lock +++ b/poetry.lock @@ -38,13 +38,13 @@ ansible-core = ">=2.14.7,<2.15.0" [[package]] name = "ansible-core" -version = "2.14.13" +version = "2.14.14" description = "Radically simple IT automation" optional = false python-versions = ">=3.9" files = [ - {file = "ansible-core-2.14.13.tar.gz", hash = "sha256:4e1bb334f0c3226ab48c599efe49cd5fe03f25d4558bc06c274ade2ba3e2576a"}, - {file = "ansible_core-2.14.13-py3-none-any.whl", hash = "sha256:65e96d04dce1e5dd415b4681d464f7f9a949d515f623145c4a8bc3468e75f3b0"}, + {file = "ansible-core-2.14.14.tar.gz", hash = "sha256:f06a94a88a372d4db4b3973e465022fbe3545602580864115d21a280accb7ca3"}, + {file = "ansible_core-2.14.14-py3-none-any.whl", hash = "sha256:d1d282b71b9d8fdd515ae045e5909cfa393cfa0e9fecaae2dbbb4d326ab58681"}, ] [package.dependencies] @@ -121,19 +121,22 @@ dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] [[package]] name = "beautifulsoup4" -version = "4.12.2" +version = "4.12.3" description = "Screen-scraping library" optional = false python-versions = ">=3.6.0" files = [ - {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"}, - {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"}, + {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, + {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, ] [package.dependencies] soupsieve = ">1.2" [package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] @@ -384,13 +387,13 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "cloudcheck" -version = "2.1.0.181" +version = "2.2.0.203" description = "Check whether an IP address belongs to a cloud provider" optional = false python-versions = ">=3.9,<4.0" files = [ - {file = "cloudcheck-2.1.0.181-py3-none-any.whl", hash = "sha256:cb9ebd5d546038974ed13bd7c9edc83fe481af791bf53decf7ecdd9070deb23a"}, - {file = "cloudcheck-2.1.0.181.tar.gz", hash = "sha256:c3b24c72b3f082f87a81bf8542dc2a5177593fe4ea0e983acd2f486a5d127cc9"}, + {file = "cloudcheck-2.2.0.203-py3-none-any.whl", hash = "sha256:8f17944c6183c7ad96b031c4cc3efb1c1addaa23c2d17f3c81ff54c4cd494716"}, + {file = "cloudcheck-2.2.0.203.tar.gz", hash = "sha256:83eb239b024579712a19d736dc9649eff4a3f6e9314db3cd94faa883adbfe46c"}, ] [package.dependencies] @@ -410,63 +413,63 @@ files = [ [[package]] name = "coverage" -version = "7.4.0" +version = "7.4.1" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36b0ea8ab20d6a7564e89cb6135920bc9188fb5f1f7152e94e8300b7b189441a"}, - {file = "coverage-7.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0676cd0ba581e514b7f726495ea75aba3eb20899d824636c6f59b0ed2f88c471"}, - {file = "coverage-7.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0ca5c71a5a1765a0f8f88022c52b6b8be740e512980362f7fdbb03725a0d6b9"}, - {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7c97726520f784239f6c62506bc70e48d01ae71e9da128259d61ca5e9788516"}, - {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:815ac2d0f3398a14286dc2cea223a6f338109f9ecf39a71160cd1628786bc6f5"}, - {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:80b5ee39b7f0131ebec7968baa9b2309eddb35b8403d1869e08f024efd883566"}, - {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5b2ccb7548a0b65974860a78c9ffe1173cfb5877460e5a229238d985565574ae"}, - {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:995ea5c48c4ebfd898eacb098164b3cc826ba273b3049e4a889658548e321b43"}, - {file = "coverage-7.4.0-cp310-cp310-win32.whl", hash = "sha256:79287fd95585ed36e83182794a57a46aeae0b64ca53929d1176db56aacc83451"}, - {file = "coverage-7.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:5b14b4f8760006bfdb6e08667af7bc2d8d9bfdb648351915315ea17645347137"}, - {file = "coverage-7.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04387a4a6ecb330c1878907ce0dc04078ea72a869263e53c72a1ba5bbdf380ca"}, - {file = "coverage-7.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea81d8f9691bb53f4fb4db603203029643caffc82bf998ab5b59ca05560f4c06"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74775198b702868ec2d058cb92720a3c5a9177296f75bd97317c787daf711505"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76f03940f9973bfaee8cfba70ac991825611b9aac047e5c80d499a44079ec0bc"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:485e9f897cf4856a65a57c7f6ea3dc0d4e6c076c87311d4bc003f82cfe199d25"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6ae8c9d301207e6856865867d762a4b6fd379c714fcc0607a84b92ee63feff70"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bf477c355274a72435ceb140dc42de0dc1e1e0bf6e97195be30487d8eaaf1a09"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:83c2dda2666fe32332f8e87481eed056c8b4d163fe18ecc690b02802d36a4d26"}, - {file = "coverage-7.4.0-cp311-cp311-win32.whl", hash = "sha256:697d1317e5290a313ef0d369650cfee1a114abb6021fa239ca12b4849ebbd614"}, - {file = "coverage-7.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:26776ff6c711d9d835557ee453082025d871e30b3fd6c27fcef14733f67f0590"}, - {file = "coverage-7.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:13eaf476ec3e883fe3e5fe3707caeb88268a06284484a3daf8250259ef1ba143"}, - {file = "coverage-7.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846f52f46e212affb5bcf131c952fb4075b55aae6b61adc9856222df89cbe3e2"}, - {file = "coverage-7.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26f66da8695719ccf90e794ed567a1549bb2644a706b41e9f6eae6816b398c4a"}, - {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:164fdcc3246c69a6526a59b744b62e303039a81e42cfbbdc171c91a8cc2f9446"}, - {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:316543f71025a6565677d84bc4df2114e9b6a615aa39fb165d697dba06a54af9"}, - {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bb1de682da0b824411e00a0d4da5a784ec6496b6850fdf8c865c1d68c0e318dd"}, - {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0e8d06778e8fbffccfe96331a3946237f87b1e1d359d7fbe8b06b96c95a5407a"}, - {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a56de34db7b7ff77056a37aedded01b2b98b508227d2d0979d373a9b5d353daa"}, - {file = "coverage-7.4.0-cp312-cp312-win32.whl", hash = "sha256:51456e6fa099a8d9d91497202d9563a320513fcf59f33991b0661a4a6f2ad450"}, - {file = "coverage-7.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:cd3c1e4cb2ff0083758f09be0f77402e1bdf704adb7f89108007300a6da587d0"}, - {file = "coverage-7.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9d1bf53c4c8de58d22e0e956a79a5b37f754ed1ffdbf1a260d9dcfa2d8a325e"}, - {file = "coverage-7.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:109f5985182b6b81fe33323ab4707011875198c41964f014579cf82cebf2bb85"}, - {file = "coverage-7.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc9d4bc55de8003663ec94c2f215d12d42ceea128da8f0f4036235a119c88ac"}, - {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc6d65b21c219ec2072c1293c505cf36e4e913a3f936d80028993dd73c7906b1"}, - {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a10a4920def78bbfff4eff8a05c51be03e42f1c3735be42d851f199144897ba"}, - {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b8e99f06160602bc64da35158bb76c73522a4010f0649be44a4e167ff8555952"}, - {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7d360587e64d006402b7116623cebf9d48893329ef035278969fa3bbf75b697e"}, - {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29f3abe810930311c0b5d1a7140f6395369c3db1be68345638c33eec07535105"}, - {file = "coverage-7.4.0-cp38-cp38-win32.whl", hash = "sha256:5040148f4ec43644702e7b16ca864c5314ccb8ee0751ef617d49aa0e2d6bf4f2"}, - {file = "coverage-7.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:9864463c1c2f9cb3b5db2cf1ff475eed2f0b4285c2aaf4d357b69959941aa555"}, - {file = "coverage-7.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:936d38794044b26c99d3dd004d8af0035ac535b92090f7f2bb5aa9c8e2f5cd42"}, - {file = "coverage-7.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:799c8f873794a08cdf216aa5d0531c6a3747793b70c53f70e98259720a6fe2d7"}, - {file = "coverage-7.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7defbb9737274023e2d7af02cac77043c86ce88a907c58f42b580a97d5bcca9"}, - {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1526d265743fb49363974b7aa8d5899ff64ee07df47dd8d3e37dcc0818f09ed"}, - {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf635a52fc1ea401baf88843ae8708591aa4adff875e5c23220de43b1ccf575c"}, - {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:756ded44f47f330666843b5781be126ab57bb57c22adbb07d83f6b519783b870"}, - {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0eb3c2f32dabe3a4aaf6441dde94f35687224dfd7eb2a7f47f3fd9428e421058"}, - {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bfd5db349d15c08311702611f3dccbef4b4e2ec148fcc636cf8739519b4a5c0f"}, - {file = "coverage-7.4.0-cp39-cp39-win32.whl", hash = "sha256:53d7d9158ee03956e0eadac38dfa1ec8068431ef8058fe6447043db1fb40d932"}, - {file = "coverage-7.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfd2a8b6b0d8e66e944d47cdec2f47c48fef2ba2f2dff5a9a75757f64172857e"}, - {file = "coverage-7.4.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:c530833afc4707fe48524a44844493f36d8727f04dcce91fb978c414a8556cc6"}, - {file = "coverage-7.4.0.tar.gz", hash = "sha256:707c0f58cb1712b8809ece32b68996ee1e609f71bd14615bd8f87a1293cb610e"}, + {file = "coverage-7.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:077d366e724f24fc02dbfe9d946534357fda71af9764ff99d73c3c596001bbd7"}, + {file = "coverage-7.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0193657651f5399d433c92f8ae264aff31fc1d066deee4b831549526433f3f61"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d17bbc946f52ca67adf72a5ee783cd7cd3477f8f8796f59b4974a9b59cacc9ee"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3277f5fa7483c927fe3a7b017b39351610265308f5267ac6d4c2b64cc1d8d25"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dceb61d40cbfcf45f51e59933c784a50846dc03211054bd76b421a713dcdf19"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6008adeca04a445ea6ef31b2cbaf1d01d02986047606f7da266629afee982630"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c61f66d93d712f6e03369b6a7769233bfda880b12f417eefdd4f16d1deb2fc4c"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9bb62fac84d5f2ff523304e59e5c439955fb3b7f44e3d7b2085184db74d733b"}, + {file = "coverage-7.4.1-cp310-cp310-win32.whl", hash = "sha256:f86f368e1c7ce897bf2457b9eb61169a44e2ef797099fb5728482b8d69f3f016"}, + {file = "coverage-7.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:869b5046d41abfea3e381dd143407b0d29b8282a904a19cb908fa24d090cc018"}, + {file = "coverage-7.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b8ffb498a83d7e0305968289441914154fb0ef5d8b3157df02a90c6695978295"}, + {file = "coverage-7.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3cacfaefe6089d477264001f90f55b7881ba615953414999c46cc9713ff93c8c"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d6850e6e36e332d5511a48a251790ddc545e16e8beaf046c03985c69ccb2676"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18e961aa13b6d47f758cc5879383d27b5b3f3dcd9ce8cdbfdc2571fe86feb4dd"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfd1e1b9f0898817babf840b77ce9fe655ecbe8b1b327983df485b30df8cc011"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6b00e21f86598b6330f0019b40fb397e705135040dbedc2ca9a93c7441178e74"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:536d609c6963c50055bab766d9951b6c394759190d03311f3e9fcf194ca909e1"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7ac8f8eb153724f84885a1374999b7e45734bf93a87d8df1e7ce2146860edef6"}, + {file = "coverage-7.4.1-cp311-cp311-win32.whl", hash = "sha256:f3771b23bb3675a06f5d885c3630b1d01ea6cac9e84a01aaf5508706dba546c5"}, + {file = "coverage-7.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:9d2f9d4cc2a53b38cabc2d6d80f7f9b7e3da26b2f53d48f05876fef7956b6968"}, + {file = "coverage-7.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f68ef3660677e6624c8cace943e4765545f8191313a07288a53d3da188bd8581"}, + {file = "coverage-7.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23b27b8a698e749b61809fb637eb98ebf0e505710ec46a8aa6f1be7dc0dc43a6"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e3424c554391dc9ef4a92ad28665756566a28fecf47308f91841f6c49288e66"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0860a348bf7004c812c8368d1fc7f77fe8e4c095d661a579196a9533778e156"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe558371c1bdf3b8fa03e097c523fb9645b8730399c14fe7721ee9c9e2a545d3"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3468cc8720402af37b6c6e7e2a9cdb9f6c16c728638a2ebc768ba1ef6f26c3a1"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:02f2edb575d62172aa28fe00efe821ae31f25dc3d589055b3fb64d51e52e4ab1"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ca6e61dc52f601d1d224526360cdeab0d0712ec104a2ce6cc5ccef6ed9a233bc"}, + {file = "coverage-7.4.1-cp312-cp312-win32.whl", hash = "sha256:ca7b26a5e456a843b9b6683eada193fc1f65c761b3a473941efe5a291f604c74"}, + {file = "coverage-7.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:85ccc5fa54c2ed64bd91ed3b4a627b9cce04646a659512a051fa82a92c04a448"}, + {file = "coverage-7.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8bdb0285a0202888d19ec6b6d23d5990410decb932b709f2b0dfe216d031d218"}, + {file = "coverage-7.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:918440dea04521f499721c039863ef95433314b1db00ff826a02580c1f503e45"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:379d4c7abad5afbe9d88cc31ea8ca262296480a86af945b08214eb1a556a3e4d"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b094116f0b6155e36a304ff912f89bbb5067157aff5f94060ff20bbabdc8da06"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2f5968608b1fe2a1d00d01ad1017ee27efd99b3437e08b83ded9b7af3f6f766"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:10e88e7f41e6197ea0429ae18f21ff521d4f4490aa33048f6c6f94c6045a6a75"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a4a3907011d39dbc3e37bdc5df0a8c93853c369039b59efa33a7b6669de04c60"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d224f0c4c9c98290a6990259073f496fcec1b5cc613eecbd22786d398ded3ad"}, + {file = "coverage-7.4.1-cp38-cp38-win32.whl", hash = "sha256:23f5881362dcb0e1a92b84b3c2809bdc90db892332daab81ad8f642d8ed55042"}, + {file = "coverage-7.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:a07f61fc452c43cd5328b392e52555f7d1952400a1ad09086c4a8addccbd138d"}, + {file = "coverage-7.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8e738a492b6221f8dcf281b67129510835461132b03024830ac0e554311a5c54"}, + {file = "coverage-7.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46342fed0fff72efcda77040b14728049200cbba1279e0bf1188f1f2078c1d70"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9641e21670c68c7e57d2053ddf6c443e4f0a6e18e547e86af3fad0795414a628"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aeb2c2688ed93b027eb0d26aa188ada34acb22dceea256d76390eea135083950"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d12c923757de24e4e2110cf8832d83a886a4cf215c6e61ed506006872b43a6d1"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0491275c3b9971cdbd28a4595c2cb5838f08036bca31765bad5e17edf900b2c7"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8dfc5e195bbef80aabd81596ef52a1277ee7143fe419efc3c4d8ba2754671756"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1a78b656a4d12b0490ca72651fe4d9f5e07e3c6461063a9b6265ee45eb2bdd35"}, + {file = "coverage-7.4.1-cp39-cp39-win32.whl", hash = "sha256:f90515974b39f4dea2f27c0959688621b46d96d5a626cf9c53dbc653a895c05c"}, + {file = "coverage-7.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:64e723ca82a84053dd7bfcc986bdb34af8d9da83c521c19d6b472bc6880e191a"}, + {file = "coverage-7.4.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:32a8d985462e37cfdab611a6f95b09d7c091d07668fdc26e47a725ee575fe166"}, + {file = "coverage-7.4.1.tar.gz", hash = "sha256:1ed4b95480952b1a26d863e546fa5094564aa0065e1e5f0d4d0041f293251d04"}, ] [package.dependencies] @@ -477,47 +480,56 @@ toml = ["tomli"] [[package]] name = "cryptography" -version = "41.0.7" +version = "42.0.1" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-41.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf"}, - {file = "cryptography-41.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d"}, - {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a"}, - {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15"}, - {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a"}, - {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1"}, - {file = "cryptography-41.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157"}, - {file = "cryptography-41.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406"}, - {file = "cryptography-41.0.7-cp37-abi3-win32.whl", hash = "sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d"}, - {file = "cryptography-41.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2"}, - {file = "cryptography-41.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960"}, - {file = "cryptography-41.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003"}, - {file = "cryptography-41.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7"}, - {file = "cryptography-41.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec"}, - {file = "cryptography-41.0.7-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be"}, - {file = "cryptography-41.0.7-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a"}, - {file = "cryptography-41.0.7-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c"}, - {file = "cryptography-41.0.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a"}, - {file = "cryptography-41.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39"}, - {file = "cryptography-41.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a"}, - {file = "cryptography-41.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248"}, - {file = "cryptography-41.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309"}, - {file = "cryptography-41.0.7.tar.gz", hash = "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc"}, + {file = "cryptography-42.0.1-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:265bdc693570b895eb641410b8fc9e8ddbce723a669236162b9d9cfb70bd8d77"}, + {file = "cryptography-42.0.1-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:160fa08dfa6dca9cb8ad9bd84e080c0db6414ba5ad9a7470bc60fb154f60111e"}, + {file = "cryptography-42.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:727387886c9c8de927c360a396c5edcb9340d9e960cda145fca75bdafdabd24c"}, + {file = "cryptography-42.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d84673c012aa698555d4710dcfe5f8a0ad76ea9dde8ef803128cc669640a2e0"}, + {file = "cryptography-42.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e6edc3a568667daf7d349d7e820783426ee4f1c0feab86c29bd1d6fe2755e009"}, + {file = "cryptography-42.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:d50718dd574a49d3ef3f7ef7ece66ef281b527951eb2267ce570425459f6a404"}, + {file = "cryptography-42.0.1-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:9544492e8024f29919eac2117edd8c950165e74eb551a22c53f6fdf6ba5f4cb8"}, + {file = "cryptography-42.0.1-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ab6b302d51fbb1dd339abc6f139a480de14d49d50f65fdc7dff782aa8631d035"}, + {file = "cryptography-42.0.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2fe16624637d6e3e765530bc55caa786ff2cbca67371d306e5d0a72e7c3d0407"}, + {file = "cryptography-42.0.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ed1b2130f5456a09a134cc505a17fc2830a1a48ed53efd37dcc904a23d7b82fa"}, + {file = "cryptography-42.0.1-cp37-abi3-win32.whl", hash = "sha256:e5edf189431b4d51f5c6fb4a95084a75cef6b4646c934eb6e32304fc720e1453"}, + {file = "cryptography-42.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:6bfd823b336fdcd8e06285ae8883d3d2624d3bdef312a0e2ef905f332f8e9302"}, + {file = "cryptography-42.0.1-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:351db02c1938c8e6b1fee8a78d6b15c5ccceca7a36b5ce48390479143da3b411"}, + {file = "cryptography-42.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:430100abed6d3652208ae1dd410c8396213baee2e01a003a4449357db7dc9e14"}, + {file = "cryptography-42.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dff7a32880a51321f5de7869ac9dde6b1fca00fc1fef89d60e93f215468e824"}, + {file = "cryptography-42.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b512f33c6ab195852595187af5440d01bb5f8dd57cb7a91e1e009a17f1b7ebca"}, + {file = "cryptography-42.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:95d900d19a370ae36087cc728e6e7be9c964ffd8cbcb517fd1efb9c9284a6abc"}, + {file = "cryptography-42.0.1-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:6ac8924085ed8287545cba89dc472fc224c10cc634cdf2c3e2866fe868108e77"}, + {file = "cryptography-42.0.1-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cb2861a9364fa27d24832c718150fdbf9ce6781d7dc246a516435f57cfa31fe7"}, + {file = "cryptography-42.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25ec6e9e81de5d39f111a4114193dbd39167cc4bbd31c30471cebedc2a92c323"}, + {file = "cryptography-42.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9d61fcdf37647765086030d81872488e4cb3fafe1d2dda1d487875c3709c0a49"}, + {file = "cryptography-42.0.1-cp39-abi3-win32.whl", hash = "sha256:16b9260d04a0bfc8952b00335ff54f471309d3eb9d7e8dbfe9b0bd9e26e67881"}, + {file = "cryptography-42.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:7911586fc69d06cd0ab3f874a169433db1bc2f0e40988661408ac06c4527a986"}, + {file = "cryptography-42.0.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d3594947d2507d4ef7a180a7f49a6db41f75fb874c2fd0e94f36b89bfd678bf2"}, + {file = "cryptography-42.0.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8d7efb6bf427d2add2f40b6e1e8e476c17508fa8907234775214b153e69c2e11"}, + {file = "cryptography-42.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:126e0ba3cc754b200a2fb88f67d66de0d9b9e94070c5bc548318c8dab6383cb6"}, + {file = "cryptography-42.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:802d6f83233cf9696b59b09eb067e6b4d5ae40942feeb8e13b213c8fad47f1aa"}, + {file = "cryptography-42.0.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0b7cacc142260ada944de070ce810c3e2a438963ee3deb45aa26fd2cee94c9a4"}, + {file = "cryptography-42.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:32ea63ceeae870f1a62e87f9727359174089f7b4b01e4999750827bf10e15d60"}, + {file = "cryptography-42.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d3902c779a92151f134f68e555dd0b17c658e13429f270d8a847399b99235a3f"}, + {file = "cryptography-42.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:50aecd93676bcca78379604ed664c45da82bc1241ffb6f97f6b7392ed5bc6f04"}, + {file = "cryptography-42.0.1.tar.gz", hash = "sha256:fd33f53809bb363cf126bebe7a99d97735988d9b0131a2be59fbf83e1259a5b7"}, ] [package.dependencies] -cffi = ">=1.12" +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} [package.extras] docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] -docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] nox = ["nox"] -pep8test = ["black", "check-sdist", "mypy", "ruff"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] sdist = ["build"] ssh = ["bcrypt (>=3.1.5)"] -test = ["pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] [[package]] @@ -551,22 +563,23 @@ files = [ [[package]] name = "dnspython" -version = "2.4.2" +version = "2.5.0" description = "DNS toolkit" optional = false -python-versions = ">=3.8,<4.0" +python-versions = ">=3.8" files = [ - {file = "dnspython-2.4.2-py3-none-any.whl", hash = "sha256:57c6fbaaeaaf39c891292012060beb141791735dbb4004798328fc2c467402d8"}, - {file = "dnspython-2.4.2.tar.gz", hash = "sha256:8dcfae8c7460a2f84b4072e26f1c9f4101ca20c071649cb7c34e8b6a93d58984"}, + {file = "dnspython-2.5.0-py3-none-any.whl", hash = "sha256:6facdf76b73c742ccf2d07add296f178e629da60be23ce4b0a9c927b1e02c3a6"}, + {file = "dnspython-2.5.0.tar.gz", hash = "sha256:a0034815a59ba9ae888946be7ccca8f7c157b286f8455b379c692efb51022a15"}, ] [package.extras] -dnssec = ["cryptography (>=2.6,<42.0)"] -doh = ["h2 (>=4.1.0)", "httpcore (>=0.17.3)", "httpx (>=0.24.1)"] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=5.0.3)", "mypy (>=1.0.1)", "pylint (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)", "sphinx (>=7.0.0)", "twine (>=4.0.0)", "wheel (>=0.41.0)"] +dnssec = ["cryptography (>=41)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=0.17.3)", "httpx (>=0.25.1)"] doq = ["aioquic (>=0.9.20)"] -idna = ["idna (>=2.1,<4.0)"] -trio = ["trio (>=0.14,<0.23)"] -wmi = ["wmi (>=1.5.1,<2.0.0)"] +idna = ["idna (>=2.1)"] +trio = ["trio (>=0.14)"] +wmi = ["wmi (>=1.5.1)"] [[package]] name = "docutils" @@ -658,13 +671,13 @@ dev = ["flake8", "markdown", "twine", "wheel"] [[package]] name = "griffe" -version = "0.38.1" +version = "0.39.1" description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." optional = false python-versions = ">=3.8" files = [ - {file = "griffe-0.38.1-py3-none-any.whl", hash = "sha256:334c79d3b5964ade65c05dfcaf53518c576dedd387aaba5c9fd71212f34f1483"}, - {file = "griffe-0.38.1.tar.gz", hash = "sha256:bd68d7da7f3d87bc57eb9962b250db123efd9bbcc06c11c1a91b6e583b2a9361"}, + {file = "griffe-0.39.1-py3-none-any.whl", hash = "sha256:6ce4ecffcf0d2f96362c5974b3f7df812da8f8d4cfcc5ebc8202ef72656fc087"}, + {file = "griffe-0.39.1.tar.gz", hash = "sha256:ead8dfede6e6531cce6bf69090a4f3c6d36fdf923c43f8e85aa530552cef0c09"}, ] [package.dependencies] @@ -783,13 +796,13 @@ files = [ [[package]] name = "jinja2" -version = "3.1.2" +version = "3.1.3" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, ] [package.dependencies] @@ -949,13 +962,13 @@ source = ["Cython (==0.29.37)"] [[package]] name = "markdown" -version = "3.5.1" +version = "3.5.2" description = "Python implementation of John Gruber's Markdown." optional = false python-versions = ">=3.8" files = [ - {file = "Markdown-3.5.1-py3-none-any.whl", hash = "sha256:5874b47d4ee3f0b14d764324d2c94c03ea66bee56f2d929da9f2508d65e722dc"}, - {file = "Markdown-3.5.1.tar.gz", hash = "sha256:b65d7beb248dc22f2e8a31fb706d93798093c308dc1aba295aedeb9d41a813bd"}, + {file = "Markdown-3.5.2-py3-none-any.whl", hash = "sha256:d43323865d89fc0cb9b20c75fc8ad313af307cc087e84b657d9eec768eddeadd"}, + {file = "Markdown-3.5.2.tar.gz", hash = "sha256:e1ac7b3dc550ee80e602e71c1d168002f062e49f1b11e26a36264dafd4df2ef8"}, ] [package.dependencies] @@ -967,61 +980,71 @@ testing = ["coverage", "pyyaml"] [[package]] name = "markupsafe" -version = "2.1.3" +version = "2.1.4" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" files = [ - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, - {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de8153a7aae3835484ac168a9a9bdaa0c5eee4e0bc595503c95d53b942879c84"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e888ff76ceb39601c59e219f281466c6d7e66bd375b4ec1ce83bcdc68306796b"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b838c37ba596fcbfca71651a104a611543077156cb0a26fe0c475e1f152ee8"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac1ebf6983148b45b5fa48593950f90ed6d1d26300604f321c74a9ca1609f8e"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbad3d346df8f9d72622ac71b69565e621ada2ce6572f37c2eae8dacd60385d"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5291d98cd3ad9a562883468c690a2a238c4a6388ab3bd155b0c75dd55ece858"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a7cc49ef48a3c7a0005a949f3c04f8baa5409d3f663a1b36f0eba9bfe2a0396e"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83041cda633871572f0d3c41dddd5582ad7d22f65a72eacd8d3d6d00291df26"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-win32.whl", hash = "sha256:0c26f67b3fe27302d3a412b85ef696792c4a2386293c53ba683a89562f9399b0"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-win_amd64.whl", hash = "sha256:a76055d5cb1c23485d7ddae533229039b850db711c554a12ea64a0fd8a0129e2"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e9e3c4020aa2dc62d5dd6743a69e399ce3de58320522948af6140ac959ab863"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0042d6a9880b38e1dd9ff83146cc3c9c18a059b9360ceae207805567aacccc69"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d03fea4c4e9fd0ad75dc2e7e2b6757b80c152c032ea1d1de487461d8140efc"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ab3a886a237f6e9c9f4f7d272067e712cdb4efa774bef494dccad08f39d8ae6"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf5ebbec056817057bfafc0445916bb688a255a5146f900445d081db08cbabb"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e1a0d1924a5013d4f294087e00024ad25668234569289650929ab871231668e7"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e7902211afd0af05fbadcc9a312e4cf10f27b779cf1323e78d52377ae4b72bea"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c669391319973e49a7c6230c218a1e3044710bc1ce4c8e6eb71f7e6d43a2c131"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-win32.whl", hash = "sha256:31f57d64c336b8ccb1966d156932f3daa4fee74176b0fdc48ef580be774aae74"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:54a7e1380dfece8847c71bf7e33da5d084e9b889c75eca19100ef98027bd9f56"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a76cd37d229fc385738bd1ce4cba2a121cf26b53864c1772694ad0ad348e509e"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:987d13fe1d23e12a66ca2073b8d2e2a75cec2ecb8eab43ff5624ba0ad42764bc"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5244324676254697fe5c181fc762284e2c5fceeb1c4e3e7f6aca2b6f107e60dc"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78bc995e004681246e85e28e068111a4c3f35f34e6c62da1471e844ee1446250"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4d176cfdfde84f732c4a53109b293d05883e952bbba68b857ae446fa3119b4f"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f9917691f410a2e0897d1ef99619fd3f7dd503647c8ff2475bf90c3cf222ad74"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f06e5a9e99b7df44640767842f414ed5d7bedaaa78cd817ce04bbd6fd86e2dd6"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:396549cea79e8ca4ba65525470d534e8a41070e6b3500ce2414921099cb73e8d"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-win32.whl", hash = "sha256:f6be2d708a9d0e9b0054856f07ac7070fbe1754be40ca8525d5adccdbda8f475"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:5045e892cfdaecc5b4c01822f353cf2c8feb88a6ec1c0adef2a2e705eef0f656"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a07f40ef8f0fbc5ef1000d0c78771f4d5ca03b4953fc162749772916b298fc4"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d18b66fe626ac412d96c2ab536306c736c66cf2a31c243a45025156cc190dc8a"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:698e84142f3f884114ea8cf83e7a67ca8f4ace8454e78fe960646c6c91c63bfa"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a3b78a5af63ec10d8604180380c13dcd870aba7928c1fe04e881d5c792dc4e"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:15866d7f2dc60cfdde12ebb4e75e41be862348b4728300c36cdf405e258415ec"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6aa5e2e7fc9bc042ae82d8b79d795b9a62bd8f15ba1e7594e3db243f158b5565"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:54635102ba3cf5da26eb6f96c4b8c53af8a9c0d97b64bdcb592596a6255d8518"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-win32.whl", hash = "sha256:3583a3a3ab7958e354dc1d25be74aee6228938312ee875a22330c4dc2e41beb0"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-win_amd64.whl", hash = "sha256:d6e427c7378c7f1b2bef6a344c925b8b63623d3321c09a237b7cc0e77dd98ceb"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bf1196dcc239e608605b716e7b166eb5faf4bc192f8a44b81e85251e62584bd2"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4df98d4a9cd6a88d6a585852f56f2155c9cdb6aec78361a19f938810aa020954"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b835aba863195269ea358cecc21b400276747cc977492319fd7682b8cd2c253d"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23984d1bdae01bee794267424af55eef4dfc038dc5d1272860669b2aa025c9e3"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c98c33ffe20e9a489145d97070a435ea0679fddaabcafe19982fe9c971987d5"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9896fca4a8eb246defc8b2a7ac77ef7553b638e04fbf170bff78a40fa8a91474"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b0fe73bac2fed83839dbdbe6da84ae2a31c11cfc1c777a40dbd8ac8a6ed1560f"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c7556bafeaa0a50e2fe7dc86e0382dea349ebcad8f010d5a7dc6ba568eaaa789"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-win32.whl", hash = "sha256:fc1a75aa8f11b87910ffd98de62b29d6520b6d6e8a3de69a70ca34dea85d2a8a"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-win_amd64.whl", hash = "sha256:3a66c36a3864df95e4f62f9167c734b3b1192cb0851b43d7cc08040c074c6279"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:765f036a3d00395a326df2835d8f86b637dbaf9832f90f5d196c3b8a7a5080cb"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21e7af8091007bf4bebf4521184f4880a6acab8df0df52ef9e513d8e5db23411"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c31fe855c77cad679b302aabc42d724ed87c043b1432d457f4976add1c2c3e"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7653fa39578957bc42e5ebc15cf4361d9e0ee4b702d7d5ec96cdac860953c5b4"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47bb5f0142b8b64ed1399b6b60f700a580335c8e1c57f2f15587bd072012decc"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fe8512ed897d5daf089e5bd010c3dc03bb1bdae00b35588c49b98268d4a01e00"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:36d7626a8cca4d34216875aee5a1d3d654bb3dac201c1c003d182283e3205949"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b6f14a9cd50c3cb100eb94b3273131c80d102e19bb20253ac7bd7336118a673a"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-win32.whl", hash = "sha256:c8f253a84dbd2c63c19590fa86a032ef3d8cc18923b8049d91bcdeeb2581fbf6"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-win_amd64.whl", hash = "sha256:8b570a1537367b52396e53325769608f2a687ec9a4363647af1cded8928af959"}, + {file = "MarkupSafe-2.1.4.tar.gz", hash = "sha256:3aae9af4cac263007fd6309c64c6ab4506dd2b79382d9d19a1994f9240b8db4f"}, ] [[package]] @@ -1110,13 +1133,13 @@ mkdocs = ">=1.1" [[package]] name = "mkdocs-material" -version = "9.5.3" +version = "9.5.6" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.3-py3-none-any.whl", hash = "sha256:76c93a8525cceb0b395b9cedab3428bf518cf6439adef2b940f1c1574b775d89"}, - {file = "mkdocs_material-9.5.3.tar.gz", hash = "sha256:5899219f422f0a6de784232d9d40374416302ffae3c160cacc72969fcc1ee372"}, + {file = "mkdocs_material-9.5.6-py3-none-any.whl", hash = "sha256:e115b90fccf5cd7f5d15b0c2f8e6246b21041628b8f590630e7fca66ed7fcf6c"}, + {file = "mkdocs_material-9.5.6.tar.gz", hash = "sha256:5b24df36d8ac6cecd611241ce6f6423ccde3e1ad89f8360c3f76d5565fc2d82a"}, ] [package.dependencies] @@ -1134,7 +1157,7 @@ requests = ">=2.26,<3.0" [package.extras] git = ["mkdocs-git-committers-plugin-2 (>=1.1,<2.0)", "mkdocs-git-revision-date-localized-plugin (>=1.2,<2.0)"] -imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=9.4,<10.0)"] +imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=10.2,<11.0)"] recommended = ["mkdocs-minify-plugin (>=0.7,<1.0)", "mkdocs-redirects (>=1.2,<2.0)", "mkdocs-rss-plugin (>=1.6,<2.0)"] [[package]] @@ -1306,13 +1329,13 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-co [[package]] name = "pluggy" -version = "1.3.0" +version = "1.4.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] [package.extras] @@ -1358,27 +1381,27 @@ virtualenv = ">=20.10.0" [[package]] name = "psutil" -version = "5.9.7" +version = "5.9.8" description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ - {file = "psutil-5.9.7-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0bd41bf2d1463dfa535942b2a8f0e958acf6607ac0be52265ab31f7923bcd5e6"}, - {file = "psutil-5.9.7-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:5794944462509e49d4d458f4dbfb92c47539e7d8d15c796f141f474010084056"}, - {file = "psutil-5.9.7-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:fe361f743cb3389b8efda21980d93eb55c1f1e3898269bc9a2a1d0bb7b1f6508"}, - {file = "psutil-5.9.7-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:e469990e28f1ad738f65a42dcfc17adaed9d0f325d55047593cb9033a0ab63df"}, - {file = "psutil-5.9.7-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:3c4747a3e2ead1589e647e64aad601981f01b68f9398ddf94d01e3dc0d1e57c7"}, - {file = "psutil-5.9.7-cp27-none-win32.whl", hash = "sha256:1d4bc4a0148fdd7fd8f38e0498639ae128e64538faa507df25a20f8f7fb2341c"}, - {file = "psutil-5.9.7-cp27-none-win_amd64.whl", hash = "sha256:4c03362e280d06bbbfcd52f29acd79c733e0af33d707c54255d21029b8b32ba6"}, - {file = "psutil-5.9.7-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ea36cc62e69a13ec52b2f625c27527f6e4479bca2b340b7a452af55b34fcbe2e"}, - {file = "psutil-5.9.7-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1132704b876e58d277168cd729d64750633d5ff0183acf5b3c986b8466cd0284"}, - {file = "psutil-5.9.7-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8b7f07948f1304497ce4f4684881250cd859b16d06a1dc4d7941eeb6233bfe"}, - {file = "psutil-5.9.7-cp36-cp36m-win32.whl", hash = "sha256:b27f8fdb190c8c03914f908a4555159327d7481dac2f01008d483137ef3311a9"}, - {file = "psutil-5.9.7-cp36-cp36m-win_amd64.whl", hash = "sha256:44969859757f4d8f2a9bd5b76eba8c3099a2c8cf3992ff62144061e39ba8568e"}, - {file = "psutil-5.9.7-cp37-abi3-win32.whl", hash = "sha256:c727ca5a9b2dd5193b8644b9f0c883d54f1248310023b5ad3e92036c5e2ada68"}, - {file = "psutil-5.9.7-cp37-abi3-win_amd64.whl", hash = "sha256:f37f87e4d73b79e6c5e749440c3113b81d1ee7d26f21c19c47371ddea834f414"}, - {file = "psutil-5.9.7-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:032f4f2c909818c86cea4fe2cc407f1c0f0cde8e6c6d702b28b8ce0c0d143340"}, - {file = "psutil-5.9.7.tar.gz", hash = "sha256:3f02134e82cfb5d089fddf20bb2e03fd5cd52395321d1c8458a9e58500ff417c"}, + {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, + {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, + {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, + {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, + {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, + {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, + {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, + {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, + {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, + {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, ] [package.extras] @@ -1419,59 +1442,59 @@ files = [ [[package]] name = "pycryptodome" -version = "3.19.1" +version = "3.20.0" description = "Cryptographic library for Python" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ - {file = "pycryptodome-3.19.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:694020d2ff985cd714381b9da949a21028c24b86f562526186f6af7c7547e986"}, - {file = "pycryptodome-3.19.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:4464b0e8fd5508bff9baf18e6fd4c6548b1ac2ce9862d6965ff6a84ec9cb302a"}, - {file = "pycryptodome-3.19.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:420972f9c62978e852c74055d81c354079ce3c3a2213a92c9d7e37bbc63a26e2"}, - {file = "pycryptodome-3.19.1-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1bc0c49d986a1491d66d2a56570f12e960b12508b7e71f2423f532e28857f36"}, - {file = "pycryptodome-3.19.1-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:e038ab77fec0956d7aa989a3c647652937fc142ef41c9382c2ebd13c127d5b4a"}, - {file = "pycryptodome-3.19.1-cp27-cp27m-win32.whl", hash = "sha256:a991f8ffe8dfe708f86690948ae46442eebdd0fff07dc1b605987939a34ec979"}, - {file = "pycryptodome-3.19.1-cp27-cp27m-win_amd64.whl", hash = "sha256:2c16426ef49d9cba018be2340ea986837e1dfa25c2ea181787971654dd49aadd"}, - {file = "pycryptodome-3.19.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6d0d2b97758ebf2f36c39060520447c26455acb3bcff309c28b1c816173a6ff5"}, - {file = "pycryptodome-3.19.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:b8b80ff92049fd042177282917d994d344365ab7e8ec2bc03e853d93d2401786"}, - {file = "pycryptodome-3.19.1-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd4e7e8bf0fc1ada854688b9b309ee607e2aa85a8b44180f91021a4dd330a928"}, - {file = "pycryptodome-3.19.1-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:8cf5d3d6cf921fa81acd1f632f6cedcc03f5f68fc50c364cd39490ba01d17c49"}, - {file = "pycryptodome-3.19.1-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:67939a3adbe637281c611596e44500ff309d547e932c449337649921b17b6297"}, - {file = "pycryptodome-3.19.1-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:11ddf6c9b52116b62223b6a9f4741bc4f62bb265392a4463282f7f34bb287180"}, - {file = "pycryptodome-3.19.1-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3e6f89480616781d2a7f981472d0cdb09b9da9e8196f43c1234eff45c915766"}, - {file = "pycryptodome-3.19.1-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27e1efcb68993b7ce5d1d047a46a601d41281bba9f1971e6be4aa27c69ab8065"}, - {file = "pycryptodome-3.19.1-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c6273ca5a03b672e504995529b8bae56da0ebb691d8ef141c4aa68f60765700"}, - {file = "pycryptodome-3.19.1-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:b0bfe61506795877ff974f994397f0c862d037f6f1c0bfc3572195fc00833b96"}, - {file = "pycryptodome-3.19.1-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:f34976c5c8eb79e14c7d970fb097482835be8d410a4220f86260695ede4c3e17"}, - {file = "pycryptodome-3.19.1-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:7c9e222d0976f68d0cf6409cfea896676ddc1d98485d601e9508f90f60e2b0a2"}, - {file = "pycryptodome-3.19.1-cp35-abi3-win32.whl", hash = "sha256:4805e053571140cb37cf153b5c72cd324bb1e3e837cbe590a19f69b6cf85fd03"}, - {file = "pycryptodome-3.19.1-cp35-abi3-win_amd64.whl", hash = "sha256:a470237ee71a1efd63f9becebc0ad84b88ec28e6784a2047684b693f458f41b7"}, - {file = "pycryptodome-3.19.1-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:ed932eb6c2b1c4391e166e1a562c9d2f020bfff44a0e1b108f67af38b390ea89"}, - {file = "pycryptodome-3.19.1-pp27-pypy_73-win32.whl", hash = "sha256:81e9d23c0316fc1b45d984a44881b220062336bbdc340aa9218e8d0656587934"}, - {file = "pycryptodome-3.19.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:37e531bf896b70fe302f003d3be5a0a8697737a8d177967da7e23eff60d6483c"}, - {file = "pycryptodome-3.19.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd4e95b0eb4b28251c825fe7aa941fe077f993e5ca9b855665935b86fbb1cc08"}, - {file = "pycryptodome-3.19.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c22c80246c3c880c6950d2a8addf156cee74ec0dc5757d01e8e7067a3c7da015"}, - {file = "pycryptodome-3.19.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e70f5c839c7798743a948efa2a65d1fe96bb397fe6d7f2bde93d869fe4f0ad69"}, - {file = "pycryptodome-3.19.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6c3df3613592ea6afaec900fd7189d23c8c28b75b550254f4bd33fe94acb84b9"}, - {file = "pycryptodome-3.19.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08b445799d571041765e7d5c9ca09c5d3866c2f22eeb0dd4394a4169285184f4"}, - {file = "pycryptodome-3.19.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:954d156cd50130afd53f8d77f830fe6d5801bd23e97a69d358fed068f433fbfe"}, - {file = "pycryptodome-3.19.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b7efd46b0b4ac869046e814d83244aeab14ef787f4850644119b1c8b0ec2d637"}, - {file = "pycryptodome-3.19.1.tar.gz", hash = "sha256:8ae0dd1bcfada451c35f9e29a3e5db385caabc190f98e4a80ad02a61098fb776"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:f0e6d631bae3f231d3634f91ae4da7a960f7ff87f2865b2d2b831af1dfb04e9a"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:baee115a9ba6c5d2709a1e88ffe62b73ecc044852a925dcb67713a288c4ec70f"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:417a276aaa9cb3be91f9014e9d18d10e840a7a9b9a9be64a42f553c5b50b4d1d"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a1250b7ea809f752b68e3e6f3fd946b5939a52eaeea18c73bdab53e9ba3c2dd"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:d5954acfe9e00bc83ed9f5cb082ed22c592fbbef86dc48b907238be64ead5c33"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-win32.whl", hash = "sha256:06d6de87c19f967f03b4cf9b34e538ef46e99a337e9a61a77dbe44b2cbcf0690"}, + {file = "pycryptodome-3.20.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ec0bb1188c1d13426039af8ffcb4dbe3aad1d7680c35a62d8eaf2a529b5d3d4f"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5601c934c498cd267640b57569e73793cb9a83506f7c73a8ec57a516f5b0b091"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d29daa681517f4bc318cd8a23af87e1f2a7bad2fe361e8aa29c77d652a065de4"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3427d9e5310af6680678f4cce149f54e0bb4af60101c7f2c16fdf878b39ccccc"}, + {file = "pycryptodome-3.20.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:3cd3ef3aee1079ae44afaeee13393cf68b1058f70576b11439483e34f93cf818"}, + {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac1c7c0624a862f2e53438a15c9259d1655325fc2ec4392e66dc46cdae24d044"}, + {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:76658f0d942051d12a9bd08ca1b6b34fd762a8ee4240984f7c06ddfb55eaf15a"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f35d6cee81fa145333137009d9c8ba90951d7d77b67c79cbe5f03c7eb74d8fe2"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cb39afede7055127e35a444c1c041d2e8d2f1f9c121ecef573757ba4cd2c3c"}, + {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a4c4dc60b78ec41d2afa392491d788c2e06edf48580fbfb0dd0f828af49d25"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fb3b87461fa35afa19c971b0a2b7456a7b1db7b4eba9a8424666104925b78128"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:acc2614e2e5346a4a4eab6e199203034924313626f9620b7b4b38e9ad74b7e0c"}, + {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:210ba1b647837bfc42dd5a813cdecb5b86193ae11a3f5d972b9a0ae2c7e9e4b4"}, + {file = "pycryptodome-3.20.0-cp35-abi3-win32.whl", hash = "sha256:8d6b98d0d83d21fb757a182d52940d028564efe8147baa9ce0f38d057104ae72"}, + {file = "pycryptodome-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:9b3ae153c89a480a0ec402e23db8d8d84a3833b65fa4b15b81b83be9d637aab9"}, + {file = "pycryptodome-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:4401564ebf37dfde45d096974c7a159b52eeabd9969135f0426907db367a652a"}, + {file = "pycryptodome-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:ec1f93feb3bb93380ab0ebf8b859e8e5678c0f010d2d78367cf6bc30bfeb148e"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:acae12b9ede49f38eb0ef76fdec2df2e94aad85ae46ec85be3648a57f0a7db04"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f47888542a0633baff535a04726948e876bf1ed880fddb7c10a736fa99146ab3"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e0e4a987d38cfc2e71b4a1b591bae4891eeabe5fa0f56154f576e26287bfdea"}, + {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c18b381553638414b38705f07d1ef0a7cf301bc78a5f9bc17a957eb19446834b"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a60fedd2b37b4cb11ccb5d0399efe26db9e0dd149016c1cc6c8161974ceac2d6"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:405002eafad114a2f9a930f5db65feef7b53c4784495dd8758069b89baf68eab"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ab6ab0cb755154ad14e507d1df72de9897e99fd2d4922851a276ccc14f4f1a5"}, + {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:acf6e43fa75aca2d33e93409f2dafe386fe051818ee79ee8a3e21de9caa2ac9e"}, + {file = "pycryptodome-3.20.0.tar.gz", hash = "sha256:09609209ed7de61c2b560cc5c8c4fbf892f8b15b1faf7e4cbffac97db1fffda7"}, ] [[package]] name = "pydantic" -version = "2.5.3" +version = "2.6.0" description = "Data validation using Python type hints" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pydantic-2.5.3-py3-none-any.whl", hash = "sha256:d0caf5954bee831b6bfe7e338c32b9e30c85dfe080c843680783ac2b631673b4"}, - {file = "pydantic-2.5.3.tar.gz", hash = "sha256:b3ef57c62535b0941697cce638c08900d87fcb67e29cfa99e8a68f747f393f7a"}, + {file = "pydantic-2.6.0-py3-none-any.whl", hash = "sha256:1440966574e1b5b99cf75a13bec7b20e3512e8a61b894ae252f56275e2c465ae"}, + {file = "pydantic-2.6.0.tar.gz", hash = "sha256:ae887bd94eb404b09d86e4d12f93893bdca79d766e738528c6fa1c849f3c6bcf"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.14.6" +pydantic-core = "2.16.1" typing-extensions = ">=4.6.1" [package.extras] @@ -1479,116 +1502,90 @@ email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.14.6" +version = "2.16.1" description = "" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.14.6-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:72f9a942d739f09cd42fffe5dc759928217649f070056f03c70df14f5770acf9"}, - {file = "pydantic_core-2.14.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6a31d98c0d69776c2576dda4b77b8e0c69ad08e8b539c25c7d0ca0dc19a50d6c"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5aa90562bc079c6c290f0512b21768967f9968e4cfea84ea4ff5af5d917016e4"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:370ffecb5316ed23b667d99ce4debe53ea664b99cc37bfa2af47bc769056d534"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f85f3843bdb1fe80e8c206fe6eed7a1caeae897e496542cee499c374a85c6e08"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9862bf828112e19685b76ca499b379338fd4c5c269d897e218b2ae8fcb80139d"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:036137b5ad0cb0004c75b579445a1efccd072387a36c7f217bb8efd1afbe5245"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92879bce89f91f4b2416eba4429c7b5ca22c45ef4a499c39f0c5c69257522c7c"}, - {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0c08de15d50fa190d577e8591f0329a643eeaed696d7771760295998aca6bc66"}, - {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:36099c69f6b14fc2c49d7996cbf4f87ec4f0e66d1c74aa05228583225a07b590"}, - {file = "pydantic_core-2.14.6-cp310-none-win32.whl", hash = "sha256:7be719e4d2ae6c314f72844ba9d69e38dff342bc360379f7c8537c48e23034b7"}, - {file = "pydantic_core-2.14.6-cp310-none-win_amd64.whl", hash = "sha256:36fa402dcdc8ea7f1b0ddcf0df4254cc6b2e08f8cd80e7010d4c4ae6e86b2a87"}, - {file = "pydantic_core-2.14.6-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:dea7fcd62915fb150cdc373212141a30037e11b761fbced340e9db3379b892d4"}, - {file = "pydantic_core-2.14.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffff855100bc066ff2cd3aa4a60bc9534661816b110f0243e59503ec2df38421"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b027c86c66b8627eb90e57aee1f526df77dc6d8b354ec498be9a757d513b92b"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:00b1087dabcee0b0ffd104f9f53d7d3eaddfaa314cdd6726143af6bc713aa27e"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75ec284328b60a4e91010c1acade0c30584f28a1f345bc8f72fe8b9e46ec6a96"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e1f4744eea1501404b20b0ac059ff7e3f96a97d3e3f48ce27a139e053bb370b"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2602177668f89b38b9f84b7b3435d0a72511ddef45dc14446811759b82235a1"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c8edaea3089bf908dd27da8f5d9e395c5b4dc092dbcce9b65e7156099b4b937"}, - {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:478e9e7b360dfec451daafe286998d4a1eeaecf6d69c427b834ae771cad4b622"}, - {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b6ca36c12a5120bad343eef193cc0122928c5c7466121da7c20f41160ba00ba2"}, - {file = "pydantic_core-2.14.6-cp311-none-win32.whl", hash = "sha256:2b8719037e570639e6b665a4050add43134d80b687288ba3ade18b22bbb29dd2"}, - {file = "pydantic_core-2.14.6-cp311-none-win_amd64.whl", hash = "sha256:78ee52ecc088c61cce32b2d30a826f929e1708f7b9247dc3b921aec367dc1b23"}, - {file = "pydantic_core-2.14.6-cp311-none-win_arm64.whl", hash = "sha256:a19b794f8fe6569472ff77602437ec4430f9b2b9ec7a1105cfd2232f9ba355e6"}, - {file = "pydantic_core-2.14.6-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:667aa2eac9cd0700af1ddb38b7b1ef246d8cf94c85637cbb03d7757ca4c3fdec"}, - {file = "pydantic_core-2.14.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdee837710ef6b56ebd20245b83799fce40b265b3b406e51e8ccc5b85b9099b7"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c5bcf3414367e29f83fd66f7de64509a8fd2368b1edf4351e862910727d3e51"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26a92ae76f75d1915806b77cf459811e772d8f71fd1e4339c99750f0e7f6324f"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a983cca5ed1dd9a35e9e42ebf9f278d344603bfcb174ff99a5815f953925140a"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cb92f9061657287eded380d7dc455bbf115430b3aa4741bdc662d02977e7d0af"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ace1e220b078c8e48e82c081e35002038657e4b37d403ce940fa679e57113b"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef633add81832f4b56d3b4c9408b43d530dfca29e68fb1b797dcb861a2c734cd"}, - {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7e90d6cc4aad2cc1f5e16ed56e46cebf4877c62403a311af20459c15da76fd91"}, - {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e8a5ac97ea521d7bde7621d86c30e86b798cdecd985723c4ed737a2aa9e77d0c"}, - {file = "pydantic_core-2.14.6-cp312-none-win32.whl", hash = "sha256:f27207e8ca3e5e021e2402ba942e5b4c629718e665c81b8b306f3c8b1ddbb786"}, - {file = "pydantic_core-2.14.6-cp312-none-win_amd64.whl", hash = "sha256:b3e5fe4538001bb82e2295b8d2a39356a84694c97cb73a566dc36328b9f83b40"}, - {file = "pydantic_core-2.14.6-cp312-none-win_arm64.whl", hash = "sha256:64634ccf9d671c6be242a664a33c4acf12882670b09b3f163cd00a24cffbd74e"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:24368e31be2c88bd69340fbfe741b405302993242ccb476c5c3ff48aeee1afe0"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:e33b0834f1cf779aa839975f9d8755a7c2420510c0fa1e9fa0497de77cd35d2c"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6af4b3f52cc65f8a0bc8b1cd9676f8c21ef3e9132f21fed250f6958bd7223bed"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15687d7d7f40333bd8266f3814c591c2e2cd263fa2116e314f60d82086e353a"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:095b707bb287bfd534044166ab767bec70a9bba3175dcdc3371782175c14e43c"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94fc0e6621e07d1e91c44e016cc0b189b48db053061cc22d6298a611de8071bb"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce830e480f6774608dedfd4a90c42aac4a7af0a711f1b52f807130c2e434c06"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a306cdd2ad3a7d795d8e617a58c3a2ed0f76c8496fb7621b6cd514eb1532cae8"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2f5fa187bde8524b1e37ba894db13aadd64faa884657473b03a019f625cee9a8"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:438027a975cc213a47c5d70672e0d29776082155cfae540c4e225716586be75e"}, - {file = "pydantic_core-2.14.6-cp37-none-win32.whl", hash = "sha256:f96ae96a060a8072ceff4cfde89d261837b4294a4f28b84a28765470d502ccc6"}, - {file = "pydantic_core-2.14.6-cp37-none-win_amd64.whl", hash = "sha256:e646c0e282e960345314f42f2cea5e0b5f56938c093541ea6dbf11aec2862391"}, - {file = "pydantic_core-2.14.6-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:db453f2da3f59a348f514cfbfeb042393b68720787bbef2b4c6068ea362c8149"}, - {file = "pydantic_core-2.14.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3860c62057acd95cc84044e758e47b18dcd8871a328ebc8ccdefd18b0d26a21b"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36026d8f99c58d7044413e1b819a67ca0e0b8ebe0f25e775e6c3d1fabb3c38fb"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ed1af8692bd8d2a29d702f1a2e6065416d76897d726e45a1775b1444f5928a7"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:314ccc4264ce7d854941231cf71b592e30d8d368a71e50197c905874feacc8a8"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:982487f8931067a32e72d40ab6b47b1628a9c5d344be7f1a4e668fb462d2da42"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dbe357bc4ddda078f79d2a36fc1dd0494a7f2fad83a0a684465b6f24b46fe80"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2f6ffc6701a0eb28648c845f4945a194dc7ab3c651f535b81793251e1185ac3d"}, - {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7f5025db12fc6de7bc1104d826d5aee1d172f9ba6ca936bf6474c2148ac336c1"}, - {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dab03ed811ed1c71d700ed08bde8431cf429bbe59e423394f0f4055f1ca0ea60"}, - {file = "pydantic_core-2.14.6-cp38-none-win32.whl", hash = "sha256:dfcbebdb3c4b6f739a91769aea5ed615023f3c88cb70df812849aef634c25fbe"}, - {file = "pydantic_core-2.14.6-cp38-none-win_amd64.whl", hash = "sha256:99b14dbea2fdb563d8b5a57c9badfcd72083f6006caf8e126b491519c7d64ca8"}, - {file = "pydantic_core-2.14.6-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:4ce8299b481bcb68e5c82002b96e411796b844d72b3e92a3fbedfe8e19813eab"}, - {file = "pydantic_core-2.14.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b9a9d92f10772d2a181b5ca339dee066ab7d1c9a34ae2421b2a52556e719756f"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd9e98b408384989ea4ab60206b8e100d8687da18b5c813c11e92fd8212a98e0"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4f86f1f318e56f5cbb282fe61eb84767aee743ebe32c7c0834690ebea50c0a6b"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86ce5fcfc3accf3a07a729779d0b86c5d0309a4764c897d86c11089be61da160"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dcf1978be02153c6a31692d4fbcc2a3f1db9da36039ead23173bc256ee3b91b"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eedf97be7bc3dbc8addcef4142f4b4164066df0c6f36397ae4aaed3eb187d8ab"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5f916acf8afbcab6bacbb376ba7dc61f845367901ecd5e328fc4d4aef2fcab0"}, - {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8a14c192c1d724c3acbfb3f10a958c55a2638391319ce8078cb36c02283959b9"}, - {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0348b1dc6b76041516e8a854ff95b21c55f5a411c3297d2ca52f5528e49d8411"}, - {file = "pydantic_core-2.14.6-cp39-none-win32.whl", hash = "sha256:de2a0645a923ba57c5527497daf8ec5df69c6eadf869e9cd46e86349146e5975"}, - {file = "pydantic_core-2.14.6-cp39-none-win_amd64.whl", hash = "sha256:aca48506a9c20f68ee61c87f2008f81f8ee99f8d7f0104bff3c47e2d148f89d9"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d5c28525c19f5bb1e09511669bb57353d22b94cf8b65f3a8d141c389a55dec95"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:78d0768ee59baa3de0f4adac9e3748b4b1fffc52143caebddfd5ea2961595277"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b93785eadaef932e4fe9c6e12ba67beb1b3f1e5495631419c784ab87e975670"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a874f21f87c485310944b2b2734cd6d318765bcbb7515eead33af9641816506e"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89f4477d915ea43b4ceea6756f63f0288941b6443a2b28c69004fe07fde0d0d"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:172de779e2a153d36ee690dbc49c6db568d7b33b18dc56b69a7514aecbcf380d"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dfcebb950aa7e667ec226a442722134539e77c575f6cfaa423f24371bb8d2e94"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:55a23dcd98c858c0db44fc5c04fc7ed81c4b4d33c653a7c45ddaebf6563a2f66"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:4241204e4b36ab5ae466ecec5c4c16527a054c69f99bba20f6f75232a6a534e2"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e574de99d735b3fc8364cba9912c2bec2da78775eba95cbb225ef7dda6acea24"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1302a54f87b5cd8528e4d6d1bf2133b6aa7c6122ff8e9dc5220fbc1e07bffebd"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8e81e4b55930e5ffab4a68db1af431629cf2e4066dbdbfef65348b8ab804ea8"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c99462ffc538717b3e60151dfaf91125f637e801f5ab008f81c402f1dff0cd0f"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e4cf2d5829f6963a5483ec01578ee76d329eb5caf330ecd05b3edd697e7d768a"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:cf10b7d58ae4a1f07fccbf4a0a956d705356fea05fb4c70608bb6fa81d103cda"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:399ac0891c284fa8eb998bcfa323f2234858f5d2efca3950ae58c8f88830f145"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c6a5c79b28003543db3ba67d1df336f253a87d3112dac3a51b94f7d48e4c0e1"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599c87d79cab2a6a2a9df4aefe0455e61e7d2aeede2f8577c1b7c0aec643ee8e"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43e166ad47ba900f2542a80d83f9fc65fe99eb63ceec4debec160ae729824052"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a0b5db001b98e1c649dd55afa928e75aa4087e587b9524a4992316fa23c9fba"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:747265448cb57a9f37572a488a57d873fd96bf51e5bb7edb52cfb37124516da4"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7ebe3416785f65c28f4f9441e916bfc8a54179c8dea73c23023f7086fa601c5d"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:86c963186ca5e50d5c8287b1d1c9d3f8f024cbe343d048c5bd282aec2d8641f2"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e0641b506486f0b4cd1500a2a65740243e8670a2549bb02bc4556a83af84ae03"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71d72ca5eaaa8d38c8df16b7deb1a2da4f650c41b58bb142f3fb75d5ad4a611f"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27e524624eace5c59af499cd97dc18bb201dc6a7a2da24bfc66ef151c69a5f2a"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3dde6cac75e0b0902778978d3b1646ca9f438654395a362cb21d9ad34b24acf"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:00646784f6cd993b1e1c0e7b0fdcbccc375d539db95555477771c27555e3c556"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:23598acb8ccaa3d1d875ef3b35cb6376535095e9405d91a3d57a8c7db5d29341"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7f41533d7e3cf9520065f610b41ac1c76bc2161415955fbcead4981b22c7611e"}, - {file = "pydantic_core-2.14.6.tar.gz", hash = "sha256:1fd0c1d395372843fba13a51c28e3bb9d59bd7aebfeb17358ffaaa1e4dbbe948"}, + {file = "pydantic_core-2.16.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:300616102fb71241ff477a2cbbc847321dbec49428434a2f17f37528721c4948"}, + {file = "pydantic_core-2.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5511f962dd1b9b553e9534c3b9c6a4b0c9ded3d8c2be96e61d56f933feef9e1f"}, + {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98f0edee7ee9cc7f9221af2e1b95bd02810e1c7a6d115cfd82698803d385b28f"}, + {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9795f56aa6b2296f05ac79d8a424e94056730c0b860a62b0fdcfe6340b658cc8"}, + {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c45f62e4107ebd05166717ac58f6feb44471ed450d07fecd90e5f69d9bf03c48"}, + {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:462d599299c5971f03c676e2b63aa80fec5ebc572d89ce766cd11ca8bcb56f3f"}, + {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ebaa4bf6386a3b22eec518da7d679c8363fb7fb70cf6972161e5542f470798"}, + {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:99f9a50b56713a598d33bc23a9912224fc5d7f9f292444e6664236ae471ddf17"}, + {file = "pydantic_core-2.16.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8ec364e280db4235389b5e1e6ee924723c693cbc98e9d28dc1767041ff9bc388"}, + {file = "pydantic_core-2.16.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:653a5dfd00f601a0ed6654a8b877b18d65ac32c9d9997456e0ab240807be6cf7"}, + {file = "pydantic_core-2.16.1-cp310-none-win32.whl", hash = "sha256:1661c668c1bb67b7cec96914329d9ab66755911d093bb9063c4c8914188af6d4"}, + {file = "pydantic_core-2.16.1-cp310-none-win_amd64.whl", hash = "sha256:561be4e3e952c2f9056fba5267b99be4ec2afadc27261505d4992c50b33c513c"}, + {file = "pydantic_core-2.16.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:102569d371fadc40d8f8598a59379c37ec60164315884467052830b28cc4e9da"}, + {file = "pydantic_core-2.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:735dceec50fa907a3c314b84ed609dec54b76a814aa14eb90da31d1d36873a5e"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e83ebbf020be727d6e0991c1b192a5c2e7113eb66e3def0cd0c62f9f266247e4"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:30a8259569fbeec49cfac7fda3ec8123486ef1b729225222f0d41d5f840b476f"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:920c4897e55e2881db6a6da151198e5001552c3777cd42b8a4c2f72eedc2ee91"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5247a3d74355f8b1d780d0f3b32a23dd9f6d3ff43ef2037c6dcd249f35ecf4c"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d5bea8012df5bb6dda1e67d0563ac50b7f64a5d5858348b5c8cb5043811c19d"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ed3025a8a7e5a59817b7494686d449ebfbe301f3e757b852c8d0d1961d6be864"}, + {file = "pydantic_core-2.16.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:06f0d5a1d9e1b7932477c172cc720b3b23c18762ed7a8efa8398298a59d177c7"}, + {file = "pydantic_core-2.16.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:150ba5c86f502c040b822777e2e519b5625b47813bd05f9273a8ed169c97d9ae"}, + {file = "pydantic_core-2.16.1-cp311-none-win32.whl", hash = "sha256:d6cbdf12ef967a6aa401cf5cdf47850559e59eedad10e781471c960583f25aa1"}, + {file = "pydantic_core-2.16.1-cp311-none-win_amd64.whl", hash = "sha256:afa01d25769af33a8dac0d905d5c7bb2d73c7c3d5161b2dd6f8b5b5eea6a3c4c"}, + {file = "pydantic_core-2.16.1-cp311-none-win_arm64.whl", hash = "sha256:1a2fe7b00a49b51047334d84aafd7e39f80b7675cad0083678c58983662da89b"}, + {file = "pydantic_core-2.16.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f478ec204772a5c8218e30eb813ca43e34005dff2eafa03931b3d8caef87d51"}, + {file = "pydantic_core-2.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1936ef138bed2165dd8573aa65e3095ef7c2b6247faccd0e15186aabdda7f66"}, + {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99d3a433ef5dc3021c9534a58a3686c88363c591974c16c54a01af7efd741f13"}, + {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd88f40f2294440d3f3c6308e50d96a0d3d0973d6f1a5732875d10f569acef49"}, + {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fac641bbfa43d5a1bed99d28aa1fded1984d31c670a95aac1bf1d36ac6ce137"}, + {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72bf9308a82b75039b8c8edd2be2924c352eda5da14a920551a8b65d5ee89253"}, + {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb4363e6c9fc87365c2bc777a1f585a22f2f56642501885ffc7942138499bf54"}, + {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:20f724a023042588d0f4396bbbcf4cffd0ddd0ad3ed4f0d8e6d4ac4264bae81e"}, + {file = "pydantic_core-2.16.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fb4370b15111905bf8b5ba2129b926af9470f014cb0493a67d23e9d7a48348e8"}, + {file = "pydantic_core-2.16.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23632132f1fd608034f1a56cc3e484be00854db845b3a4a508834be5a6435a6f"}, + {file = "pydantic_core-2.16.1-cp312-none-win32.whl", hash = "sha256:b9f3e0bffad6e238f7acc20c393c1ed8fab4371e3b3bc311020dfa6020d99212"}, + {file = "pydantic_core-2.16.1-cp312-none-win_amd64.whl", hash = "sha256:a0b4cfe408cd84c53bab7d83e4209458de676a6ec5e9c623ae914ce1cb79b96f"}, + {file = "pydantic_core-2.16.1-cp312-none-win_arm64.whl", hash = "sha256:d195add190abccefc70ad0f9a0141ad7da53e16183048380e688b466702195dd"}, + {file = "pydantic_core-2.16.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:502c062a18d84452858f8aea1e520e12a4d5228fc3621ea5061409d666ea1706"}, + {file = "pydantic_core-2.16.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d8c032ccee90b37b44e05948b449a2d6baed7e614df3d3f47fe432c952c21b60"}, + {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:920f4633bee43d7a2818e1a1a788906df5a17b7ab6fe411220ed92b42940f818"}, + {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f5d37ff01edcbace53a402e80793640c25798fb7208f105d87a25e6fcc9ea06"}, + {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:399166f24c33a0c5759ecc4801f040dbc87d412c1a6d6292b2349b4c505effc9"}, + {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ac89ccc39cd1d556cc72d6752f252dc869dde41c7c936e86beac5eb555041b66"}, + {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73802194f10c394c2bedce7a135ba1d8ba6cff23adf4217612bfc5cf060de34c"}, + {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8fa00fa24ffd8c31fac081bf7be7eb495be6d248db127f8776575a746fa55c95"}, + {file = "pydantic_core-2.16.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:601d3e42452cd4f2891c13fa8c70366d71851c1593ed42f57bf37f40f7dca3c8"}, + {file = "pydantic_core-2.16.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07982b82d121ed3fc1c51faf6e8f57ff09b1325d2efccaa257dd8c0dd937acca"}, + {file = "pydantic_core-2.16.1-cp38-none-win32.whl", hash = "sha256:d0bf6f93a55d3fa7a079d811b29100b019784e2ee6bc06b0bb839538272a5610"}, + {file = "pydantic_core-2.16.1-cp38-none-win_amd64.whl", hash = "sha256:fbec2af0ebafa57eb82c18c304b37c86a8abddf7022955d1742b3d5471a6339e"}, + {file = "pydantic_core-2.16.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a497be217818c318d93f07e14502ef93d44e6a20c72b04c530611e45e54c2196"}, + {file = "pydantic_core-2.16.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:694a5e9f1f2c124a17ff2d0be613fd53ba0c26de588eb4bdab8bca855e550d95"}, + {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d4dfc66abea3ec6d9f83e837a8f8a7d9d3a76d25c9911735c76d6745950e62c"}, + {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8655f55fe68c4685673265a650ef71beb2d31871c049c8b80262026f23605ee3"}, + {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21e3298486c4ea4e4d5cc6fb69e06fb02a4e22089304308817035ac006a7f506"}, + {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:71b4a48a7427f14679f0015b13c712863d28bb1ab700bd11776a5368135c7d60"}, + {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dca874e35bb60ce4f9f6665bfbfad050dd7573596608aeb9e098621ac331dc"}, + {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa496cd45cda0165d597e9d6f01e36c33c9508f75cf03c0a650018c5048f578e"}, + {file = "pydantic_core-2.16.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5317c04349472e683803da262c781c42c5628a9be73f4750ac7d13040efb5d2d"}, + {file = "pydantic_core-2.16.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:42c29d54ed4501a30cd71015bf982fa95e4a60117b44e1a200290ce687d3e640"}, + {file = "pydantic_core-2.16.1-cp39-none-win32.whl", hash = "sha256:ba07646f35e4e49376c9831130039d1b478fbfa1215ae62ad62d2ee63cf9c18f"}, + {file = "pydantic_core-2.16.1-cp39-none-win_amd64.whl", hash = "sha256:2133b0e412a47868a358713287ff9f9a328879da547dc88be67481cdac529118"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d25ef0c33f22649b7a088035fd65ac1ce6464fa2876578df1adad9472f918a76"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:99c095457eea8550c9fa9a7a992e842aeae1429dab6b6b378710f62bfb70b394"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b49c604ace7a7aa8af31196abbf8f2193be605db6739ed905ecaf62af31ccae0"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c56da23034fe66221f2208c813d8aa509eea34d97328ce2add56e219c3a9f41c"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cebf8d56fee3b08ad40d332a807ecccd4153d3f1ba8231e111d9759f02edfd05"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:1ae8048cba95f382dba56766525abca438328455e35c283bb202964f41a780b0"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:780daad9e35b18d10d7219d24bfb30148ca2afc309928e1d4d53de86822593dc"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c94b5537bf6ce66e4d7830c6993152940a188600f6ae044435287753044a8fe2"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:adf28099d061a25fbcc6531febb7a091e027605385de9fe14dd6a97319d614cf"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:644904600c15816a1f9a1bafa6aab0d21db2788abcdf4e2a77951280473f33e1"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87bce04f09f0552b66fca0c4e10da78d17cb0e71c205864bab4e9595122cb9d9"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:877045a7969ace04d59516d5d6a7dee13106822f99a5d8df5e6822941f7bedc8"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9c46e556ee266ed3fb7b7a882b53df3c76b45e872fdab8d9cf49ae5e91147fd7"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4eebbd049008eb800f519578e944b8dc8e0f7d59a5abb5924cc2d4ed3a1834ff"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:c0be58529d43d38ae849a91932391eb93275a06b93b79a8ab828b012e916a206"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b1fc07896fc1851558f532dffc8987e526b682ec73140886c831d773cef44b76"}, + {file = "pydantic_core-2.16.1.tar.gz", hash = "sha256:daff04257b49ab7f4b3f73f98283d3dbb1a65bf3500d55c7beac3c66c310fe34"}, ] [package.dependencies] @@ -2013,18 +2010,16 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "requests-file" -version = "1.5.1" +version = "2.0.0" description = "File transport adapter for Requests" optional = false python-versions = "*" files = [ - {file = "requests-file-1.5.1.tar.gz", hash = "sha256:07d74208d3389d01c38ab89ef403af0cfec63957d53a0081d8eca738d0247d8e"}, - {file = "requests_file-1.5.1-py2.py3-none-any.whl", hash = "sha256:dfe5dae75c12481f68ba353183c53a65e6044c923e64c24b2209f6c7570ca953"}, + {file = "requests-file-2.0.0.tar.gz", hash = "sha256:20c5931629c558fda566cacc10cfe2cd502433e628f568c34c80d96a0cc95972"}, ] [package.dependencies] requests = ">=1.0.0" -six = "*" [[package]] name = "resolvelib" @@ -2403,4 +2398,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "1d40c762ee4808a285f17096c5140f4e6fa7d236a56f97f0415aa81aef917ea1" \ No newline at end of file +content-hash = "1d40c762ee4808a285f17096c5140f4e6fa7d236a56f97f0415aa81aef917ea1" From 697ef7d684a26afe7f7ac8f4c84a9069bb966f0c Mon Sep 17 00:00:00 2001 From: liquidsec Date: Mon, 29 Jan 2024 18:06:58 -0500 Subject: [PATCH 19/35] bumping baddns version again --- bbot/modules/baddns.py | 2 +- bbot/modules/baddns_zone.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bbot/modules/baddns.py b/bbot/modules/baddns.py index 8a3e6fd90..7b5d840c2 100644 --- a/bbot/modules/baddns.py +++ b/bbot/modules/baddns.py @@ -18,7 +18,7 @@ class baddns(BaseModule): "only_high_confidence": "Do not emit low-confidence or generic detections", } max_event_handlers = 8 - deps_pip = ["baddns~=1.0.675"] + deps_pip = ["baddns~=1.0.679"] def select_modules(self): selected_modules = [] diff --git a/bbot/modules/baddns_zone.py b/bbot/modules/baddns_zone.py index 233ae3918..b51efcaa3 100644 --- a/bbot/modules/baddns_zone.py +++ b/bbot/modules/baddns_zone.py @@ -18,7 +18,7 @@ class baddns_zone(baddns_module): "only_high_confidence": "Do not emit low-confidence or generic detections", } max_event_handlers = 8 - deps_pip = ["baddns~=1.0.675"] + deps_pip = ["baddns~=1.0.679"] def select_modules(self): selected_modules = [] From 97223898c5b9088449d635882d87e6607bc8b23d Mon Sep 17 00:00:00 2001 From: liquidsec Date: Mon, 5 Feb 2024 13:40:56 -0500 Subject: [PATCH 20/35] bumping baddns version to ensure python 3.12 compatability --- bbot/modules/baddns.py | 2 +- bbot/modules/baddns_zone.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bbot/modules/baddns.py b/bbot/modules/baddns.py index 7b5d840c2..89fbecda1 100644 --- a/bbot/modules/baddns.py +++ b/bbot/modules/baddns.py @@ -18,7 +18,7 @@ class baddns(BaseModule): "only_high_confidence": "Do not emit low-confidence or generic detections", } max_event_handlers = 8 - deps_pip = ["baddns~=1.0.679"] + deps_pip = ["baddns~=1.0.698"] def select_modules(self): selected_modules = [] diff --git a/bbot/modules/baddns_zone.py b/bbot/modules/baddns_zone.py index b51efcaa3..4cf8c7eee 100644 --- a/bbot/modules/baddns_zone.py +++ b/bbot/modules/baddns_zone.py @@ -18,7 +18,7 @@ class baddns_zone(baddns_module): "only_high_confidence": "Do not emit low-confidence or generic detections", } max_event_handlers = 8 - deps_pip = ["baddns~=1.0.679"] + deps_pip = ["baddns~=1.0.698"] def select_modules(self): selected_modules = [] From b3bfd1a711ff0e778b533d7e271c3fed61ca5cc7 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Mon, 5 Feb 2024 14:39:27 -0500 Subject: [PATCH 21/35] rolling version numbers again for python 3.12 fix --- bbot/modules/baddns.py | 2 +- bbot/modules/baddns_zone.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bbot/modules/baddns.py b/bbot/modules/baddns.py index 89fbecda1..8221364c4 100644 --- a/bbot/modules/baddns.py +++ b/bbot/modules/baddns.py @@ -18,7 +18,7 @@ class baddns(BaseModule): "only_high_confidence": "Do not emit low-confidence or generic detections", } max_event_handlers = 8 - deps_pip = ["baddns~=1.0.698"] + deps_pip = ["baddns~=1.0.702"] def select_modules(self): selected_modules = [] diff --git a/bbot/modules/baddns_zone.py b/bbot/modules/baddns_zone.py index 4cf8c7eee..a7aac1f74 100644 --- a/bbot/modules/baddns_zone.py +++ b/bbot/modules/baddns_zone.py @@ -18,7 +18,7 @@ class baddns_zone(baddns_module): "only_high_confidence": "Do not emit low-confidence or generic detections", } max_event_handlers = 8 - deps_pip = ["baddns~=1.0.698"] + deps_pip = ["baddns~=1.0.702"] def select_modules(self): selected_modules = [] From caf444fe591ce35e41336991c2023999929303dc Mon Sep 17 00:00:00 2001 From: liquidsec Date: Tue, 6 Feb 2024 17:08:17 -0500 Subject: [PATCH 22/35] performance increases using async tasks --- bbot/modules/baddns.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/bbot/modules/baddns.py b/bbot/modules/baddns.py index 8221364c4..e2270b4f6 100644 --- a/bbot/modules/baddns.py +++ b/bbot/modules/baddns.py @@ -1,6 +1,7 @@ from baddns.base import get_all_modules from .base import BaseModule +import asyncio import logging from bbot.core.logger.logger import include_logger @@ -35,6 +36,9 @@ async def setup(self): return True async def handle_event(self, event): + + tasks = [] + for ModuleClass in self.select_modules(): module_instance = ModuleClass( event.data, @@ -42,7 +46,10 @@ async def handle_event(self, event): dns_client=self.scan.helpers.dns.resolver, custom_nameservers=self.custom_nameservers, ) - if await module_instance.dispatch(): + tasks.append((module_instance, asyncio.create_task(module_instance.dispatch()))) + + for module_instance, task in tasks: + if await task: results = module_instance.analyze() if results and len(results) > 0: for r in results: From 22823dbf327bddc28fce8ab2b688c276839606ca Mon Sep 17 00:00:00 2001 From: TheTechromancer Date: Tue, 30 Jan 2024 17:43:39 -0500 Subject: [PATCH 23/35] dns mock cleanup --- bbot/core/helpers/dns.py | 25 ------- bbot/test/bbot_fixtures.py | 65 ++++++++++++++++++ bbot/test/conftest.py | 67 ------------------- bbot/test/test_step_1/test_dns.py | 10 ++- .../test_step_1/test_manager_deduplication.py | 16 ++--- .../test_manager_scope_accuracy.py | 8 +-- bbot/test/test_step_1/test_modules_basic.py | 46 +++++++++---- bbot/test/test_step_1/test_scan.py | 9 +-- bbot/test/test_step_2/module_tests/base.py | 14 ++-- .../module_tests/test_module_affiliates.py | 9 ++- .../test_module_asset_inventory.py | 6 +- .../module_tests/test_module_baddns.py | 14 ++-- .../module_tests/test_module_baddns_zone.py | 21 +++--- .../module_tests/test_module_internetdb.py | 8 +-- .../module_tests/test_module_ipneighbor.py | 18 +---- .../module_tests/test_module_postman.py | 2 +- .../module_tests/test_module_speculate.py | 6 +- 17 files changed, 160 insertions(+), 184 deletions(-) diff --git a/bbot/core/helpers/dns.py b/bbot/core/helpers/dns.py index 9ad22116f..825536fa8 100644 --- a/bbot/core/helpers/dns.py +++ b/bbot/core/helpers/dns.py @@ -1040,28 +1040,3 @@ def _get_dummy_module(self, name): dummy_module = self.parent_helper._make_dummy_module(name=name, _type="DNS") self._dummy_modules[name] = dummy_module return dummy_module - - def mock_dns(self, dns_dict): - if self._orig_resolve_raw is None: - self._orig_resolve_raw = self.resolve_raw - - async def mock_resolve_raw(query, **kwargs): - results = [] - errors = [] - types = self._parse_rdtype(kwargs.get("type", ["A", "AAAA"])) - for t in types: - with suppress(KeyError): - results += self._mock_table[(query, t)] - return results, errors - - for (query, rdtype), answers in dns_dict.items(): - if isinstance(answers, str): - answers = [answers] - for answer in answers: - rdata = dns.rdata.from_text("IN", rdtype, answer) - try: - self._mock_table[(query, rdtype)].append((rdtype, rdata)) - except KeyError: - self._mock_table[(query, rdtype)] = [(rdtype, [rdata])] - - self.resolve_raw = mock_resolve_raw diff --git a/bbot/test/bbot_fixtures.py b/bbot/test/bbot_fixtures.py index a08ef3ece..7170e5a70 100644 --- a/bbot/test/bbot_fixtures.py +++ b/bbot/test/bbot_fixtures.py @@ -1,4 +1,5 @@ import os +import dns import sys import pytest import asyncio # noqa @@ -252,3 +253,67 @@ def install_all_python_deps(): for module in module_loader.preloaded().values(): deps_pip.update(set(module.get("deps", {}).get("pip", []))) subprocess.run([sys.executable, "-m", "pip", "install"] + list(deps_pip)) + + +class MockResolver: + import dns + + def __init__(self, mock_data=None): + self.mock_data = mock_data if mock_data else {} + self.nameservers = ["127.0.0.1"] + + async def resolve_address(self, ipaddr, *args, **kwargs): + modified_kwargs = {} + modified_kwargs.update(kwargs) + modified_kwargs["rdtype"] = "PTR" + return await self.resolve(str(dns.reversename.from_address(ipaddr)), *args, **modified_kwargs) + + def create_dns_response(self, query_name, rdtype): + query_name = query_name.strip(".") + answers = self.mock_data.get(query_name, {}).get(rdtype, []) + if not answers: + raise self.dns.resolver.NXDOMAIN(f"No answer found for {query_name} {rdtype}") + + message_text = f"""id 1234 +opcode QUERY +rcode NOERROR +flags QR AA RD +;QUESTION +{query_name}. IN {rdtype} +;ANSWER""" + for answer in answers: + message_text += f"\n{query_name}. 1 IN {rdtype} {answer}" + + message_text += "\n;AUTHORITY\n;ADDITIONAL\n" + message = self.dns.message.from_text(message_text) + return message + + async def resolve(self, query_name, rdtype=None): + if rdtype is None: + rdtype = "A" + elif isinstance(rdtype, str): + rdtype = rdtype.upper() + else: + rdtype = str(rdtype.name).upper() + + domain_name = self.dns.name.from_text(query_name) + rdtype_obj = self.dns.rdatatype.from_text(rdtype) + + if "_NXDOMAIN" in self.mock_data and query_name in self.mock_data["_NXDOMAIN"]: + # Simulate the NXDOMAIN exception + raise self.dns.resolver.NXDOMAIN + + try: + response = self.create_dns_response(query_name, rdtype) + answer = self.dns.resolver.Answer(domain_name, rdtype_obj, self.dns.rdataclass.IN, response) + return answer + except self.dns.resolver.NXDOMAIN: + return [] + + +@pytest.fixture() +def mock_dns(): + def _mock_dns(scan, mock_data): + scan.helpers.dns.resolver = MockResolver(mock_data) + + return _mock_dns diff --git a/bbot/test/conftest.py b/bbot/test/conftest.py index dd59eac30..b60a0633d 100644 --- a/bbot/test/conftest.py +++ b/bbot/test/conftest.py @@ -179,70 +179,3 @@ def proxy_server(): # Stop the server. server.shutdown() server_thread.join() - - -class MockResolver: - import dns - - def __init__(self, mock_data=None): - self.mock_data = mock_data if mock_data else {} - self.nameservers = ["127.0.0.1"] - - async def resolve_address(self, host): - try: - from dns.asyncresolver import resolve_address - - result = await resolve_address(host) - return result - except ImportError: - raise ImportError("dns.asyncresolver.Resolver.resolve_address not found") - - def create_dns_response(self, query_name, rdtype): - answers = self.mock_data.get(query_name, {}).get(rdtype, []) - if not answers: - raise self.dns.resolver.NXDOMAIN(f"No answer found for {query_name} {rdtype}") - - message_text = f"""id 1234 -opcode QUERY -rcode NOERROR -flags QR AA RD -;QUESTION -{query_name}. IN {rdtype} -;ANSWER""" - for answer in answers: - message_text += f"\n{query_name}. 1 IN {rdtype} {answer}" - - message_text += "\n;AUTHORITY\n;ADDITIONAL\n" - message = self.dns.message.from_text(message_text) - return message - - async def resolve(self, query_name, rdtype=None): - if rdtype is None: - rdtype = "A" - elif isinstance(rdtype, str): - rdtype = rdtype.upper() - else: - rdtype = str(rdtype.name).upper() - - domain_name = self.dns.name.from_text(query_name) - rdtype_obj = self.dns.rdatatype.from_text(rdtype) - - if "_NXDOMAIN" in self.mock_data and query_name in self.mock_data["_NXDOMAIN"]: - # Simulate the NXDOMAIN exception - raise self.dns.resolver.NXDOMAIN - - try: - response = self.create_dns_response(query_name, rdtype) - answer = self.dns.resolver.Answer(domain_name, rdtype_obj, self.dns.rdataclass.IN, response) - return answer - except self.dns.resolver.NXDOMAIN: - return [] - - -@pytest.fixture() -def configure_mock_resolver(monkeypatch): - def _configure(mock_data): - mock_resolver = MockResolver(mock_data) - return mock_resolver - - return _configure diff --git a/bbot/test/test_step_1/test_dns.py b/bbot/test/test_step_1/test_dns.py index debaed2b9..37ddf86a8 100644 --- a/bbot/test/test_step_1/test_dns.py +++ b/bbot/test/test_step_1/test_dns.py @@ -2,7 +2,7 @@ @pytest.mark.asyncio -async def test_dns(bbot_scanner, bbot_config): +async def test_dns(bbot_scanner, bbot_config, mock_dns): scan = bbot_scanner("1.1.1.1", config=bbot_config) helpers = scan.helpers @@ -85,8 +85,12 @@ async def test_dns(bbot_scanner, bbot_config): dns_config = OmegaConf.create({"dns_resolution": True}) dns_config = OmegaConf.merge(bbot_config, dns_config) scan2 = bbot_scanner("evilcorp.com", config=dns_config) - scan2.helpers.dns.mock_dns( - {("evilcorp.com", "TXT"): '"v=spf1 include:cloudprovider.com ~all"', ("cloudprovider.com", "A"): "1.2.3.4"} + mock_dns( + scan2, + { + "evilcorp.com": {"TXT": ['"v=spf1 include:cloudprovider.com ~all"']}, + "cloudprovider.com": {"A": ["1.2.3.4"]}, + }, ) events = [e async for e in scan2.async_start()] assert 1 == len( diff --git a/bbot/test/test_step_1/test_manager_deduplication.py b/bbot/test/test_step_1/test_manager_deduplication.py index e046988ae..e8ec69edc 100644 --- a/bbot/test/test_step_1/test_manager_deduplication.py +++ b/bbot/test/test_step_1/test_manager_deduplication.py @@ -3,7 +3,7 @@ @pytest.mark.asyncio -async def test_manager_deduplication(bbot_config, bbot_scanner): +async def test_manager_deduplication(bbot_config, bbot_scanner, mock_dns): class DefaultModule(BaseModule): _name = "default_module" @@ -62,7 +62,7 @@ async def do_scan(*args, _config={}, _dns_mock={}, scan_callback=None, **kwargs) scan.modules["per_hostport_only"] = per_hostport_only scan.modules["per_domain_only"] = per_domain_only if _dns_mock: - scan.helpers.dns.mock_dns(_dns_mock) + mock_dns(scan, _dns_mock) if scan_callback is not None: scan_callback(scan) return ( @@ -76,12 +76,12 @@ async def do_scan(*args, _config={}, _dns_mock={}, scan_callback=None, **kwargs) ) dns_mock_chain = { - ("default_module.test.notreal", "A"): "127.0.0.3", - ("everything_module.test.notreal", "A"): "127.0.0.4", - ("no_suppress_dupes.test.notreal", "A"): "127.0.0.5", - ("accept_dupes.test.notreal", "A"): "127.0.0.6", - ("per_hostport_only.test.notreal", "A"): "127.0.0.7", - ("per_domain_only.test.notreal", "A"): "127.0.0.8", + "default_module.test.notreal": {"A": ["127.0.0.3"]}, + "everything_module.test.notreal": {"A": ["127.0.0.4"]}, + "no_suppress_dupes.test.notreal": {"A": ["127.0.0.5"]}, + "accept_dupes.test.notreal": {"A": ["127.0.0.6"]}, + "per_hostport_only.test.notreal": {"A": ["127.0.0.7"]}, + "per_domain_only.test.notreal": {"A": ["127.0.0.8"]}, } # dns search distance = 1, report distance = 0 diff --git a/bbot/test/test_step_1/test_manager_scope_accuracy.py b/bbot/test/test_step_1/test_manager_scope_accuracy.py index e8e5da391..1175d0608 100644 --- a/bbot/test/test_step_1/test_manager_scope_accuracy.py +++ b/bbot/test/test_step_1/test_manager_scope_accuracy.py @@ -777,7 +777,7 @@ def custom_setup(scan): @pytest.mark.asyncio -async def test_manager_blacklist(bbot_config, bbot_scanner, bbot_httpserver, caplog): +async def test_manager_blacklist(bbot_config, bbot_scanner, bbot_httpserver, caplog, mock_dns): bbot_httpserver.expect_request(uri="/").respond_with_data(response_data="") @@ -791,9 +791,9 @@ async def test_manager_blacklist(bbot_config, bbot_scanner, bbot_httpserver, cap whitelist=["127.0.0.0/29", "test.notreal"], blacklist=["127.0.0.64/29"], ) - scan.helpers.dns.mock_dns({ - ("www-prod.test.notreal", "A"): "127.0.0.66", - ("www-dev.test.notreal", "A"): "127.0.0.22", + mock_dns(scan, { + "www-prod.test.notreal": {"A": ["127.0.0.66"]}, + "www-dev.test.notreal": {"A": ["127.0.0.22"]}, }) events = [e async for e in scan.async_start()] diff --git a/bbot/test/test_step_1/test_modules_basic.py b/bbot/test/test_step_1/test_modules_basic.py index 7a25bb4ea..e226eaea4 100644 --- a/bbot/test/test_step_1/test_modules_basic.py +++ b/bbot/test/test_step_1/test_modules_basic.py @@ -303,7 +303,7 @@ async def test_modules_basic_perdomainonly(scan, helpers, events, bbot_config, b @pytest.mark.asyncio -async def test_modules_basic_stats(helpers, events, bbot_config, bbot_scanner, httpx_mock, monkeypatch): +async def test_modules_basic_stats(helpers, events, bbot_config, bbot_scanner, httpx_mock, monkeypatch, mock_dns): from bbot.modules.base import BaseModule class dummy(BaseModule): @@ -311,23 +311,43 @@ class dummy(BaseModule): watched_events = ["*"] async def handle_event(self, event): + # quick emit events like FINDINGS behave differently than normal ones + # hosts are not speculated from them await self.emit_event( {"host": "www.evilcorp.com", "url": "http://www.evilcorp.com", "description": "asdf"}, "FINDING", event ) + await self.emit_event("https://asdf.evilcorp.com", "URL", event, tags=["status-200"]) scan = bbot_scanner( "evilcorp.com", + modules=["speculate"], config=bbot_config, force_start=True, ) - scan.helpers.dns.mock_dns({("evilcorp.com", "A"): "127.0.254.1", ("www.evilcorp.com", "A"): "127.0.254.2"}) + mock_dns( + scan, + { + "evilcorp.com": {"A": ["127.0.254.1"]}, + "www.evilcorp.com": {"A": ["127.0.254.2"]}, + "asdf.evilcorp.com": {"A": ["127.0.254.3"]}, + }, + ) scan.modules["dummy"] = dummy(scan) events = [e async for e in scan.async_start()] - assert len(events) == 3 + assert len(events) == 6 + + assert scan.stats.events_emitted_by_type == { + "SCAN": 1, + "DNS_NAME": 3, + "URL": 1, + "FINDING": 1, + "ORG_STUB": 1, + "OPEN_TCP_PORT": 1, + } - assert set(scan.stats.module_stats) == {"dummy", "python", "TARGET"} + assert set(scan.stats.module_stats) == {"host", "speculate", "python", "dummy", "TARGET"} target_stats = scan.stats.module_stats["TARGET"] assert target_stats.emitted == {"SCAN": 1, "DNS_NAME": 1} @@ -338,19 +358,17 @@ async def handle_event(self, event): assert target_stats.consumed_total == 0 dummy_stats = scan.stats.module_stats["dummy"] - assert dummy_stats.emitted == {"FINDING": 1} - assert dummy_stats.emitted_total == 1 - assert dummy_stats.produced == {"FINDING": 1} - assert dummy_stats.produced_total == 1 - assert dummy_stats.consumed == {"SCAN": 1, "DNS_NAME": 1} - assert dummy_stats.consumed_total == 2 + assert dummy_stats.emitted == {"FINDING": 1, "URL": 1} + assert dummy_stats.emitted_total == 2 + assert dummy_stats.produced == {"FINDING": 1, "URL": 1} + assert dummy_stats.produced_total == 2 + assert dummy_stats.consumed == {"DNS_NAME": 2, "OPEN_TCP_PORT": 1, "SCAN": 1, "URL": 1} + assert dummy_stats.consumed_total == 5 python_stats = scan.stats.module_stats["python"] assert python_stats.emitted == {} assert python_stats.emitted_total == 0 assert python_stats.produced == {} assert python_stats.produced_total == 0 - assert python_stats.consumed == {"SCAN": 1, "FINDING": 1, "DNS_NAME": 1} - assert python_stats.consumed_total == 3 - - assert scan.stats.events_emitted_by_type == {"SCAN": 1, "FINDING": 1, "DNS_NAME": 1} + assert python_stats.consumed == {"DNS_NAME": 2, "FINDING": 1, "ORG_STUB": 1, "SCAN": 1, "URL": 1} + assert python_stats.consumed_total == 6 diff --git a/bbot/test/test_step_1/test_scan.py b/bbot/test/test_step_1/test_scan.py index fde8dc368..27ebe3404 100644 --- a/bbot/test/test_step_1/test_scan.py +++ b/bbot/test/test_step_1/test_scan.py @@ -9,6 +9,7 @@ async def test_scan( neograph, monkeypatch, bbot_scanner, + mock_dns, ): scan0 = bbot_scanner( "1.1.1.1/31", @@ -55,15 +56,15 @@ async def test_scan( assert not scan2.in_scope("1.0.0.1") dns_table = { - ("1.1.1.1", "PTR"): "one.one.one.one", - ("one.one.one.one", "A"): "1.1.1.1", + "1.1.1.1": {"PTR": ["one.one.one.one"]}, + "one.one.one.one": {"A": ["1.1.1.1"]}, } # make sure DNS resolution works dns_config = OmegaConf.create({"dns_resolution": True}) dns_config = OmegaConf.merge(bbot_config, dns_config) scan4 = bbot_scanner("1.1.1.1", config=dns_config) - scan4.helpers.dns.mock_dns(dns_table) + mock_dns(scan4, dns_table) events = [] async for event in scan4.async_start(): events.append(event) @@ -74,7 +75,7 @@ async def test_scan( no_dns_config = OmegaConf.create({"dns_resolution": False}) no_dns_config = OmegaConf.merge(bbot_config, no_dns_config) scan5 = bbot_scanner("1.1.1.1", config=no_dns_config) - scan5.helpers.dns.mock_dns(dns_table) + mock_dns(scan5, dns_table) events = [] async for event in scan5.async_start(): events.append(event) diff --git a/bbot/test/test_step_2/module_tests/base.py b/bbot/test/test_step_2/module_tests/base.py index e511d7256..be8c282fb 100644 --- a/bbot/test/test_step_2/module_tests/base.py +++ b/bbot/test/test_step_2/module_tests/base.py @@ -8,7 +8,7 @@ from bbot.scanner import Scanner from bbot.modules import module_loader from bbot.core.helpers.misc import rand_string -from ...bbot_fixtures import test_config +from ...bbot_fixtures import test_config, MockResolver log = logging.getLogger("bbot.test.modules") @@ -93,17 +93,17 @@ def set_expect_requests(self, expect_args={}, respond_args={}): def set_expect_requests_handler(self, expect_args=None, request_handler=None): self.httpserver.expect_request(expect_args).respond_with_handler(request_handler) + def mock_dns(self, mock_data, scan=None): + if scan is None: + scan = self.scan + scan.helpers.dns.resolver = MockResolver(mock_data) + @property def module(self): return self.scan.modules[self.name] - def mock_record(self, *args, **kwargs): - return MockRecord(*args, **kwargs) - @pytest_asyncio.fixture - async def module_test( - self, httpx_mock, bbot_httpserver, bbot_httpserver_ssl, monkeypatch, request, configure_mock_resolver - ): + async def module_test(self, httpx_mock, bbot_httpserver, bbot_httpserver_ssl, monkeypatch, request): module_test = self.ModuleTest(self, httpx_mock, bbot_httpserver, bbot_httpserver_ssl, monkeypatch, request) module_test.log.info(f"Starting {self.name} module test") await self.setup_before_prep(module_test) diff --git a/bbot/test/test_step_2/module_tests/test_module_affiliates.py b/bbot/test/test_step_2/module_tests/test_module_affiliates.py index fc9bd1c58..00135e4e4 100644 --- a/bbot/test/test_step_2/module_tests/test_module_affiliates.py +++ b/bbot/test/test_step_2/module_tests/test_module_affiliates.py @@ -6,12 +6,11 @@ class TestAffiliates(ModuleTestBase): config_overrides = {"dns_resolution": True} async def setup_before_prep(self, module_test): - module_test.scan.helpers.dns.mock_dns( + module_test.mock_dns( { - ("8.8.8.8", "PTR"): "dns.google", - ("dns.google", "A"): "8.8.8.8", - ("dns.google", "NS"): "ns1.zdns.google", - ("ns1.zdns.google", "A"): "1.2.3.4", + "8.8.8.8": {"PTR": ["dns.google"]}, + "dns.google": {"A": ["8.8.8.8"], "NS": ["ns1.zdns.google"]}, + "ns1.zdns.google": {"A": ["1.2.3.4"]}, } ) diff --git a/bbot/test/test_step_2/module_tests/test_module_asset_inventory.py b/bbot/test/test_step_2/module_tests/test_module_asset_inventory.py index cc4399266..d58808505 100644 --- a/bbot/test/test_step_2/module_tests/test_module_asset_inventory.py +++ b/bbot/test/test_step_2/module_tests/test_module_asset_inventory.py @@ -8,10 +8,10 @@ class TestAsset_Inventory(ModuleTestBase): modules_overrides = ["asset_inventory", "nmap", "sslcert"] async def setup_before_prep(self, module_test): - module_test.scan.helpers.dns.mock_dns( + module_test.mock_dns( { - ("127.0.0.1", "PTR"): "www.bbottest.notreal", - ("www.bbottest.notreal", "A"): "127.0.0.1", + "127.0.0.1": {"PTR": ["www.bbottest.notreal"]}, + "www.bbottest.notreal": {"A": ["127.0.0.1"]}, } ) diff --git a/bbot/test/test_step_2/module_tests/test_module_baddns.py b/bbot/test/test_step_2/module_tests/test_module_baddns.py index 5f1ad4d7a..57cca7d5f 100644 --- a/bbot/test/test_step_2/module_tests/test_module_baddns.py +++ b/bbot/test/test_step_2/module_tests/test_module_baddns.py @@ -24,10 +24,9 @@ async def setup_after_prep(self, module_test): from bbot.modules import baddns as baddns_module from baddns.lib.whoismanager import WhoisManager - mock_data = {"bad.dns": {"CNAME": ["baddns.azurewebsites.net."]}, "_NXDOMAIN": ["baddns.azurewebsites.net"]} - configure_mock_resolver = module_test.request_fixture.getfixturevalue("configure_mock_resolver") - mock_resolver = configure_mock_resolver(mock_data) - module_test.monkeypatch.setattr(module_test.scan.helpers.dns, "resolver", mock_resolver) + module_test.mock_dns( + {"bad.dns": {"CNAME": ["baddns.azurewebsites.net."]}, "_NXDOMAIN": ["baddns.azurewebsites.net"]} + ) module_test.monkeypatch.setattr(baddns_module.baddns, "select_modules", self.select_modules) module_test.monkeypatch.setattr(WhoisManager, "dispatchWHOIS", self.dispatchWHOIS) @@ -53,10 +52,9 @@ def set_target(self, target): respond_args = {"response_data": "

Oops! We couldn’t find that page.

", "status": 200} module_test.set_expect_requests(expect_args=expect_args, respond_args=respond_args) - mock_data = {"bad.dns": {"CNAME": ["baddns.bigcartel.com."]}, "baddns.bigcartel.com": {"A": ["127.0.0.1"]}} - configure_mock_resolver = module_test.request_fixture.getfixturevalue("configure_mock_resolver") - mock_resolver = configure_mock_resolver(mock_data) - module_test.monkeypatch.setattr(module_test.scan.helpers.dns, "resolver", mock_resolver) + module_test.mock_dns( + {"bad.dns": {"CNAME": ["baddns.bigcartel.com."]}, "baddns.bigcartel.com": {"A": ["127.0.0.1"]}} + ) module_test.monkeypatch.setattr(baddns_module.baddns, "select_modules", self.select_modules) module_test.monkeypatch.setattr(BadDNS_base, "set_target", set_target) module_test.monkeypatch.setattr(WhoisManager, "dispatchWHOIS", self.dispatchWHOIS) diff --git a/bbot/test/test_step_2/module_tests/test_module_baddns_zone.py b/bbot/test/test_step_2/module_tests/test_module_baddns_zone.py index 1a0ef9cf5..349db8ed8 100644 --- a/bbot/test/test_step_2/module_tests/test_module_baddns_zone.py +++ b/bbot/test/test_step_2/module_tests/test_module_baddns_zone.py @@ -31,12 +31,8 @@ def from_xfr(*args, **kwargs): zone = dns.zone.from_text(zone_text, origin="bad.dns.") return zone - mock_data = {"bad.dns": {"NS": ["ns1.bad.dns."]}, "ns1.bad.dns": {"A": ["127.0.0.1"]}} - configure_mock_resolver = module_test.request_fixture.getfixturevalue("configure_mock_resolver") - - mock_resolver = configure_mock_resolver(mock_data) + module_test.mock_dns({"bad.dns": {"NS": ["ns1.bad.dns."]}, "ns1.bad.dns": {"A": ["127.0.0.1"]}}) module_test.monkeypatch.setattr("dns.zone.from_xfr", from_xfr) - module_test.monkeypatch.setattr(module_test.scan.helpers.dns, "resolver", mock_resolver) module_test.monkeypatch.setattr(WhoisManager, "dispatchWHOIS", self.dispatchWHOIS) def check(self, module_test, events): @@ -50,14 +46,13 @@ class TestBaddns_zone_nsec(BaseTestBaddns_zone): async def setup_after_prep(self, module_test): from baddns.lib.whoismanager import WhoisManager - mock_data = { - "bad.dns": {"NSEC": ["asdf.bad.dns"]}, - "asdf.bad.dns": {"NSEC": ["zzzz.bad.dns"]}, - "zzzz.bad.dns": {"NSEC": ["xyz.bad.dns"]}, - } - configure_mock_resolver = module_test.request_fixture.getfixturevalue("configure_mock_resolver") - mock_resolver = configure_mock_resolver(mock_data) - module_test.monkeypatch.setattr(module_test.scan.helpers.dns, "resolver", mock_resolver) + module_test.mock_dns( + { + "bad.dns": {"NSEC": ["asdf.bad.dns"]}, + "asdf.bad.dns": {"NSEC": ["zzzz.bad.dns"]}, + "zzzz.bad.dns": {"NSEC": ["xyz.bad.dns"]}, + } + ) module_test.monkeypatch.setattr(WhoisManager, "dispatchWHOIS", self.dispatchWHOIS) def check(self, module_test, events): diff --git a/bbot/test/test_step_2/module_tests/test_module_internetdb.py b/bbot/test/test_step_2/module_tests/test_module_internetdb.py index 5b232f679..d24cdebc0 100644 --- a/bbot/test/test_step_2/module_tests/test_module_internetdb.py +++ b/bbot/test/test_step_2/module_tests/test_module_internetdb.py @@ -5,11 +5,11 @@ class TestInternetDB(ModuleTestBase): config_overrides = {"dns_resolution": True} async def setup_before_prep(self, module_test): - module_test.scan.helpers.mock_dns( + module_test.mock_dns( { - ("blacklanternsecurity.com", "A"): "1.2.3.4", - ("autodiscover.blacklanternsecurity.com", "A"): "2.3.4.5", - ("mail.blacklanternsecurity.com", "A"): "3.4.5.6", + "blacklanternsecurity.com": {"A": ["1.2.3.4"]}, + "autodiscover.blacklanternsecurity.com": {"A": ["2.3.4.5"]}, + "mail.blacklanternsecurity.com": {"A": ["3.4.5.6"]}, } ) diff --git a/bbot/test/test_step_2/module_tests/test_module_ipneighbor.py b/bbot/test/test_step_2/module_tests/test_module_ipneighbor.py index fbccc69a7..b8ba8331a 100644 --- a/bbot/test/test_step_2/module_tests/test_module_ipneighbor.py +++ b/bbot/test/test_step_2/module_tests/test_module_ipneighbor.py @@ -6,21 +6,9 @@ class TestIPNeighbor(ModuleTestBase): config_overrides = {"scope_report_distance": 1, "dns_resolution": True, "scope_dns_search_distance": 2} async def setup_after_prep(self, module_test): - old_resolve_ip = module_test.scan.helpers.dns._resolve_ip - old_resolve_hostname = module_test.scan.helpers.dns._resolve_hostname - - async def _resolve_ip(query, **kwargs): - if query == "127.0.0.3": - return [module_test.mock_record("asdf.www.bls.notreal", "PTR")], [] - return await old_resolve_ip(query, **kwargs) - - async def _resolve_hostname(query, **kwargs): - if query == "asdf.www.bls.notreal" and kwargs.get("rdtype", "") == "A": - return [module_test.mock_record("127.0.0.3", "A")], [] - return await old_resolve_hostname(query, **kwargs) - - module_test.monkeypatch.setattr(module_test.scan.helpers.dns, "_resolve_ip", _resolve_ip) - module_test.monkeypatch.setattr(module_test.scan.helpers.dns, "_resolve_hostname", _resolve_hostname) + module_test.mock_dns( + {"3.0.0.127.in-addr.arpa": {"PTR": ["asdf.www.bls.notreal"]}, "asdf.www.bls.notreal": {"A": ["127.0.0.3"]}} + ) def check(self, module_test, events): assert any(e.data == "127.0.0.3" for e in events) diff --git a/bbot/test/test_step_2/module_tests/test_module_postman.py b/bbot/test/test_step_2/module_tests/test_module_postman.py index e4c8d8ce6..21f464054 100644 --- a/bbot/test/test_step_2/module_tests/test_module_postman.py +++ b/bbot/test/test_step_2/module_tests/test_module_postman.py @@ -235,7 +235,7 @@ async def new_emit_event(event_data, event_type, **kwargs): await old_emit_event(event_data, event_type, **kwargs) module_test.monkeypatch.setattr(module_test.module, "emit_event", new_emit_event) - module_test.scan.helpers.dns.mock_dns({("asdf.blacklanternsecurity.com", "A"): "127.0.0.1"}) + module_test.mock_dns({"asdf.blacklanternsecurity.com": {"A": ["127.0.0.1"]}}) request_args = dict(uri="/_api/request/28129865-987c8ac8-bfa9-4bab-ade9-88ccf0597862") respond_args = dict(response_data="https://asdf.blacklanternsecurity.com") diff --git a/bbot/test/test_step_2/module_tests/test_module_speculate.py b/bbot/test/test_step_2/module_tests/test_module_speculate.py index db36db97b..0036451e4 100644 --- a/bbot/test/test_step_2/module_tests/test_module_speculate.py +++ b/bbot/test/test_step_2/module_tests/test_module_speculate.py @@ -28,10 +28,10 @@ class TestSpeculate_OpenPorts(ModuleTestBase): config_overrides = {"speculate": True} async def setup_before_prep(self, module_test): - module_test.scan.helpers.dns.mock_dns( + module_test.mock_dns( { - ("evilcorp.com", "A"): "127.0.254.1", - ("asdf.evilcorp.com", "A"): "127.0.254.2", + "evilcorp.com": {"A": "127.0.254.1"}, + "asdf.evilcorp.com": {"A": "127.0.254.2"}, } ) From 5a63b73801c27cd1f888c1b5f59668b44ea6248e Mon Sep 17 00:00:00 2001 From: TheTechromancer Date: Wed, 31 Jan 2024 18:02:43 -0500 Subject: [PATCH 24/35] fix dnscommonsrv tests --- .../test_manager_scope_accuracy.py | 26 +++++++++---------- .../module_tests/test_module_dnscommonsrv.py | 21 +++++++-------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/bbot/test/test_step_1/test_manager_scope_accuracy.py b/bbot/test/test_step_1/test_manager_scope_accuracy.py index 1175d0608..453625a06 100644 --- a/bbot/test/test_step_1/test_manager_scope_accuracy.py +++ b/bbot/test/test_step_1/test_manager_scope_accuracy.py @@ -31,7 +31,7 @@ def bbot_other_httpservers(): @pytest.mark.asyncio -async def test_manager_scope_accuracy(bbot_config, bbot_scanner, bbot_httpserver, bbot_other_httpservers, bbot_httpserver_ssl): +async def test_manager_scope_accuracy(bbot_config, bbot_scanner, bbot_httpserver, bbot_other_httpservers, bbot_httpserver_ssl, mock_dns): """ This test ensures that BBOT correctly handles different scope distance settings. It performs these tests for normal modules, output modules, and their graph variants, @@ -102,7 +102,7 @@ async def do_scan(*args, _config={}, _dns_mock={}, scan_callback=None, **kwargs) scan.modules["dummy_graph_output_module"] = dummy_graph_output_module scan.modules["dummy_graph_batch_output_module"] = dummy_graph_batch_output_module if _dns_mock: - scan.helpers.dns.mock_dns(_dns_mock) + mock_dns(scan, _dns_mock) if scan_callback is not None: scan_callback(scan) return ( @@ -114,12 +114,12 @@ async def do_scan(*args, _config={}, _dns_mock={}, scan_callback=None, **kwargs) ) dns_mock_chain = { - ("test.notreal", "A"): "127.0.0.66", - ("127.0.0.66", "PTR"): "test.notrealzies", - ("test.notrealzies", "CNAME"): "www.test.notreal", - ("www.test.notreal", "A"): "127.0.0.77", - ("127.0.0.77", "PTR"): "test2.notrealzies", - ("test2.notrealzies", "A"): "127.0.0.88", + "test.notreal": {"A": ["127.0.0.66"]}, + "66.0.0.127.in-addr.arpa": {"PTR": ["test.notrealzies"]}, + "test.notrealzies": {"CNAME": ["www.test.notreal"]}, + "www.test.notreal": {"A": ["127.0.0.77"]}, + "77.0.0.127.in-addr.arpa": {"PTR": ["test2.notrealzies"]}, + "test2.notrealzies": {"A": ["127.0.0.88"]}, } # dns search distance = 1, report distance = 0 @@ -240,9 +240,9 @@ async def do_scan(*args, _config={}, _dns_mock={}, scan_callback=None, **kwargs) assert 0 == len([e for e in _graph_output_events if e.type == "IP_ADDRESS" and e.data == "127.0.0.88"]) dns_mock_chain = { - ("test.notreal", "A"): "127.0.0.66", - ("127.0.0.66", "PTR"): "test.notrealzies", - ("test.notrealzies", "A"): "127.0.0.77", + "test.notreal": {"A": ["127.0.0.66"]}, + "66.0.0.127.in-addr.arpa": {"PTR": ["test.notrealzies"]}, + "test.notrealzies": {"A": ["127.0.0.77"]}, } class DummyVulnModule(BaseModule): @@ -667,7 +667,7 @@ def custom_setup(scan): "127.0.0.0/31", modules=["speculate", "sslcert"], _config={"dns_resolution": False, "scope_report_distance": 0, "internal_modules": {"speculate": {"ports": "9999"}}}, - _dns_mock={("www.bbottest.notreal", "A"): "127.0.1.0", ("test.notreal", "A"): "127.0.0.1"}, + _dns_mock={"www.bbottest.notreal": {"A": ["127.0.1.0"]}, "test.notreal": {"A": ["127.0.0.1"]}}, ) assert len(events) == 6 @@ -727,7 +727,7 @@ def custom_setup(scan): modules=["speculate", "sslcert"], whitelist=["127.0.1.0"], _config={"dns_resolution": False, "scope_report_distance": 0, "internal_modules": {"speculate": {"ports": "9999"}}}, - _dns_mock={("www.bbottest.notreal", "A"): "127.0.0.1", ("test.notreal", "A"): "127.0.1.0"}, + _dns_mock={"www.bbottest.notreal": {"A": ["127.0.0.1"]}, "test.notreal": {"A": ["127.0.1.0"]}}, ) assert len(events) == 3 diff --git a/bbot/test/test_step_2/module_tests/test_module_dnscommonsrv.py b/bbot/test/test_step_2/module_tests/test_module_dnscommonsrv.py index bdb6f1207..aaf26664c 100644 --- a/bbot/test/test_step_2/module_tests/test_module_dnscommonsrv.py +++ b/bbot/test/test_step_2/module_tests/test_module_dnscommonsrv.py @@ -3,22 +3,21 @@ class TestDNSCommonSRV(ModuleTestBase): targets = ["blacklanternsecurity.notreal"] + config_overrides = {"dns_resolution": True} async def setup_after_prep(self, module_test): - old_resolve_fn = module_test.scan.helpers.dns.resolve - - async def resolve(query, **kwargs): - if ( - query == "_ldap._tcp.gc._msdcs.blacklanternsecurity.notreal" - and kwargs.get("type", "").upper() == "SRV" - ): - return {"asdf.blacklanternsecurity.notreal"} - return await old_resolve_fn(query, **kwargs) - - module_test.monkeypatch.setattr(module_test.scan.helpers.dns, "resolve", resolve) + module_test.mock_dns( + { + "_ldap._tcp.gc._msdcs.blacklanternsecurity.notreal": { + "SRV": ["0 100 3268 asdf.blacklanternsecurity.notreal"] + }, + "asdf.blacklanternsecurity.notreal": {"A": "1.2.3.4"}, + } + ) def check(self, module_test, events): assert any( e.data == "_ldap._tcp.gc._msdcs.blacklanternsecurity.notreal" for e in events ), "Failed to detect subdomain" + assert any(e.data == "asdf.blacklanternsecurity.notreal" for e in events), "Failed to detect subdomain" assert not any(e.data == "_ldap._tcp.dc._msdcs.blacklanternsecurity.notreal" for e in events), "False positive" From ae7e41fa012f8cd66ca39216c3d0071bfa96bea6 Mon Sep 17 00:00:00 2001 From: TheTechromancer Date: Thu, 1 Feb 2024 13:01:28 -0500 Subject: [PATCH 25/35] fix tests --- bbot/test/test_step_1/test_modules_basic.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/bbot/test/test_step_1/test_modules_basic.py b/bbot/test/test_step_1/test_modules_basic.py index e226eaea4..8784923ed 100644 --- a/bbot/test/test_step_1/test_modules_basic.py +++ b/bbot/test/test_step_1/test_modules_basic.py @@ -337,14 +337,21 @@ async def handle_event(self, event): events = [e async for e in scan.async_start()] assert len(events) == 6 + assert 1 == len([e for e in events if e.type == "SCAN"]) + assert 2 == len([e for e in events if e.type == "DNS_NAME"]) + assert 1 == len([e for e in events if e.type == "DNS_NAME" and e.data == "evilcorp.com"]) + # the reason we don't have a DNS_NAME for www.evilcorp.com is because FINDING.quick_emit = True + assert 0 == len([e for e in events if e.type == "DNS_NAME" and e.data == "www.evilcorp.com"]) + assert 1 == len([e for e in events if e.type == "DNS_NAME" and e.data == "asdf.evilcorp.com"]) + assert 1 == len([e for e in events if e.type == "ORG_STUB" and e.data == "evilcorp"]) + assert 1 == len([e for e in events if e.type == "FINDING"]) assert scan.stats.events_emitted_by_type == { "SCAN": 1, - "DNS_NAME": 3, + "DNS_NAME": 2, "URL": 1, "FINDING": 1, "ORG_STUB": 1, - "OPEN_TCP_PORT": 1, } assert set(scan.stats.module_stats) == {"host", "speculate", "python", "dummy", "TARGET"} From 652b233df1e472b780972491cc344f34de7cee34 Mon Sep 17 00:00:00 2001 From: TheTechromancer Date: Thu, 1 Feb 2024 13:56:56 -0500 Subject: [PATCH 26/35] fix PTR tests --- bbot/test/test_step_1/test_scan.py | 2 +- bbot/test/test_step_2/module_tests/test_module_affiliates.py | 2 +- .../test_step_2/module_tests/test_module_asset_inventory.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bbot/test/test_step_1/test_scan.py b/bbot/test/test_step_1/test_scan.py index 27ebe3404..9aa89ffe2 100644 --- a/bbot/test/test_step_1/test_scan.py +++ b/bbot/test/test_step_1/test_scan.py @@ -56,7 +56,7 @@ async def test_scan( assert not scan2.in_scope("1.0.0.1") dns_table = { - "1.1.1.1": {"PTR": ["one.one.one.one"]}, + "1.1.1.1.in-addr.arpa": {"PTR": ["one.one.one.one"]}, "one.one.one.one": {"A": ["1.1.1.1"]}, } diff --git a/bbot/test/test_step_2/module_tests/test_module_affiliates.py b/bbot/test/test_step_2/module_tests/test_module_affiliates.py index 00135e4e4..4afd4cd29 100644 --- a/bbot/test/test_step_2/module_tests/test_module_affiliates.py +++ b/bbot/test/test_step_2/module_tests/test_module_affiliates.py @@ -8,7 +8,7 @@ class TestAffiliates(ModuleTestBase): async def setup_before_prep(self, module_test): module_test.mock_dns( { - "8.8.8.8": {"PTR": ["dns.google"]}, + "8.8.8.8.in-addr.arpa": {"PTR": ["dns.google"]}, "dns.google": {"A": ["8.8.8.8"], "NS": ["ns1.zdns.google"]}, "ns1.zdns.google": {"A": ["1.2.3.4"]}, } diff --git a/bbot/test/test_step_2/module_tests/test_module_asset_inventory.py b/bbot/test/test_step_2/module_tests/test_module_asset_inventory.py index d58808505..b63de31ec 100644 --- a/bbot/test/test_step_2/module_tests/test_module_asset_inventory.py +++ b/bbot/test/test_step_2/module_tests/test_module_asset_inventory.py @@ -10,7 +10,7 @@ class TestAsset_Inventory(ModuleTestBase): async def setup_before_prep(self, module_test): module_test.mock_dns( { - "127.0.0.1": {"PTR": ["www.bbottest.notreal"]}, + "1.0.0.127.in-addr.arpa": {"PTR": ["www.bbottest.notreal"]}, "www.bbottest.notreal": {"A": ["127.0.0.1"]}, } ) From e5d88dcf9d6a02b1ef3a8725da9e3c83fb52c75d Mon Sep 17 00:00:00 2001 From: TheTechromancer Date: Thu, 1 Feb 2024 14:18:55 -0500 Subject: [PATCH 27/35] fix aggregate tests --- bbot/test/test_step_2/module_tests/test_module_aggregate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/test/test_step_2/module_tests/test_module_aggregate.py b/bbot/test/test_step_2/module_tests/test_module_aggregate.py index 59f5370b3..7a41fe022 100644 --- a/bbot/test/test_step_2/module_tests/test_module_aggregate.py +++ b/bbot/test/test_step_2/module_tests/test_module_aggregate.py @@ -5,7 +5,7 @@ class TestAggregate(ModuleTestBase): config_overrides = {"dns_resolution": True, "scope_report_distance": 1} async def setup_before_prep(self, module_test): - module_test.scan.helpers.dns.mock_dns({("blacklanternsecurity.com", "A"): "1.2.3.4"}) + module_test.mock_dns({"blacklanternsecurity.com": {"A": ["1.2.3.4"]}}) def check(self, module_test, events): filename = next(module_test.scan.home.glob("scan-stats-table*.txt")) From 5f8d266d8a46847563adc87be82f2a71e970574d Mon Sep 17 00:00:00 2001 From: TheTechromancer Date: Thu, 1 Feb 2024 17:59:50 -0500 Subject: [PATCH 28/35] fix speculate tests --- bbot/test/test_step_2/module_tests/test_module_speculate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bbot/test/test_step_2/module_tests/test_module_speculate.py b/bbot/test/test_step_2/module_tests/test_module_speculate.py index 0036451e4..2dcafaddc 100644 --- a/bbot/test/test_step_2/module_tests/test_module_speculate.py +++ b/bbot/test/test_step_2/module_tests/test_module_speculate.py @@ -30,8 +30,8 @@ class TestSpeculate_OpenPorts(ModuleTestBase): async def setup_before_prep(self, module_test): module_test.mock_dns( { - "evilcorp.com": {"A": "127.0.254.1"}, - "asdf.evilcorp.com": {"A": "127.0.254.2"}, + "evilcorp.com": {"A": ["127.0.254.1"]}, + "asdf.evilcorp.com": {"A": ["127.0.254.2"]}, } ) From 0bcf77f099e962e48f2e541ee96b1225db44a005 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Wed, 7 Feb 2024 17:19:28 -0500 Subject: [PATCH 29/35] normalizing tag names --- bbot/modules/baddns.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bbot/modules/baddns.py b/bbot/modules/baddns.py index e2270b4f6..80caf670a 100644 --- a/bbot/modules/baddns.py +++ b/bbot/modules/baddns.py @@ -61,14 +61,16 @@ async def handle_event(self, event): "description": f"{r_dict['description']}. Confidence: [{r_dict['confidence']}] Signature: [{r_dict['signature']}] Indicator: [{r_dict['indicator']}] Trigger: [{r_dict['trigger']}] baddns Module: [{r_dict['module']}]", "host": str(event.host), } - await self.emit_event(data, "VULNERABILITY", event, tags=[f"baddns-{ModuleClass.name}"]) + await self.emit_event( + data, "VULNERABILITY", event, tags=[f"baddns-{ModuleClass.name.lower()}"] + ) elif r_dict["confidence"] in ["UNLIKELY", "POSSIBLE"] and not self.only_high_confidence: data = { "description": f"{r_dict['description']} Confidence: [{r_dict['confidence']}] Signature: [{r_dict['signature']}] Indicator: [{r_dict['indicator']}] Trigger: [{r_dict['trigger']}] baddns Module: [{r_dict['module']}]", "host": str(event.host), } - await self.emit_event(data, "FINDING", event, tags=[f"baddns-{ModuleClass.name}"]) + await self.emit_event(data, "FINDING", event, tags=[f"baddns-{ModuleClass.name.lower()}"]) else: self.warning(f"Got unrecognized confidence level: {r['confidence']}") @@ -76,5 +78,5 @@ async def handle_event(self, event): if found_domains: for found_domain in found_domains: await self.emit_event( - found_domain, "DNS_NAME", event, tags=[f"baddns-{ModuleClass.name}"] + found_domain, "DNS_NAME", event, tags=[f"baddns-{ModuleClass.name.lower()}"] ) From fa6be6e65ac559ebc76baf3feefe3c3bab6199b9 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Wed, 7 Feb 2024 17:20:51 -0500 Subject: [PATCH 30/35] baddns version roll --- bbot/modules/baddns.py | 2 +- bbot/modules/baddns_zone.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bbot/modules/baddns.py b/bbot/modules/baddns.py index 80caf670a..2458a51de 100644 --- a/bbot/modules/baddns.py +++ b/bbot/modules/baddns.py @@ -19,7 +19,7 @@ class baddns(BaseModule): "only_high_confidence": "Do not emit low-confidence or generic detections", } max_event_handlers = 8 - deps_pip = ["baddns~=1.0.702"] + deps_pip = ["baddns~=1.0.707"] def select_modules(self): selected_modules = [] diff --git a/bbot/modules/baddns_zone.py b/bbot/modules/baddns_zone.py index a7aac1f74..8dc127f3f 100644 --- a/bbot/modules/baddns_zone.py +++ b/bbot/modules/baddns_zone.py @@ -18,7 +18,7 @@ class baddns_zone(baddns_module): "only_high_confidence": "Do not emit low-confidence or generic detections", } max_event_handlers = 8 - deps_pip = ["baddns~=1.0.702"] + deps_pip = ["baddns~=1.0.707"] def select_modules(self): selected_modules = [] From ba9236a16046e363873d0a6cb9b27c7160d1451a Mon Sep 17 00:00:00 2001 From: liquidsec Date: Wed, 7 Feb 2024 17:32:36 -0500 Subject: [PATCH 31/35] fixing bug with tag name --- bbot/modules/baddns.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bbot/modules/baddns.py b/bbot/modules/baddns.py index 2458a51de..501c8c708 100644 --- a/bbot/modules/baddns.py +++ b/bbot/modules/baddns.py @@ -62,7 +62,7 @@ async def handle_event(self, event): "host": str(event.host), } await self.emit_event( - data, "VULNERABILITY", event, tags=[f"baddns-{ModuleClass.name.lower()}"] + data, "VULNERABILITY", event, tags=[f"baddns-{module_instance.name.lower()}"] ) elif r_dict["confidence"] in ["UNLIKELY", "POSSIBLE"] and not self.only_high_confidence: @@ -70,7 +70,7 @@ async def handle_event(self, event): "description": f"{r_dict['description']} Confidence: [{r_dict['confidence']}] Signature: [{r_dict['signature']}] Indicator: [{r_dict['indicator']}] Trigger: [{r_dict['trigger']}] baddns Module: [{r_dict['module']}]", "host": str(event.host), } - await self.emit_event(data, "FINDING", event, tags=[f"baddns-{ModuleClass.name.lower()}"]) + await self.emit_event(data, "FINDING", event, tags=[f"baddns-{module_instance.name.lower()}"]) else: self.warning(f"Got unrecognized confidence level: {r['confidence']}") @@ -78,5 +78,5 @@ async def handle_event(self, event): if found_domains: for found_domain in found_domains: await self.emit_event( - found_domain, "DNS_NAME", event, tags=[f"baddns-{ModuleClass.name.lower()}"] + found_domain, "DNS_NAME", event, tags=[f"baddns-{module_instance.name.lower()}"] ) From 0097506c96da2d07ca5e4ded37d9c073da7438d4 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Wed, 7 Feb 2024 19:51:55 -0500 Subject: [PATCH 32/35] black --- bbot/modules/baddns.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bbot/modules/baddns.py b/bbot/modules/baddns.py index 501c8c708..f448588ae 100644 --- a/bbot/modules/baddns.py +++ b/bbot/modules/baddns.py @@ -70,7 +70,9 @@ async def handle_event(self, event): "description": f"{r_dict['description']} Confidence: [{r_dict['confidence']}] Signature: [{r_dict['signature']}] Indicator: [{r_dict['indicator']}] Trigger: [{r_dict['trigger']}] baddns Module: [{r_dict['module']}]", "host": str(event.host), } - await self.emit_event(data, "FINDING", event, tags=[f"baddns-{module_instance.name.lower()}"]) + await self.emit_event( + data, "FINDING", event, tags=[f"baddns-{module_instance.name.lower()}"] + ) else: self.warning(f"Got unrecognized confidence level: {r['confidence']}") From b627499a8f2bdebc8a6c346c924cf3899d1e2b69 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Thu, 8 Feb 2024 15:22:15 -0500 Subject: [PATCH 33/35] actually bumping version --- bbot/modules/baddns.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bbot/modules/baddns.py b/bbot/modules/baddns.py index f448588ae..ad5786ff8 100644 --- a/bbot/modules/baddns.py +++ b/bbot/modules/baddns.py @@ -1,4 +1,5 @@ from baddns.base import get_all_modules +from baddns.lib.loader import load_signatures from .base import BaseModule import asyncio @@ -33,18 +34,19 @@ async def setup(self): if self.custom_nameservers: self.custom_nameservers = self.helpers.chain_lists(self.custom_nameservers) self.only_high_confidence = self.config.get("only_high_confidence", False) + self.signatures = load_signatures() return True async def handle_event(self, event): tasks = [] - for ModuleClass in self.select_modules(): module_instance = ModuleClass( event.data, http_client_class=self.scan.helpers.web.AsyncClient, dns_client=self.scan.helpers.dns.resolver, custom_nameservers=self.custom_nameservers, + signatures=self.signatures, ) tasks.append((module_instance, asyncio.create_task(module_instance.dispatch()))) From 0f56b0951cfab9e49b047f5e1f5f7371bbf8a8a4 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Thu, 8 Feb 2024 15:24:55 -0500 Subject: [PATCH 34/35] \!actually\! bumping version :D --- bbot/modules/baddns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/modules/baddns.py b/bbot/modules/baddns.py index ad5786ff8..9abfebc84 100644 --- a/bbot/modules/baddns.py +++ b/bbot/modules/baddns.py @@ -20,7 +20,7 @@ class baddns(BaseModule): "only_high_confidence": "Do not emit low-confidence or generic detections", } max_event_handlers = 8 - deps_pip = ["baddns~=1.0.707"] + deps_pip = ["baddns~=1.1.0"] def select_modules(self): selected_modules = [] From 8709d6a26441a404200512223aad3a167cbf3b35 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Thu, 8 Feb 2024 16:32:21 -0500 Subject: [PATCH 35/35] baddns_zone version --- bbot/modules/baddns_zone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/modules/baddns_zone.py b/bbot/modules/baddns_zone.py index 8dc127f3f..a42fe2e21 100644 --- a/bbot/modules/baddns_zone.py +++ b/bbot/modules/baddns_zone.py @@ -18,7 +18,7 @@ class baddns_zone(baddns_module): "only_high_confidence": "Do not emit low-confidence or generic detections", } max_event_handlers = 8 - deps_pip = ["baddns~=1.0.707"] + deps_pip = ["baddns~=1.1.0"] def select_modules(self): selected_modules = []