From 16f12d0c50f01af8a9fd810a1852d174afe9b91f Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 31 Jan 2025 14:41:20 -0500 Subject: [PATCH 01/79] update issue template --- .github/ISSUE_TEMPLATE/bug_report.md | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 780ad1b26e..55fccf323f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,24 +7,18 @@ assignees: "" --- **Describe the bug** -What happened? - -**Expected behavior** -What was supposed to happen? +What happened vs what was expected? **BBOT Command** Example: `bbot -m httpx -t evilcorp.com` **OS, BBOT Installation Method + Version** Example: `OS: Arch Linux, Installation method: pip, BBOT version: 1.0.3.545` -Note: You can get the bbot version with `bbot --version` -Note: Windows is **not** supported. We have successfully used BBOT on Docker Desktop in the past, however Windows is highly problematic so if you choose this path you are on your own. +Note: You can get the BBOT version with `bbot --version` +Note: BBOT is designed from the ground up to run on Linux. Windows and MacOS are not officially supported. If you are using one of these platforms, it's recommended to use Docker. **BBOT Config** -Attach your BBOT config (`bbot --current-config`). - -**Logs** -If possible, produce the bug while `--debug` is enabled, and attach the relevant parts of `~/.bbot/logs/bbot.debug.log` +Attach your full BBOT preset (to show it, add `--current-preset` to your BBOT command). -**Screenshots** -If applicable, add screenshots to help explain your problem. +**Logs/Screenshots** +If possible, produce the bug while `--debug` is enabled, and attach the relevant parts of the output. From 3c10b346fc0c2bd0b1523248d32084c618ad9512 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Fri, 31 Jan 2025 19:17:41 -0500 Subject: [PATCH 02/79] adding subwords capability to ffuf_shortnames, adding ignore_case option to ffuf --- bbot/modules/deadly/ffuf.py | 19 +++-- bbot/modules/ffuf_shortnames.py | 78 ++++++++++++++++--- bbot/presets/web/dotnet-audit.yml | 3 + .../module_tests/test_module_ffuf.py | 24 ++++++ .../test_module_ffuf_shortnames.py | 19 +++++ 5 files changed, 125 insertions(+), 18 deletions(-) diff --git a/bbot/modules/deadly/ffuf.py b/bbot/modules/deadly/ffuf.py index 0351fe93aa..9db80af239 100644 --- a/bbot/modules/deadly/ffuf.py +++ b/bbot/modules/deadly/ffuf.py @@ -17,6 +17,7 @@ class ffuf(BaseModule): "lines": 5000, "max_depth": 0, "extensions": "", + "ignore_case": False, } options_desc = { @@ -24,6 +25,7 @@ class ffuf(BaseModule): "lines": "take only the first N lines from the wordlist when finding directories", "max_depth": "the maximum directory depth to attempt to solve", "extensions": "Optionally include a list of extensions to extend the keyword with (comma separated)", + "ignore_case": "Only put lowercase words into the wordlist", } deps_common = ["ffuf"] @@ -301,11 +303,13 @@ async def execute_ffuf( ] if len(pre_emit_temp_canary) == 0: yield found_json + break + else: - self.warning( - "Baseline changed mid-scan. This is probably due to a WAF turning on a block against you." + self.verbose( + "Baseline changed mid-scan after multiple attempts. This is probably due to a WAF turning on a block against you." ) - self.warning(f"Aborting the current run against [{url}]") + self.verbose(f"Aborting the current run against [{url}]") return yield found_json @@ -328,7 +332,8 @@ def generate_templist(self, prefix=None): return self.helpers.tempfile(virtual_file, pipe=False), len(virtual_file) def generate_wordlist(self, wordlist_file): - wordlist = [] + wordlist_set = set() # Use a set to avoid duplicates + ignore_case = self.config.get("ignore_case", False) # Get the ignore_case option for line in self.helpers.read_file(wordlist_file): line = line.strip() if not line: @@ -339,5 +344,7 @@ def generate_wordlist(self, wordlist_file): if any(x in line for x in self.banned_characters): self.debug(f"Skipping adding [{line}] to wordlist because it has a banned character") continue - wordlist.append(line) - return wordlist + if ignore_case: + line = line.lower() # Convert to lowercase if ignore_case is enabled + wordlist_set.add(line) # Add to set to handle duplicates + return list(wordlist_set) # Convert set back to list before returning diff --git a/bbot/modules/ffuf_shortnames.py b/bbot/modules/ffuf_shortnames.py index 23d6ac6e33..bbada22343 100644 --- a/bbot/modules/ffuf_shortnames.py +++ b/bbot/modules/ffuf_shortnames.py @@ -1,7 +1,9 @@ import pickle +import nltk import re import random import string +import logging from bbot.modules.deadly.ffuf import ffuf @@ -9,7 +11,7 @@ class ffuf_shortnames(ffuf): watched_events = ["URL_HINT"] produced_events = ["URL_UNVERIFIED"] - deps_pip = ["numpy"] + deps_pip = ["numpy", "nltk"] flags = ["aggressive", "active", "iis-shortnames", "web-thorough"] meta = { "description": "Use ffuf in combination IIS shortnames", @@ -26,6 +28,7 @@ class ffuf_shortnames(ffuf): "ignore_redirects": True, "find_common_prefixes": False, "find_delimiters": True, + "find_subwords": False, "max_predictions": 250, } @@ -38,21 +41,25 @@ class ffuf_shortnames(ffuf): "ignore_redirects": "Explicitly ignore redirects (301,302)", "find_common_prefixes": "Attempt to automatically detect common prefixes and make additional ffuf runs against them", "find_delimiters": "Attempt to detect common delimiters and make additional ffuf runs against them", + "find_subwords": "", "max_predictions": "The maximum number of predictions to generate per shortname prefix", } deps_common = ["ffuf"] - in_scope_only = True - def generate_templist(self, prefix, shortname_type): - virtual_file = [] + supplementary_words = ["html", "ajax", "xml", "json", "api"] + + def generate_templist(self, hint, shortname_type): + virtual_file = set() # Use a set to avoid duplicates - for prediction, score in self.predict(prefix, self.max_predictions, model=shortname_type): - self.debug(f"Got prediction: [{prediction}] from prefix [{prefix}] with score [{score}]") - virtual_file.append(prediction) - virtual_file.append(self.canary) - return self.helpers.tempfile(virtual_file, pipe=False), len(virtual_file) + for prediction, score in self.predict(hint, self.max_predictions, model=shortname_type): + prediction_lower = prediction.lower() # Convert to lowercase + self.debug(f"Got prediction: [{prediction_lower}] from prefix [{hint}] with score [{score}]") + virtual_file.add(prediction_lower) # Add to set to ensure uniqueness + + virtual_file.add(self.canary.lower()) # Ensure canary is also lowercase + return self.helpers.tempfile(list(virtual_file), pipe=False), len(virtual_file) def predict(self, prefix, n=25, model="endpoint"): predictor_name = f"{model}_predictor" @@ -92,6 +99,7 @@ async def setup(self): self.wordlist_extensions = await self.helpers.wordlist(wordlist_extensions) self.ignore_redirects = self.config.get("ignore_redirects") self.max_predictions = self.config.get("max_predictions") + self.find_subwords = self.config.get("find_subwords") class MinimalWordPredictor: def __init__(self): @@ -116,13 +124,12 @@ def find_class(self, module, name): return MinimalWordPredictor return super().find_class(module, name) - endpoint_model = await self.helpers.download( + endpoint_model = await self.helpers.wordlist( "https://raw.githubusercontent.com/blacklanternsecurity/wordpredictor/refs/heads/main/trained_models/endpoints.bin" ) - directory_model = await self.helpers.download( + directory_model = await self.helpers.wordlist( "https://raw.githubusercontent.com/blacklanternsecurity/wordpredictor/refs/heads/main/trained_models/directories.bin" ) - self.debug(f"Loading endpoint model from: {endpoint_model}") with open(endpoint_model, "rb") as f: unpickler = CustomUnpickler(f) @@ -133,8 +140,23 @@ def find_class(self, module, name): unpickler = CustomUnpickler(f) self.directory_predictor = unpickler.load() + self.subword_list = [] + if self.find_subwords: + self.debug("find_subwords is enabled, downloading nltk data") + self.nltk_dir = self.helpers.tools_dir / "nltk_data" + + nltk.download("words", download_dir=self.nltk_dir, quiet=True) + + from nltk.corpus import words + + self.subword_list = {word.lower() for word in words.words() if 3 <= len(word) <= 5} + self.debug(f"Created subword_list with {len(self.subword_list)} words") + self.subword_list = self.subword_list.union(self.supplementary_words) + self.debug(f"Extended subword_list with supplementary words, total size: {len(self.subword_list)}") + self.per_host_collection = {} self.shortname_to_event = {} + return True def build_extension_list(self, event): @@ -163,6 +185,14 @@ async def filter_event(self, event): return False, "its parent event is not of type URL" return True + def find_subword(self, word): + for i in range(len(word), 2, -1): # Start from full length down to 3 characters + candidate = word[:i] + if candidate in self.subword_list: + leftover = word[i:] + return candidate, leftover + return None, word # No match found, return None and the original word + async def handle_event(self, event): filename_hint = re.sub(r"~\d", "", event.parsed_url.path.rsplit(".", 1)[0].split("/")[-1]).lower() @@ -256,6 +286,30 @@ async def handle_event(self, event): context=f'{{module}} brute-forced {ext.upper()} files with detected prefix "{ffuf_prefix}" and found {{event.type}}: {{event.data}}', ) + if self.config.get("find_subwords"): + subword, suffix = self.find_subword(filename_hint) + if "shortname-directory" in event.tags: + tempfile, tempfile_len = self.generate_templist(suffix, "directory") + async for r in self.execute_ffuf(tempfile, root_url, prefix=subword, exts=["/"]): + await self.emit_event( + r["url"], + "URL_UNVERIFIED", + parent=event, + tags=[f"status-{r['status']}"], + context=f'{{module}} brute-forced directories with detected subword "{subword}" and found {{event.type}}: {{event.data}}', + ) + elif "shortname-endpoint" in event.tags: + for ext in used_extensions: + tempfile, tempfile_len = self.generate_templist(suffix, "endpoint") + async for r in self.execute_ffuf(tempfile, root_url, prefix=subword, suffix=f".{ext}"): + await self.emit_event( + r["url"], + "URL_UNVERIFIED", + parent=event, + tags=[f"status-{r['status']}"], + context=f'{{module}} brute-forced {ext.upper()} files with detected subword "{subword}" and found {{event.type}}: {{event.data}}', + ) + async def finish(self): if self.config.get("find_common_prefixes"): per_host_collection = dict(self.per_host_collection) diff --git a/bbot/presets/web/dotnet-audit.yml b/bbot/presets/web/dotnet-audit.yml index d80cafb396..993d6dd198 100644 --- a/bbot/presets/web/dotnet-audit.yml +++ b/bbot/presets/web/dotnet-audit.yml @@ -17,6 +17,9 @@ config: modules: ffuf: extensions: asp,aspx,ashx,asmx,ascx + extensions_ignore_case: True + ffuf_shortnames: + find_subwords: True telerik: exploit_RAU_crypto: True include_subdirs: True # Run against every directory, not the default first received URL per-host diff --git a/bbot/test/test_step_2/module_tests/test_module_ffuf.py b/bbot/test/test_step_2/module_tests/test_module_ffuf.py index b1296a5b9a..3df659e159 100644 --- a/bbot/test/test_step_2/module_tests/test_module_ffuf.py +++ b/bbot/test/test_step_2/module_tests/test_module_ffuf.py @@ -45,6 +45,30 @@ def check(self, module_test, events): assert not any(e.type == "URL_UNVERIFIED" and "11111111" in e.data for e in events) +class TestFFUF_ignorecase(TestFFUF): + test_wordlist = ["11111111", "Admin", "admin", "zzzjunkword2"] + config_overrides = { + "modules": {"ffuf": {"wordlist": tempwordlist(test_wordlist), "extensions": "php", "ignore_case": True}} + } + + async def setup_before_prep(self, module_test): + expect_args = {"method": "GET", "uri": "/admin"} + respond_args = {"response_data": "alive admin page"} + module_test.set_expect_requests(expect_args=expect_args, respond_args=respond_args) + + expect_args = {"method": "GET", "uri": "/Admin"} + respond_args = {"response_data": "alive admin page"} + module_test.set_expect_requests(expect_args=expect_args, respond_args=respond_args) + + expect_args = {"method": "GET", "uri": "/"} + respond_args = {"response_data": "alive"} + module_test.set_expect_requests(expect_args=expect_args, respond_args=respond_args) + + def check(self, module_test, events): + assert any(e.type == "URL_UNVERIFIED" and "admin" in e.data for e in events) + assert not any(e.type == "URL_UNVERIFIED" and "Admin" in e.data for e in events) + + class TestFFUFHeaders(TestFFUF): test_wordlist = ["11111111", "console", "junkword1", "zzzjunkword2"] config_overrides = { diff --git a/bbot/test/test_step_2/module_tests/test_module_ffuf_shortnames.py b/bbot/test/test_step_2/module_tests/test_module_ffuf_shortnames.py index 8218eaa6a3..a10d8a1eae 100644 --- a/bbot/test/test_step_2/module_tests/test_module_ffuf_shortnames.py +++ b/bbot/test/test_step_2/module_tests/test_module_ffuf_shortnames.py @@ -8,6 +8,7 @@ class TestFFUFShortnames(ModuleTestBase): "modules": { "ffuf_shortnames": { "find_common_prefixes": True, + "find_subwords": True, "wordlist": tempwordlist(test_wordlist), } } @@ -142,6 +143,16 @@ async def setup_after_prep(self, module_test): tags=["shortname-endpoint"], ) ) + + seed_events.append( + module_test.scan.make_event( + "http://127.0.0.1:8888/newpro~1.asp", + "URL_HINT", + parent_event, + module="iis_shortnames", + tags=["shortname-endpoint"], + ) + ) module_test.scan.target.seeds.events = set(seed_events) expect_args = {"method": "GET", "uri": "/administrator.aspx"} @@ -172,6 +183,10 @@ async def setup_after_prep(self, module_test): respond_args = {"response_data": "alive"} module_test.set_expect_requests(expect_args=expect_args, respond_args=respond_args) + expect_args = {"method": "GET", "uri": "/newproxy.aspx"} + respond_args = {"response_data": "alive"} + module_test.set_expect_requests(expect_args=expect_args, respond_args=respond_args) + def check(self, module_test, events): basic_detection = False directory_detection = False @@ -180,6 +195,7 @@ def check(self, module_test, events): directory_delimiter_detection = False prefix_delimiter_detection = False short_extensions_detection = False + subword_detection = False for e in events: if e.type == "URL_UNVERIFIED": @@ -197,6 +213,8 @@ def check(self, module_test, events): prefix_delimiter_detection = True if e.data == "http://127.0.0.1:8888/short.pl": short_extensions_detection = True + if e.data == "http://127.0.0.1:8888/newproxy.aspx": + subword_detection = True assert basic_detection assert directory_detection @@ -205,3 +223,4 @@ def check(self, module_test, events): assert directory_delimiter_detection assert prefix_delimiter_detection assert short_extensions_detection + assert subword_detection From b5dac083f82c8e71f8258208aca81c80661b21c3 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Fri, 31 Jan 2025 19:22:14 -0500 Subject: [PATCH 03/79] adjust log message --- bbot/modules/deadly/ffuf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/modules/deadly/ffuf.py b/bbot/modules/deadly/ffuf.py index 9db80af239..dae31b5ca1 100644 --- a/bbot/modules/deadly/ffuf.py +++ b/bbot/modules/deadly/ffuf.py @@ -307,7 +307,7 @@ async def execute_ffuf( else: self.verbose( - "Baseline changed mid-scan after multiple attempts. This is probably due to a WAF turning on a block against you." + "Baseline changed mid-scan, or all strings send to this directory are coming back as a match. This may be due to a WAF turning on a block against you, or an unusual configuration on the web server." ) self.verbose(f"Aborting the current run against [{url}]") return From 16c765f72405b9ab2ad9cf27a714ae6192b2f875 Mon Sep 17 00:00:00 2001 From: blsaccess Date: Sat, 1 Feb 2025 00:24:16 +0000 Subject: [PATCH 04/79] Update trufflehog --- bbot/modules/trufflehog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/modules/trufflehog.py b/bbot/modules/trufflehog.py index 8a9076d4e4..c992285637 100644 --- a/bbot/modules/trufflehog.py +++ b/bbot/modules/trufflehog.py @@ -13,7 +13,7 @@ class trufflehog(BaseModule): } options = { - "version": "3.88.3", + "version": "3.88.4", "config": "", "only_verified": True, "concurrency": 8, From a9e06e0036968d192fba59637a857d1d1a5451da Mon Sep 17 00:00:00 2001 From: liquidsec Date: Fri, 31 Jan 2025 19:31:40 -0500 Subject: [PATCH 05/79] adding debug message --- bbot/modules/ffuf_shortnames.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/modules/ffuf_shortnames.py b/bbot/modules/ffuf_shortnames.py index bbada22343..8f2995df77 100644 --- a/bbot/modules/ffuf_shortnames.py +++ b/bbot/modules/ffuf_shortnames.py @@ -144,7 +144,7 @@ def find_class(self, module, name): if self.find_subwords: self.debug("find_subwords is enabled, downloading nltk data") self.nltk_dir = self.helpers.tools_dir / "nltk_data" - + self.debug(f"Attempting to download nltk data from {self.nltk_dir}") nltk.download("words", download_dir=self.nltk_dir, quiet=True) from nltk.corpus import words From ea9e73646f08c2e9ce6d775a846dc7e90ee0b2d0 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Fri, 31 Jan 2025 19:38:07 -0500 Subject: [PATCH 06/79] better ntlk data handling --- bbot/modules/ffuf_shortnames.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/bbot/modules/ffuf_shortnames.py b/bbot/modules/ffuf_shortnames.py index 8f2995df77..7f3e608b0f 100644 --- a/bbot/modules/ffuf_shortnames.py +++ b/bbot/modules/ffuf_shortnames.py @@ -4,6 +4,7 @@ import random import string import logging +import os from bbot.modules.deadly.ffuf import ffuf @@ -142,10 +143,21 @@ def find_class(self, module, name): self.subword_list = [] if self.find_subwords: - self.debug("find_subwords is enabled, downloading nltk data") + self.debug("find_subwords is enabled, checking for nltk data") self.nltk_dir = self.helpers.tools_dir / "nltk_data" - self.debug(f"Attempting to download nltk data from {self.nltk_dir}") - nltk.download("words", download_dir=self.nltk_dir, quiet=True) + + # Ensure the directory exists + os.makedirs(self.nltk_dir, exist_ok=True) + + # Set the NLTK data path to include self.nltk_dir + nltk.data.path.append(str(self.nltk_dir)) + + try: + nltk.data.find('corpora/words.zip') + self.debug("NLTK words data already present") + except LookupError: + self.debug("NLTK words data not found, downloading") + nltk.download("words", download_dir=self.nltk_dir, quiet=True) from nltk.corpus import words From bf4c0d73e080c7c0c199273e990a5e05b2e28fb0 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Fri, 31 Jan 2025 19:46:36 -0500 Subject: [PATCH 07/79] ruff format --- bbot/modules/ffuf_shortnames.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/modules/ffuf_shortnames.py b/bbot/modules/ffuf_shortnames.py index 7f3e608b0f..d8f865ac5e 100644 --- a/bbot/modules/ffuf_shortnames.py +++ b/bbot/modules/ffuf_shortnames.py @@ -153,7 +153,7 @@ def find_class(self, module, name): nltk.data.path.append(str(self.nltk_dir)) try: - nltk.data.find('corpora/words.zip') + nltk.data.find("corpora/words.zip") self.debug("NLTK words data already present") except LookupError: self.debug("NLTK words data not found, downloading") From 958dad9cc6267c0ac2db649f7377d07559147378 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Fri, 31 Jan 2025 19:52:27 -0500 Subject: [PATCH 08/79] undoing error --- bbot/modules/deadly/ffuf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bbot/modules/deadly/ffuf.py b/bbot/modules/deadly/ffuf.py index dae31b5ca1..b01c1a0534 100644 --- a/bbot/modules/deadly/ffuf.py +++ b/bbot/modules/deadly/ffuf.py @@ -303,7 +303,6 @@ async def execute_ffuf( ] if len(pre_emit_temp_canary) == 0: yield found_json - break else: self.verbose( From eee42f9ae027d838029c7ba557bbc3927d4203c7 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Fri, 31 Jan 2025 20:15:15 -0500 Subject: [PATCH 09/79] better status message --- bbot/modules/deadly/ffuf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/modules/deadly/ffuf.py b/bbot/modules/deadly/ffuf.py index b01c1a0534..e21aebe0cf 100644 --- a/bbot/modules/deadly/ffuf.py +++ b/bbot/modules/deadly/ffuf.py @@ -306,7 +306,7 @@ async def execute_ffuf( else: self.verbose( - "Baseline changed mid-scan, or all strings send to this directory are coming back as a match. This may be due to a WAF turning on a block against you, or an unusual configuration on the web server." + f"Would have reported URL [{found_json['url']}], but baseline check failed. This could be due to a WAF turning on mid-scan, or an unusual web server configuration." ) self.verbose(f"Aborting the current run against [{url}]") return From 2d9f75bfd1cc07856864d745eb9c519c4bef1c32 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Fri, 31 Jan 2025 20:22:02 -0500 Subject: [PATCH 10/79] bug fix --- bbot/modules/ffuf_shortnames.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/bbot/modules/ffuf_shortnames.py b/bbot/modules/ffuf_shortnames.py index d8f865ac5e..68fe97e282 100644 --- a/bbot/modules/ffuf_shortnames.py +++ b/bbot/modules/ffuf_shortnames.py @@ -300,27 +300,28 @@ async def handle_event(self, event): if self.config.get("find_subwords"): subword, suffix = self.find_subword(filename_hint) - if "shortname-directory" in event.tags: - tempfile, tempfile_len = self.generate_templist(suffix, "directory") - async for r in self.execute_ffuf(tempfile, root_url, prefix=subword, exts=["/"]): - await self.emit_event( - r["url"], - "URL_UNVERIFIED", - parent=event, - tags=[f"status-{r['status']}"], - context=f'{{module}} brute-forced directories with detected subword "{subword}" and found {{event.type}}: {{event.data}}', - ) - elif "shortname-endpoint" in event.tags: - for ext in used_extensions: - tempfile, tempfile_len = self.generate_templist(suffix, "endpoint") - async for r in self.execute_ffuf(tempfile, root_url, prefix=subword, suffix=f".{ext}"): + if subword: + if "shortname-directory" in event.tags: + tempfile, tempfile_len = self.generate_templist(suffix, "directory") + async for r in self.execute_ffuf(tempfile, root_url, prefix=subword, exts=["/"]): await self.emit_event( r["url"], "URL_UNVERIFIED", parent=event, tags=[f"status-{r['status']}"], - context=f'{{module}} brute-forced {ext.upper()} files with detected subword "{subword}" and found {{event.type}}: {{event.data}}', + context=f'{{module}} brute-forced directories with detected subword "{subword}" and found {{event.type}}: {{event.data}}', ) + elif "shortname-endpoint" in event.tags: + for ext in used_extensions: + tempfile, tempfile_len = self.generate_templist(suffix, "endpoint") + async for r in self.execute_ffuf(tempfile, root_url, prefix=subword, suffix=f".{ext}"): + await self.emit_event( + r["url"], + "URL_UNVERIFIED", + parent=event, + tags=[f"status-{r['status']}"], + context=f'{{module}} brute-forced {ext.upper()} files with detected subword "{subword}" and found {{event.type}}: {{event.data}}', + ) async def finish(self): if self.config.get("find_common_prefixes"): From e4c07119689119d8b36a614bd91b20a9598595dc Mon Sep 17 00:00:00 2001 From: liquidsec Date: Fri, 31 Jan 2025 21:13:42 -0500 Subject: [PATCH 11/79] prevent ffuf_shortnames from trying to solve impossible URL_HINTs --- bbot/modules/ffuf_shortnames.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bbot/modules/ffuf_shortnames.py b/bbot/modules/ffuf_shortnames.py index 68fe97e282..f2fcb69055 100644 --- a/bbot/modules/ffuf_shortnames.py +++ b/bbot/modules/ffuf_shortnames.py @@ -193,6 +193,8 @@ def find_delimiter(self, hint): return None async def filter_event(self, event): + if "iis-magic-url" in event.tags: + return False, "iis-magic-url URL_HINTs are not solvable by ffuf_shortnames" if event.parent.type != "URL": return False, "its parent event is not of type URL" return True From 4714ac631cf4dbdcc78dca90f4ac1eb70b5dcb06 Mon Sep 17 00:00:00 2001 From: Dom Whewell Date: Sun, 2 Feb 2025 19:24:48 +0000 Subject: [PATCH 12/79] Added a gitdumper module --- bbot/modules/git.py | 9 +- bbot/modules/gitdumper.py | 72 ++++ .../module_tests/test_module_gitdumper.py | 385 ++++++++++++++++++ 3 files changed, 465 insertions(+), 1 deletion(-) create mode 100644 bbot/modules/gitdumper.py create mode 100644 bbot/test/test_step_2/module_tests/test_module_gitdumper.py diff --git a/bbot/modules/git.py b/bbot/modules/git.py index 9a180bc11e..569aa0e489 100644 --- a/bbot/modules/git.py +++ b/bbot/modules/git.py @@ -5,7 +5,7 @@ class git(BaseModule): watched_events = ["URL"] - produced_events = ["FINDING"] + produced_events = ["FINDING", "CODE_REPOSITORY"] flags = ["active", "safe", "web-basic", "code-enum"] meta = { "description": "Check for exposed .git repositories", @@ -37,3 +37,10 @@ async def handle_event(self, event): event, context="{module} detected {event.type}: {description}", ) + await self.emit_event( + {"url": url.rstrip("config")}, + "CODE_REPOSITORY", + event, + tags="git_directory", + context="{module} detected {event.type}: {description}", + ) diff --git a/bbot/modules/gitdumper.py b/bbot/modules/gitdumper.py new file mode 100644 index 0000000000..1a4bb8c07b --- /dev/null +++ b/bbot/modules/gitdumper.py @@ -0,0 +1,72 @@ +import sys +import git_dumper +from pathlib import Path +from bbot.modules.base import BaseModule + + +class gitdumper(BaseModule): + watched_events = ["CODE_REPOSITORY"] + produced_events = ["FILESYSTEM"] + flags = ["passive", "safe", "slow", "code-enum"] + meta = { + "description": "Download a leaked .git folder recursively or by fuzzing common names", + "created_date": "", + "author": "@domwhewell-sage", + } + options = { + "output_folder": "", + "jobs": 10, + "retry": 3, + "timeout": 3, + } + options_desc = { + "output_folder": "Folder to download repositories to", + "jobs": "Number of concurrent jobs to run", + "retry": "Number of retries for each request", + "timeout": "Request timeout", + } + + deps_pip = ["git-dumper~=1.0.8"] + deps_apt = ["git"] + + scope_distance_modifier = 2 + + async def setup(self): + output_folder = self.config.get("output_folder") + if output_folder: + self.output_dir = Path(output_folder) / "git_repos" + else: + self.output_dir = self.scan.home / "git_repos" + self.helpers.mkdir(self.output_dir) + self.jobs = self.config.get("jobs", 10) + self.retry = self.config.get("retry", 3) + self.timeout = self.config.get("timeout", 3) + return await super().setup() + + async def filter_event(self, event): + if event.type == "CODE_REPOSITORY": + if "git-directory" not in event.tags: + return False, "event is not a leaked .git directory" + return True + + async def handle_event(self, event): + repo_url = event.data.get("url") + self.verbose(f"Processing leaked .git directory at {repo_url}") + repo_folder = self.output_dir / self.helpers.tagify(repo_url) + self.helpers.mkdir(repo_folder) + error = await self.helpers.run_in_executor(self.process_repo, repo_url, repo_folder) + if not error: + codebase_event = self.make_event({"path": str(repo_folder)}, "FILESYSTEM", tags=["git"], parent=event) + await self.emit_event( + codebase_event, + context=f"{{module}} cloned git repo at {repo_url} to {{event.type}}: {str(repo_folder)}", + ) + else: + self.helpers.rm_rf(repo_folder) + + def process_repo(self, repo_url, repo_folder): + http_headers = {"User-Agent": self.scan.useragent} + for hk, hv in self.scan.custom_http_headers.items(): + http_headers[hk] = hv + result = git_dumper.fetch_git(repo_url, repo_folder, self.jobs, self.retry, self.timeout, http_headers) + return result diff --git a/bbot/test/test_step_2/module_tests/test_module_gitdumper.py b/bbot/test/test_step_2/module_tests/test_module_gitdumper.py new file mode 100644 index 0000000000..4edadc2a90 --- /dev/null +++ b/bbot/test/test_step_2/module_tests/test_module_gitdumper.py @@ -0,0 +1,385 @@ +from pathlib import Path +from .base import ModuleTestBase + + +class TestGitDumper_Dirlisting(ModuleTestBase): + targets = [ + "http://127.0.0.1:8888/test", + ] + + modules_overrides = ["git", "gitdumper", "httpx"] + + index_html = """ + + Index of /.git + + +

Index of /.git

+ + + + + + + + + + + + +
NameSize
<branches>
COMMIT_EDITMSG157B
config157B
description73B
HEAD23B
<hooks>
<info>
<objects>
<refs>
<logs>
+ + """ + + info_index = """ + + Index of /.git/info + + +

Index of /.git/info

+ + + + + +
NameSize
[..]
exclude240B
excludeme0B
+ + """ + + objects_index = """ + + Index of /.git/objects + + +

Index of /.git/objects

+ + + + + + + + +
NameSize
[..]
<05>
<34>
<c2>
<pack>
<info>
+ + """ + + objects_o5_index = """ + + Index of /.git/objects/05 + + +

Index of /.git/objects/05

+ + + + +
NameSize
[..]
27e6bd2d76b45e2933183f1b506c7ac49f5872
+ + """ + + objects_34_index = """ + + Index of /.git/objects/34 + + +

Index of /.git/objects/34

+ + + + +
NameSize
[..]
dc86f0247798892a89553e7c5c2d5aa06c2c5b
+ + """ + + objects_c2_index = """ + + Index of /.git/objects/c2 + + +

Index of /.git/objects/c2

+ + + + +
NameSize
[..]
69d751b8e2fd0be0d0dc7a6437a4dce4ec0200
+ + """ + + refs_index = """ + + Index of /.git/refs + + +

Index of /.git/refs

+ + + + + +
NameSize
[..]
<heads>
<tags>
+ + + """ + + refs_heads_index = """ + + Index of /.git/refs/heads + + +

Index of /.git/refs/heads

+ + + + +
NameSize
[..]
master
+ + + """ + + logs_index = """ + + Index of /.git/logs + + +

Index of /.git/logs

+ + + + + +
NameSize
[..]
HEAD
<tags>
+ + + """ + + logs_refs_index = """ + + Index of /.git/logs/refs + + +

Index of /.git/logs/refs

+ + + + +
NameSize
[..]
<heads>
+ + + """ + + logs_refs_heads_index = """ + + Index of /.git/logs/refs/heads + + +

Index of /.git/logs/refs/heads

+ + + + +
NameSize
[..]
master
+ + + """ + + empty_index = """ + + Index of /.git/... + + +

Index of /.git/...

+ + + +
NameSize
[..]
+ + """ + + git_head = "ref: refs/heads/master" + + refs_head = "34dc86f0247798892a89553e7c5c2d5aa06c2c5b" + + logs_head = "0000000000000000000000000000000000000000 34dc86f0247798892a89553e7c5c2d5aa06c2c5b Test 1738516534 +0000 commit (initial): Initial commit" + + logs_master_head = "0000000000000000000000000000000000000000 34dc86f0247798892a89553e7c5c2d5aa06c2c5b Test 1738516534 +0000 commit (initial): Initial commit" + + git_description = "Unnamed repository; edit this file 'description' to name the repository." + + git_commit_editmsg = "Initial commit" + + git_config = """[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true""" + + git_exclude = """# git ls-files --others --exclude-from=.git/info/exclude + # Lines that start with '#' are comments. + # For a project mostly in C, the following would be a good set of + # exclude patterns (uncomment them if you want to use them): + # *.[oa] + # *~""" + + filebytes_gitindex = b"DIRC\x00\x00\x00\x02\x00\x00\x00\x01g\x9f\xbe\x04\x14\xfcb\xd1g\x9f\xbe\x04\x14\xfcb\xd1\x00\x00\x08 \x00\x04aD\x00\x00\x81\xa4\x00\x00\x03\xe8\x00\x00\x03\xe8\x00\x00\x00\x0f\x05'\xe6\xbd-v\xb4^)3\x18?\x1bPlz\xc4\x9fXr\x00\x08test.txt\x00\x00TREE\x00\x00\x00\x19\x001 0\n\xc2i\xd7Q\xb8\xe2\xfd\x0b\xe0\xd0\xdczd7\xa4\xdc\xe4\xec\x02\x00\xe8m|iw\xbb\xd6\x88;f\xdbW\x10yY\xd2\xb0G\xcfJ" + filebytes_27e6bd2d76b45e2933183f1b506c7ac49f5872 = ( + b"x\x01K\xca\xc9OR04e\x08\xc9\xc8,V\x00\xa2D\x85\x92\xd4\xe2\x12.\x00U\xab\x07%" + ) + filebytes_dc86f0247798892a89553e7c5c2d5aa06c2c5b = b"x\x01\x9d\x8dK\n\x021\x10D]\xe7\x14\xbd\x17\x86\xce?\x82\x88\x0b7\x9e\xc0u\xa6\xd3:\x81\xc4\xc0\x18\x99\xeb\x1b\x98\x1bX\xbbzP\xaf\xa8\xd5\x9a;\xc8\xa0\x0f}e\x06R\xee\x94\xbc\x95s`\xf5L83&L\xe4\xa33\xdaG\x93\x88\r\x13*D\x11\xbf}i+\xdcZ\x85\xc7\xc2\x1b\x97\x02\xe7\xd4\xea\xb4\xed\xe5\xfa\x89/\x9e\xa8\xd5\x0bH\xaf\x83\x95\xcej\x03G\x1c\x11\x83\x8e\xcf\xce\xff\xad\xc5\xfd\x9d{\x8e\x05v\x8d\xf8\x01\xfaF<\x05" + filebytes_69d751b8e2fd0be0d0dc7a6437a4dce4ec0200 = b"x\x01+)JMU06c040031Q(I-.\xd1+\xa9(a`U\x7f\xb6W\xb7lK\x9c\xa6\xb1\x84\xbdt@N\xd5\x91\xf9\x11E\x00*\x05\x0e\x8c" + + async def setup_after_prep(self, module_test): + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/"}, respond_args={"response_data": self.index_html} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/index"}, respond_args={"response_data": self.filebytes_gitindex} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/COMMIT_EDITMSG"}, respond_args={"response_data": self.git_commit_editmsg} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/config"}, respond_args={"response_data": self.git_config} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/branches/"}, respond_args={"response_data": self.empty_index} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/description"}, respond_args={"response_data": self.git_description} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/HEAD"}, respond_args={"response_data": self.git_head} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/hooks/"}, respond_args={"response_data": self.empty_index} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/info/"}, respond_args={"response_data": self.info_index} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/info/exclude"}, respond_args={"response_data": self.git_exclude} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/objects/"}, respond_args={"response_data": self.objects_index} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/objects/05/"}, respond_args={"response_data": self.objects_o5_index} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/objects/05/27e6bd2d76b45e2933183f1b506c7ac49f5872"}, + respond_args={"response_data": self.filebytes_27e6bd2d76b45e2933183f1b506c7ac49f5872}, + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/objects/34/"}, respond_args={"response_data": self.objects_34_index} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/objects/34/dc86f0247798892a89553e7c5c2d5aa06c2c5b"}, + respond_args={"response_data": self.filebytes_dc86f0247798892a89553e7c5c2d5aa06c2c5b}, + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/objects/c2/"}, respond_args={"response_data": self.objects_c2_index} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/objects/c2/69d751b8e2fd0be0d0dc7a6437a4dce4ec0200"}, + respond_args={"response_data": self.filebytes_69d751b8e2fd0be0d0dc7a6437a4dce4ec0200}, + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/objects/info/"}, respond_args={"response_data": self.empty_index} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/objects/pack/"}, respond_args={"response_data": self.empty_index} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/refs/"}, respond_args={"response_data": self.refs_index} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/refs/heads/"}, respond_args={"response_data": self.refs_heads_index} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/refs/heads/master"}, respond_args={"response_data": self.refs_head} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/refs/tags/"}, respond_args={"response_data": self.empty_index} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/logs/"}, respond_args={"response_data": self.logs_index} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/logs/refs/"}, respond_args={"response_data": self.logs_refs_index} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/logs/refs/heads/"}, + respond_args={"response_data": self.logs_refs_heads_index}, + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/logs/refs/heads/master"}, + respond_args={"response_data": self.logs_master_head}, + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/logs/HEAD"}, respond_args={"response_data": self.logs_head} + ) + + def check(self, module_test, events): + assert any( + e.type == "CODE_REPOSITORY" + and "git-directory" in e.tags + and e.data["url"] == "http://127.0.0.1:8888/test/.git/" + for e in events + ) + filesystem_events = [ + e + for e in events + if e.type == "FILESYSTEM" and "http-127-0-0-1-8888-test-git" in e.data["path"] and "git" in e.tags + ] + assert 1 == len(filesystem_events), "Failed to git clone CODE_REPOSITORY" + filesystem_event = filesystem_events[0] + folder = Path(filesystem_event.data["path"]) + assert folder.is_dir(), "Destination folder doesn't exist" + with open(folder / "test.txt") as f: + content = f.read() + assert content == "This is a test\n", "File content doesn't match" + + +class TestGitDumper_NoDirlisting(TestGitDumper_Dirlisting): + async def setup_after_prep(self, module_test): + module_test.set_expect_requests(expect_args={"uri": "/test/.git/"}, respond_args={"response_data": ""}) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/index"}, respond_args={"response_data": self.filebytes_gitindex} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/COMMIT_EDITMSG"}, respond_args={"response_data": self.git_commit_editmsg} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/config"}, respond_args={"response_data": self.git_config} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/description"}, respond_args={"response_data": self.git_description} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/HEAD"}, respond_args={"response_data": self.git_head} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/info/exclude"}, respond_args={"response_data": self.git_exclude} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/objects/05/27e6bd2d76b45e2933183f1b506c7ac49f5872"}, + respond_args={"response_data": self.filebytes_27e6bd2d76b45e2933183f1b506c7ac49f5872}, + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/objects/34/dc86f0247798892a89553e7c5c2d5aa06c2c5b"}, + respond_args={"response_data": self.filebytes_dc86f0247798892a89553e7c5c2d5aa06c2c5b}, + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/objects/c2/69d751b8e2fd0be0d0dc7a6437a4dce4ec0200"}, + respond_args={"response_data": self.filebytes_69d751b8e2fd0be0d0dc7a6437a4dce4ec0200}, + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/refs/heads/master"}, respond_args={"response_data": self.refs_head} + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/logs/refs/heads/master"}, + respond_args={"response_data": self.logs_master_head}, + ) + module_test.set_expect_requests( + expect_args={"uri": "/test/.git/logs/HEAD"}, respond_args={"response_data": self.logs_head} + ) From b65df1d0831d439645150ec924ebc1a357ddab28 Mon Sep 17 00:00:00 2001 From: Dom Whewell Date: Sun, 2 Feb 2025 21:09:36 +0000 Subject: [PATCH 13/79] Remove unused module --- bbot/modules/gitdumper.py | 1 - bbot/test/test_step_2/module_tests/test_module_gitdumper.py | 1 - 2 files changed, 2 deletions(-) diff --git a/bbot/modules/gitdumper.py b/bbot/modules/gitdumper.py index 1a4bb8c07b..6cf3f7c1ac 100644 --- a/bbot/modules/gitdumper.py +++ b/bbot/modules/gitdumper.py @@ -1,4 +1,3 @@ -import sys import git_dumper from pathlib import Path from bbot.modules.base import BaseModule diff --git a/bbot/test/test_step_2/module_tests/test_module_gitdumper.py b/bbot/test/test_step_2/module_tests/test_module_gitdumper.py index 4edadc2a90..66995793c0 100644 --- a/bbot/test/test_step_2/module_tests/test_module_gitdumper.py +++ b/bbot/test/test_step_2/module_tests/test_module_gitdumper.py @@ -342,7 +342,6 @@ def check(self, module_test, events): class TestGitDumper_NoDirlisting(TestGitDumper_Dirlisting): async def setup_after_prep(self, module_test): - module_test.set_expect_requests(expect_args={"uri": "/test/.git/"}, respond_args={"response_data": ""}) module_test.set_expect_requests( expect_args={"uri": "/test/.git/index"}, respond_args={"response_data": self.filebytes_gitindex} ) From 227c56578d861f1dd90d303bc349930ed2a936df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 04:21:56 +0000 Subject: [PATCH 14/79] Bump fastapi from 0.115.7 to 0.115.8 Bumps [fastapi](https://github.com/fastapi/fastapi) from 0.115.7 to 0.115.8. - [Release notes](https://github.com/fastapi/fastapi/releases) - [Commits](https://github.com/fastapi/fastapi/compare/0.115.7...0.115.8) --- updated-dependencies: - dependency-name: fastapi dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 0bf09a941c..56a3e289fb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -589,13 +589,13 @@ test = ["pytest (>=6)"] [[package]] name = "fastapi" -version = "0.115.7" +version = "0.115.8" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.115.7-py3-none-any.whl", hash = "sha256:eb6a8c8bf7f26009e8147111ff15b5177a0e19bb4a45bc3486ab14804539d21e"}, - {file = "fastapi-0.115.7.tar.gz", hash = "sha256:0f106da6c01d88a6786b3248fb4d7a940d071f6f488488898ad5d354b25ed015"}, + {file = "fastapi-0.115.8-py3-none-any.whl", hash = "sha256:753a96dd7e036b34eeef8babdfcfe3f28ff79648f86551eb36bfc1b0bf4a8cbf"}, + {file = "fastapi-0.115.8.tar.gz", hash = "sha256:0ce9111231720190473e222cdf0f07f7206ad7e53ea02beb1d2dc36e2f0741e9"}, ] [package.dependencies] From 64b201b7c627e36de82f5c9fc65607d8981bb82e Mon Sep 17 00:00:00 2001 From: liquidsec Date: Mon, 3 Feb 2025 14:26:54 -0500 Subject: [PATCH 15/79] adding optin description --- bbot/modules/ffuf_shortnames.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/modules/ffuf_shortnames.py b/bbot/modules/ffuf_shortnames.py index f2fcb69055..c3f4e46fb0 100644 --- a/bbot/modules/ffuf_shortnames.py +++ b/bbot/modules/ffuf_shortnames.py @@ -42,7 +42,7 @@ class ffuf_shortnames(ffuf): "ignore_redirects": "Explicitly ignore redirects (301,302)", "find_common_prefixes": "Attempt to automatically detect common prefixes and make additional ffuf runs against them", "find_delimiters": "Attempt to detect common delimiters and make additional ffuf runs against them", - "find_subwords": "", + "find_subwords": "Attempt to detect subwords and make additional ffuf runs against them", "max_predictions": "The maximum number of predictions to generate per shortname prefix", } From a73ef4376f147e7b53cb7183c40e5fce0c46d706 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Thu, 6 Feb 2025 10:21:46 -0500 Subject: [PATCH 16/79] turning off cloud warning when strict-scope is on --- bbot/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/cli.py b/bbot/cli.py index 943f7727ef..d2bace1d0c 100755 --- a/bbot/cli.py +++ b/bbot/cli.py @@ -183,7 +183,7 @@ async def _main(): for event in scan.target.seeds.events: if event.type == "DNS_NAME": cloudcheck_result = scan.helpers.cloudcheck(event.host) - if cloudcheck_result: + if cloudcheck_result and not scan.preset.strict_scope: scan.hugewarning( f'YOUR TARGET CONTAINS A CLOUD DOMAIN: "{event.host}". You\'re in for a wild ride!' ) From d83d7f13a2d7df501dc4029909e3f2bb9988186a Mon Sep 17 00:00:00 2001 From: liquidsec Date: Thu, 6 Feb 2025 10:25:46 -0500 Subject: [PATCH 17/79] more efficient --- bbot/cli.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/bbot/cli.py b/bbot/cli.py index d2bace1d0c..761fcbd5ff 100755 --- a/bbot/cli.py +++ b/bbot/cli.py @@ -180,13 +180,14 @@ async def _main(): if sys.stdin.isatty(): # warn if any targets belong directly to a cloud provider - for event in scan.target.seeds.events: - if event.type == "DNS_NAME": - cloudcheck_result = scan.helpers.cloudcheck(event.host) - if cloudcheck_result and not scan.preset.strict_scope: - scan.hugewarning( - f'YOUR TARGET CONTAINS A CLOUD DOMAIN: "{event.host}". You\'re in for a wild ride!' - ) + if not scan.preset.strict_scope: + for event in scan.target.seeds.events: + if event.type == "DNS_NAME": + cloudcheck_result = scan.helpers.cloudcheck(event.host) + if cloudcheck_result: + scan.hugewarning( + f'YOUR TARGET CONTAINS A CLOUD DOMAIN: "{event.host}". You\'re in for a wild ride!' + ) if not options.yes: log.hugesuccess(f"Scan ready. Press enter to execute {scan.name}") From 61475a6ef109604b47b528e8cd2b224361a626ce Mon Sep 17 00:00:00 2001 From: liquidsec Date: Thu, 6 Feb 2025 11:12:47 -0500 Subject: [PATCH 18/79] temporary change to islate generic ssrf test --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c0443b350f..b7a9d2be91 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -34,7 +34,7 @@ jobs: poetry run ruff format --check - name: Run tests run: | - poetry run pytest -vv --exitfirst --reruns 2 -o timeout_func_only=true --timeout 1200 --disable-warnings --log-cli-level=INFO --cov-config=bbot/test/coverage.cfg --cov-report xml:cov.xml --cov=bbot . + poetry run pytest -vv --exitfirst --reruns 2 -o timeout_func_only=true --timeout 1200 --disable-warnings --log-cli-level=INFO -k TestGeneric_SSRF --cov-config=bbot/test/coverage.cfg --cov-report xml:cov.xml --cov=bbot . - name: Upload Debug Logs if: always() uses: actions/upload-artifact@v4 From 79ed370461659d0eae674c4a53f3fb3a0b2260fc Mon Sep 17 00:00:00 2001 From: liquidsec Date: Thu, 6 Feb 2025 11:19:18 -0500 Subject: [PATCH 19/79] temporary to troubleshoot generic_ssrf test --- .github/workflows/distro_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/distro_tests.yml b/.github/workflows/distro_tests.yml index 21f40c8133..6040e06e78 100644 --- a/.github/workflows/distro_tests.yml +++ b/.github/workflows/distro_tests.yml @@ -61,7 +61,7 @@ jobs: export BBOT_DISTRO_TESTS=true poetry env use python3.11 poetry install - poetry run pytest --reruns 2 -o timeout_func_only=true --timeout 1200 --disable-warnings --log-cli-level=INFO . + poetry run pytest --reruns 2 -o timeout_func_only=true --timeout 1200 -k TestGeneric_SSRF --disable-warnings --log-cli-level=INFO . - name: Upload Debug Logs if: always() uses: actions/upload-artifact@v4 From 3710e16461f862ada7786dda64a92a29fd1738ff Mon Sep 17 00:00:00 2001 From: liquidsec Date: Thu, 6 Feb 2025 11:39:40 -0500 Subject: [PATCH 20/79] adding locks to mock interactsh to prevent race conditions --- bbot/test/conftest.py | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/bbot/test/conftest.py b/bbot/test/conftest.py index f9807db819..8c7fe280d6 100644 --- a/bbot/test/conftest.py +++ b/bbot/test/conftest.py @@ -8,6 +8,7 @@ from contextlib import suppress from omegaconf import OmegaConf from pytest_httpserver import HTTPServer +import time from bbot.core import CORE from bbot.core.helpers.misc import execute_sync_or_async @@ -53,6 +54,12 @@ def silence_live_logging(): handler.setLevel(logging.CRITICAL) +def stop_server(server): + server.stop() + while server.is_running(): + time.sleep(0.1) # Wait a bit before checking again + + @pytest.fixture def bbot_httpserver(): server = HTTPServer(host="127.0.0.1", port=8888, threaded=True) @@ -61,11 +68,7 @@ def bbot_httpserver(): yield server server.clear() - if server.is_running(): - server.stop() - - # this is to check if the client has made any request where no - # `assert_request` was called on it from the test + stop_server(server) # Ensure the server is fully stopped server.check_assertions() server.clear() @@ -84,11 +87,7 @@ def bbot_httpserver_ssl(): yield server server.clear() - if server.is_running(): - server.stop() - - # this is to check if the client has made any request where no - # `assert_request` was called on it from the test + stop_server(server) # Ensure the server is fully stopped server.check_assertions() server.clear() @@ -133,6 +132,7 @@ def __init__(self, name): self.correlation_id = "deadbeef-dead-beef-dead-beefdeadbeef" self.stop = False self.poll_task = None + self.lock = asyncio.Lock() def mock_interaction(self, subdomain_tag, msg=None): self.log.info(f"Mocking interaction to subdomain tag: {subdomain_tag}") @@ -149,7 +149,7 @@ async def deregister(self, callback=None): self.stop = True if self.poll_task is not None: self.poll_task.cancel() - with suppress(BaseException): + with suppress(asyncio.CancelledError): await self.poll_task async def poll_loop(self, callback=None): @@ -161,12 +161,16 @@ async def poll_loop(self, callback=None): async def poll(self, callback=None): poll_results = [] - for subdomain_tag in self.interactions: - result = {"full-id": f"{subdomain_tag}.fakedomain.fakeinteractsh.com", "protocol": "HTTP"} - poll_results.append(result) - if callback is not None: - await execute_sync_or_async(callback, result) - self.interactions = [] + async with self.lock: + try: + for subdomain_tag in self.interactions: + result = {"full-id": f"{subdomain_tag}.fakedomain.fakeinteractsh.com", "protocol": "HTTP"} + poll_results.append(result) + if callback is not None: + await execute_sync_or_async(callback, result) + self.interactions = [] + except Exception as e: + self.log.error(f"Error during poll: {e}") return poll_results From fd3709ada87e2cee50c1ccbc873d80efedd91026 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Thu, 6 Feb 2025 12:13:41 -0500 Subject: [PATCH 21/79] reverting temp test changes --- .github/workflows/distro_tests.yml | 2 +- .github/workflows/tests.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/distro_tests.yml b/.github/workflows/distro_tests.yml index 6040e06e78..21f40c8133 100644 --- a/.github/workflows/distro_tests.yml +++ b/.github/workflows/distro_tests.yml @@ -61,7 +61,7 @@ jobs: export BBOT_DISTRO_TESTS=true poetry env use python3.11 poetry install - poetry run pytest --reruns 2 -o timeout_func_only=true --timeout 1200 -k TestGeneric_SSRF --disable-warnings --log-cli-level=INFO . + poetry run pytest --reruns 2 -o timeout_func_only=true --timeout 1200 --disable-warnings --log-cli-level=INFO . - name: Upload Debug Logs if: always() uses: actions/upload-artifact@v4 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b7a9d2be91..c0443b350f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -34,7 +34,7 @@ jobs: poetry run ruff format --check - name: Run tests run: | - poetry run pytest -vv --exitfirst --reruns 2 -o timeout_func_only=true --timeout 1200 --disable-warnings --log-cli-level=INFO -k TestGeneric_SSRF --cov-config=bbot/test/coverage.cfg --cov-report xml:cov.xml --cov=bbot . + poetry run pytest -vv --exitfirst --reruns 2 -o timeout_func_only=true --timeout 1200 --disable-warnings --log-cli-level=INFO --cov-config=bbot/test/coverage.cfg --cov-report xml:cov.xml --cov=bbot . - name: Upload Debug Logs if: always() uses: actions/upload-artifact@v4 From 62e7d5f53de599e7565a1c37868ae1f7c3d03603 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Thu, 6 Feb 2025 14:29:06 -0500 Subject: [PATCH 22/79] changing DNS events to FINDING, adding omit option --- bbot/modules/generic_ssrf.py | 33 +++++++++++++------ bbot/test/conftest.py | 14 ++++---- .../module_tests/test_module_generic_ssrf.py | 29 ++++++++++++++++ 3 files changed, 58 insertions(+), 18 deletions(-) diff --git a/bbot/modules/generic_ssrf.py b/bbot/modules/generic_ssrf.py index fb530a697d..6c7c7ed49d 100644 --- a/bbot/modules/generic_ssrf.py +++ b/bbot/modules/generic_ssrf.py @@ -154,6 +154,12 @@ class generic_ssrf(BaseModule): produced_events = ["VULNERABILITY"] flags = ["active", "aggressive", "web-thorough"] meta = {"description": "Check for generic SSRFs", "created_date": "2022-07-30", "author": "@liquidsec"} + options = { + "skip_dns_interaction": False, + } + options_desc = { + "skip_dns_interaction": "Do not report DNS interactions (only HTTP interaction)", + } in_scope_only = True deps_apt = ["curl"] @@ -163,7 +169,7 @@ async def setup(self): self.interactsh_subdomain_tags = {} self.parameter_subdomain_tags_map = {} self.severity = None - self.generic_only = self.config.get("generic_only", False) + self.skip_dns_interaction = self.config.get("skip_dns_interaction", False) if self.scan.config.get("interactsh_disable", False) is False: try: @@ -191,6 +197,10 @@ async def handle_event(self, event): await s.test(event) async def interactsh_callback(self, r): + protocol = r.get("protocol").upper() + if protocol == "DNS" and self.skip_dns_interaction: + return + full_id = r.get("full-id", None) subdomain_tag = full_id.split(".")[0] @@ -204,24 +214,27 @@ async def interactsh_callback(self, r): matched_severity = match[2] matched_echoed_response = str(match[3]) - # Check if any SSRF parameter is in the DNS request triggering_param = self.parameter_subdomain_tags_map.get(subdomain_tag, None) description = f"Out-of-band interaction: [{matched_technique}]" if triggering_param: self.debug(f"Found triggering parameter: {triggering_param}") description += f" [Triggering Parameter: {triggering_param}]" - description += f" [{r.get('protocol').upper()}] Echoed Response: {matched_echoed_response}" + description += f" [{protocol}] Echoed Response: {matched_echoed_response}" self.debug(f"Emitting event with description: {description}") # Debug the final description + event_type = "VULNERABILITY" if protocol == "HTTP" else "FINDING" + event_data = { + "host": str(matched_event.host), + "url": matched_event.data, + "description": description, + } + if protocol == "HTTP": + event_data["severity"] = matched_severity + await self.emit_event( - { - "severity": matched_severity, - "host": str(matched_event.host), - "url": matched_event.data, - "description": description, - }, - "VULNERABILITY", + event_data, + event_type, matched_event, context=f"{{module}} scanned {matched_event.data} and detected {{event.type}}: {matched_technique}", ) diff --git a/bbot/test/conftest.py b/bbot/test/conftest.py index 8c7fe280d6..f6c2ebff98 100644 --- a/bbot/test/conftest.py +++ b/bbot/test/conftest.py @@ -160,18 +160,16 @@ async def poll_loop(self, callback=None): continue async def poll(self, callback=None): - poll_results = [] async with self.lock: - try: - for subdomain_tag in self.interactions: - result = {"full-id": f"{subdomain_tag}.fakedomain.fakeinteractsh.com", "protocol": "HTTP"} + poll_results = [] + for subdomain_tag in self.interactions: + for protocol in ["HTTP", "DNS"]: + result = {"full-id": f"{subdomain_tag}.fakedomain.fakeinteractsh.com", "protocol": protocol} poll_results.append(result) if callback is not None: await execute_sync_or_async(callback, result) - self.interactions = [] - except Exception as e: - self.log.error(f"Error during poll: {e}") - return poll_results + self.interactions = [] + return poll_results import threading diff --git a/bbot/test/test_step_2/module_tests/test_module_generic_ssrf.py b/bbot/test/test_step_2/module_tests/test_module_generic_ssrf.py index 4502511da4..d0cc5255c3 100644 --- a/bbot/test/test_step_2/module_tests/test_module_generic_ssrf.py +++ b/bbot/test/test_step_2/module_tests/test_module_generic_ssrf.py @@ -41,6 +41,18 @@ async def setup_after_prep(self, module_test): module_test.set_expect_requests_handler(expect_args=expect_args, request_handler=self.request_handler) def check(self, module_test, events): + total_vulnerabilities = 0 + total_findings = 0 + + for e in events: + if e.type == "VULNERABILITY": + total_vulnerabilities += 1 + elif e.type == "FINDING": + total_findings += 1 + + assert total_vulnerabilities == 30, "Incorrect number of vulnerabilities detected" + assert total_findings == 30, "Incorrect number of findings detected" + assert any( e.type == "VULNERABILITY" and "Out-of-band interaction: [Generic SSRF (GET)]" @@ -55,3 +67,20 @@ def check(self, module_test, events): e.type == "VULNERABILITY" and "Out-of-band interaction: [Generic XXE] [HTTP]" in e.data["description"] for e in events ), "Failed to detect Generic SSRF (XXE)" + + +class TestGeneric_SSRF_httponly(TestGeneric_SSRF): + config_overrides = {"modules": {"generic_ssrf": {"skip_dns_interaction": True}}} + + def check(self, module_test, events): + total_vulnerabilities = 0 + total_findings = 0 + + for e in events: + if e.type == "VULNERABILITY": + total_vulnerabilities += 1 + elif e.type == "FINDING": + total_findings += 1 + + assert total_vulnerabilities == 30, "Incorrect number of vulnerabilities detected" + assert total_findings == 0, "Incorrect number of findings detected" From 5bb903030eee72330dd865b340dd36e17344cba7 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Thu, 6 Feb 2025 15:08:49 -0500 Subject: [PATCH 23/79] increasing detection wait time --- bbot/modules/generic_ssrf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/modules/generic_ssrf.py b/bbot/modules/generic_ssrf.py index 6c7c7ed49d..6ccde510b9 100644 --- a/bbot/modules/generic_ssrf.py +++ b/bbot/modules/generic_ssrf.py @@ -254,7 +254,7 @@ async def cleanup(self): async def finish(self): if self.scan.config.get("interactsh_disable", False) is False: - await self.helpers.sleep(2) + await self.helpers.sleep(5) try: for r in await self.interactsh_instance.poll(): await self.interactsh_callback(r) From 40999903b89d281389ed877aa4ebd1ba63a71818 Mon Sep 17 00:00:00 2001 From: Olivier Cervello Date: Fri, 7 Feb 2025 05:43:56 -0500 Subject: [PATCH 24/79] update --- poetry.lock | 148 +++++++++++++++++++++++++++++++++++++++++++------ pyproject.toml | 2 +- 2 files changed, 132 insertions(+), 18 deletions(-) diff --git a/poetry.lock b/poetry.lock index 0bf09a941c..9a94ca1843 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. [[package]] name = "annotated-types" @@ -6,31 +6,19 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, ] -[[package]] -name = "ansible" -version = "8.7.0" -description = "Radically simple IT automation" -optional = false -python-versions = ">=3.9" -files = [ - {file = "ansible-8.7.0-py3-none-any.whl", hash = "sha256:fa7d3bc2dfdb0ab031df645814ff86b15cb5ec041bfbee4041f795abfa5646ca"}, - {file = "ansible-8.7.0.tar.gz", hash = "sha256:3a5ca5152e4547d590e40b542d76b18dbbe2b36da4edd00a13a7c51a374ff737"}, -] - -[package.dependencies] -ansible-core = ">=2.15.7,<2.16.0" - [[package]] name = "ansible-core" version = "2.15.13" description = "Radically simple IT automation" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "ansible_core-2.15.13-py3-none-any.whl", hash = "sha256:e7f50bbb61beae792f5ecb86eff82149d3948d078361d70aedb01d76bc483c30"}, {file = "ansible_core-2.15.13.tar.gz", hash = "sha256:f542e702ee31fb049732143aeee6b36311ca48b7d13960a0685afffa0d742d7f"}, @@ -50,6 +38,7 @@ version = "2.4.0" description = "\"Consistent Ansible Python API and CLI with container and process isolation runtime capabilities\"" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "ansible-runner-2.4.0.tar.gz", hash = "sha256:82d02b2548830f37a53517b65c823c4af371069406c7d213b5c9041d45e0c5b6"}, {file = "ansible_runner-2.4.0-py3-none-any.whl", hash = "sha256:a3f592ae4cdfa62a72ad15de60da9c8210f376d67f495c4a78d4cf1dc7ccdf89"}, @@ -68,6 +57,7 @@ version = "4.9.3" description = "ANTLR 4.9.3 runtime for Python 3.7" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "antlr4-python3-runtime-4.9.3.tar.gz", hash = "sha256:f224469b4168294902bb1efa80a8bf7855f24c99aef99cbefc1bcd3cce77881b"}, ] @@ -78,6 +68,7 @@ version = "4.8.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" +groups = ["main", "dev"] files = [ {file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"}, {file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"}, @@ -100,6 +91,7 @@ version = "2.16.0" description = "Internationalization utilities" optional = false python-versions = ">=3.8" +groups = ["docs"] files = [ {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, @@ -114,6 +106,7 @@ version = "4.12.3" description = "Screen-scraping library" optional = false python-versions = ">=3.6.0" +groups = ["main", "docs"] files = [ {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, @@ -135,6 +128,7 @@ version = "5.5.1" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "cachetools-5.5.1-py3-none-any.whl", hash = "sha256:b76651fdc3b24ead3c648bbdeeb940c1b04d365b38b4af66788f9ec4a81d42bb"}, {file = "cachetools-5.5.1.tar.gz", hash = "sha256:70f238fbba50383ef62e55c6aff6d9673175fe59f7c6782c7a0b9e38f4a9df95"}, @@ -146,6 +140,7 @@ version = "2024.12.14" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["main", "dev", "docs"] files = [ {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, @@ -157,6 +152,8 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "implementation_name == \"pypy\" or platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -236,6 +233,7 @@ version = "3.4.0" description = "Validate configuration and produce human readable error messages." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, @@ -247,6 +245,7 @@ version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" +groups = ["main", "docs"] files = [ {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, @@ -348,6 +347,7 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" +groups = ["dev", "docs"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -362,6 +362,7 @@ version = "7.0.47" description = "Check whether an IP address belongs to a cloud provider" optional = false python-versions = "<4.0,>=3.9" +groups = ["main"] files = [ {file = "cloudcheck-7.0.47-py3-none-any.whl", hash = "sha256:71faaf5c090e9ae1501b692b0c7c2ed1f5efb88d02b190187d9d410f7a823d87"}, {file = "cloudcheck-7.0.47.tar.gz", hash = "sha256:61c4a3b70dcd86349c72e3179e427e7db6ee046cc88ba0d76ada1bea84223242"}, @@ -379,10 +380,12 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev", "docs"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +markers = {dev = "sys_platform == \"win32\" or platform_system == \"Windows\""} [[package]] name = "coverage" @@ -390,6 +393,7 @@ version = "7.6.10" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78"}, {file = "coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c"}, @@ -467,6 +471,7 @@ version = "43.0.3" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"}, {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"}, @@ -516,6 +521,7 @@ version = "8.1.1" description = "Deep Difference and Search of any Python object/data. Recreate objects by adding adding deltas to each other." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "deepdiff-8.1.1-py3-none-any.whl", hash = "sha256:b0231fa3afb0f7184e82535f2b4a36636442ed21e94a0cf3aaa7982157e7ebca"}, {file = "deepdiff-8.1.1.tar.gz", hash = "sha256:dd7bc7d5c8b51b5b90f01b0e2fe23c801fd8b4c6a7ee7e31c5a3c3663fcc7ceb"}, @@ -534,6 +540,7 @@ version = "0.3.9" description = "Distribution utilities" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, @@ -545,6 +552,7 @@ version = "2.7.0" description = "DNS toolkit" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"}, {file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"}, @@ -565,6 +573,7 @@ version = "1.23.0" description = "Dynamic version generation" optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "dunamai-1.23.0-py3-none-any.whl", hash = "sha256:a0906d876e92441793c6a423e16a4802752e723e9c9a5aabdc5535df02dbe041"}, {file = "dunamai-1.23.0.tar.gz", hash = "sha256:a163746de7ea5acb6dacdab3a6ad621ebc612ed1e528aaa8beedb8887fccd2c4"}, @@ -579,6 +588,8 @@ version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] +markers = "python_version < \"3.11\"" files = [ {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, @@ -593,6 +604,7 @@ version = "0.115.7" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "fastapi-0.115.7-py3-none-any.whl", hash = "sha256:eb6a8c8bf7f26009e8147111ff15b5177a0e19bb4a45bc3486ab14804539d21e"}, {file = "fastapi-0.115.7.tar.gz", hash = "sha256:0f106da6c01d88a6786b3248fb4d7a940d071f6f488488898ad5d354b25ed015"}, @@ -613,6 +625,7 @@ version = "3.17.0" description = "A platform independent file lock." optional = false python-versions = ">=3.9" +groups = ["main", "dev"] files = [ {file = "filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338"}, {file = "filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e"}, @@ -629,6 +642,7 @@ version = "2.1.0" description = "Copy your docs directly to the gh-pages branch." optional = false python-versions = "*" +groups = ["docs"] files = [ {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, @@ -646,6 +660,7 @@ version = "1.5.5" 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.9" +groups = ["docs"] files = [ {file = "griffe-1.5.5-py3-none-any.whl", hash = "sha256:2761b1e8876c6f1f9ab1af274df93ea6bbadd65090de5f38f4cb5cc84897c7dd"}, {file = "griffe-1.5.5.tar.gz", hash = "sha256:35ee5b38b93d6a839098aad0f92207e6ad6b70c3e8866c08ca669275b8cba585"}, @@ -660,6 +675,7 @@ version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, @@ -671,6 +687,7 @@ version = "1.0.7" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, @@ -692,6 +709,7 @@ version = "0.27.2" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, @@ -717,6 +735,7 @@ version = "2.6.6" description = "File identification library for Python" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "identify-2.6.6-py2.py3-none-any.whl", hash = "sha256:cbd1810bce79f8b671ecb20f53ee0ae8e86ae84b557de31d89709dc2a48ba881"}, {file = "identify-2.6.6.tar.gz", hash = "sha256:7bec12768ed44ea4761efb47806f0a41f86e7c0a5fdf5950d4648c90eca7e251"}, @@ -731,6 +750,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main", "dev", "docs"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -745,10 +765,12 @@ version = "6.2.1" description = "Read metadata from Python packages" optional = false python-versions = ">=3.7" +groups = ["main", "docs"] files = [ {file = "importlib_metadata-6.2.1-py3-none-any.whl", hash = "sha256:f65e478a7c2177bd19517a3a15dac094d253446d8690c5f3e71e735a04312374"}, {file = "importlib_metadata-6.2.1.tar.gz", hash = "sha256:5a66966b39ff1c14ef5b2d60c1d842b0141fefff0f4cc6365b4bc9446c652807"}, ] +markers = {main = "python_version < \"3.10\""} [package.dependencies] zipp = ">=0.5" @@ -764,10 +786,12 @@ version = "5.0.7" description = "Read resources from Python packages" optional = false python-versions = ">=3.6" +groups = ["main", "docs"] files = [ {file = "importlib_resources-5.0.7-py3-none-any.whl", hash = "sha256:2238159eb743bd85304a16e0536048b3e991c531d1cd51c4a834d1ccf2829057"}, {file = "importlib_resources-5.0.7.tar.gz", hash = "sha256:4df460394562b4581bb4e4087ad9447bd433148fba44241754ec3152499f1d1b"}, ] +markers = {main = "python_version < \"3.10\""} [package.extras] docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] @@ -779,6 +803,7 @@ version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -790,6 +815,7 @@ version = "3.1.5" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" +groups = ["main", "dev", "docs"] files = [ {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, @@ -807,6 +833,7 @@ version = "0.23.0" description = "Sass for Python: A straightforward binding of libsass for Python." optional = false python-versions = ">=3.8" +groups = ["docs"] files = [ {file = "libsass-0.23.0-cp38-abi3-macosx_11_0_x86_64.whl", hash = "sha256:34cae047cbbfc4ffa832a61cbb110f3c95f5471c6170c842d3fed161e40814dc"}, {file = "libsass-0.23.0-cp38-abi3-macosx_14_0_arm64.whl", hash = "sha256:ea97d1b45cdc2fc3590cb9d7b60f1d8915d3ce17a98c1f2d4dd47ee0d9c68ce6"}, @@ -822,6 +849,7 @@ version = "2.7.1" description = "Python LiveReload is an awesome tool for web developers" optional = false python-versions = ">=3.7" +groups = ["docs"] files = [ {file = "livereload-2.7.1-py3-none-any.whl", hash = "sha256:5201740078c1b9433f4b2ba22cd2729a39b9d0ec0a2cc6b4d3df257df5ad0564"}, {file = "livereload-2.7.1.tar.gz", hash = "sha256:3d9bf7c05673df06e32bea23b494b8d36ca6d10f7d5c3c8a6989608c09c986a9"}, @@ -836,6 +864,7 @@ version = "0.12.2" description = "Platform-independent file locking module" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "lockfile-0.12.2-py2.py3-none-any.whl", hash = "sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa"}, {file = "lockfile-0.12.2.tar.gz", hash = "sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799"}, @@ -847,6 +876,7 @@ version = "5.3.0" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "lxml-5.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dd36439be765e2dde7660212b5275641edbc813e7b24668831a5c8ac91180656"}, {file = "lxml-5.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ae5fe5c4b525aa82b8076c1a59d642c17b6e8739ecf852522c6321852178119d"}, @@ -1001,6 +1031,7 @@ version = "3.7" description = "Python implementation of John Gruber's Markdown." optional = false python-versions = ">=3.8" +groups = ["docs"] files = [ {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, @@ -1019,6 +1050,7 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" +groups = ["main", "dev", "docs"] files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -1089,6 +1121,7 @@ version = "1.3.4" description = "A deep merge function for 🐍." optional = false python-versions = ">=3.6" +groups = ["docs"] files = [ {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, @@ -1100,6 +1133,7 @@ version = "2.1.3" description = "Manage multiple versions of your MkDocs-powered documentation" optional = false python-versions = "*" +groups = ["docs"] files = [ {file = "mike-2.1.3-py3-none-any.whl", hash = "sha256:d90c64077e84f06272437b464735130d380703a76a5738b152932884c60c062a"}, {file = "mike-2.1.3.tar.gz", hash = "sha256:abd79b8ea483fb0275b7972825d3082e5ae67a41820f8d8a0dc7a3f49944e810"}, @@ -1125,6 +1159,7 @@ version = "1.6.1" description = "Project documentation with Markdown." optional = false python-versions = ">=3.8" +groups = ["docs"] files = [ {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, @@ -1156,6 +1191,7 @@ version = "1.3.0" description = "Automatically link across pages in MkDocs." optional = false python-versions = ">=3.9" +groups = ["docs"] files = [ {file = "mkdocs_autorefs-1.3.0-py3-none-any.whl", hash = "sha256:d180f9778a04e78b7134e31418f238bba56f56d6a8af97873946ff661befffb3"}, {file = "mkdocs_autorefs-1.3.0.tar.gz", hash = "sha256:6867764c099ace9025d6ac24fd07b85a98335fbd30107ef01053697c8f46db61"}, @@ -1172,6 +1208,7 @@ version = "0.1.0" description = "This plugin adds stylesheets to your mkdocs site from `Sass`/`SCSS`." optional = false python-versions = ">=3.6" +groups = ["docs"] files = [ {file = "mkdocs-extra-sass-plugin-0.1.0.tar.gz", hash = "sha256:cca7ae778585514371b22a63bcd69373d77e474edab4b270cf2924e05c879219"}, {file = "mkdocs_extra_sass_plugin-0.1.0-py3-none-any.whl", hash = "sha256:10aa086fa8ef1fc4650f7bb6927deb7bf5bbf5a2dd3178f47e4ef44546b156db"}, @@ -1188,6 +1225,7 @@ version = "0.2.0" description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" optional = false python-versions = ">=3.8" +groups = ["docs"] files = [ {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, @@ -1205,6 +1243,7 @@ version = "9.5.50" description = "Documentation that simply works" optional = false python-versions = ">=3.8" +groups = ["docs"] files = [ {file = "mkdocs_material-9.5.50-py3-none-any.whl", hash = "sha256:f24100f234741f4d423a9d672a909d859668a4f404796be3cf035f10d6050385"}, {file = "mkdocs_material-9.5.50.tar.gz", hash = "sha256:ae5fe16f3d7c9ccd05bb6916a7da7420cf99a9ce5e33debd9d40403a090d5825"}, @@ -1234,6 +1273,7 @@ version = "1.3.1" description = "Extension pack for Python Markdown and MkDocs Material." optional = false python-versions = ">=3.8" +groups = ["docs"] files = [ {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, @@ -1245,6 +1285,7 @@ version = "0.27.0" description = "Automatic documentation from sources, for MkDocs." optional = false python-versions = ">=3.9" +groups = ["docs"] files = [ {file = "mkdocstrings-0.27.0-py3-none-any.whl", hash = "sha256:6ceaa7ea830770959b55a16203ac63da24badd71325b96af950e59fd37366332"}, {file = "mkdocstrings-0.27.0.tar.gz", hash = "sha256:16adca6d6b0a1f9e0c07ff0b02ced8e16f228a9d65a37c063ec4c14d7b76a657"}, @@ -1273,6 +1314,7 @@ version = "1.13.0" description = "A Python handler for mkdocstrings." optional = false python-versions = ">=3.9" +groups = ["docs"] files = [ {file = "mkdocstrings_python-1.13.0-py3-none-any.whl", hash = "sha256:b88bbb207bab4086434743849f8e796788b373bd32e7bfefbf8560ac45d88f97"}, {file = "mkdocstrings_python-1.13.0.tar.gz", hash = "sha256:2dbd5757e8375b9720e81db16f52f1856bf59905428fd7ef88005d1370e2f64c"}, @@ -1289,6 +1331,7 @@ version = "5.1.0" description = "Python extension for MurmurHash (MurmurHash3), a set of fast and robust hash functions." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "mmh3-5.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:eaf4ac5c6ee18ca9232238364d7f2a213278ae5ca97897cafaa123fcc7bb8bec"}, {file = "mmh3-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:48f9aa8ccb9ad1d577a16104834ac44ff640d8de8c0caed09a2300df7ce8460a"}, @@ -1387,6 +1430,7 @@ version = "1.9.1" description = "Node.js virtual environment builder" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, @@ -1398,6 +1442,7 @@ version = "2.3.0" description = "A flexible configuration library" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "omegaconf-2.3.0-py3-none-any.whl", hash = "sha256:7b4df175cdb08ba400f45cae3bdcae7ba8365db4d165fc65fd04b050ab63b46b"}, {file = "omegaconf-2.3.0.tar.gz", hash = "sha256:d5d4b6d29955cc50ad50c46dc269bcd92c6e00f5f90d23ab5fee7bfca4ba4cc7"}, @@ -1413,6 +1458,7 @@ version = "5.2.3" description = "Orderly set" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "orderly_set-5.2.3-py3-none-any.whl", hash = "sha256:d357cedcf67f4ebff0d4cbd5b0997e98eeb65dd24fdf5c990a501ae9e82c7d34"}, {file = "orderly_set-5.2.3.tar.gz", hash = "sha256:571ed97c5a5fca7ddeb6b2d26c19aca896b0ed91f334d9c109edd2f265fb3017"}, @@ -1424,6 +1470,7 @@ version = "3.10.15" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "orjson-3.10.15-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:552c883d03ad185f720d0c09583ebde257e41b9521b74ff40e08b7dec4559c04"}, {file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:616e3e8d438d02e4854f70bfdc03a6bcdb697358dbaa6bcd19cbe24d24ece1f8"}, @@ -1512,6 +1559,7 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["main", "dev", "docs"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -1523,6 +1571,7 @@ version = "0.5.7" description = "Divides large result sets into pages for easier browsing" optional = false python-versions = "*" +groups = ["docs"] files = [ {file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"}, {file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"}, @@ -1538,6 +1587,7 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" +groups = ["docs"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -1549,6 +1599,7 @@ version = "4.9.0" description = "Pexpect allows easy control of interactive console applications." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, @@ -1563,6 +1614,7 @@ version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" +groups = ["dev", "docs"] files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -1579,6 +1631,7 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -1594,6 +1647,7 @@ version = "1.7.1" description = "Plugin for Poetry to enable dynamic versioning based on VCS tags" optional = false python-versions = "<4.0,>=3.7" +groups = ["dev"] files = [ {file = "poetry_dynamic_versioning-1.7.1-py3-none-any.whl", hash = "sha256:70a4a54bee89aef276e3f2f8841f10a6f140b19c5aeb371a1a6095f84fcbe7b1"}, {file = "poetry_dynamic_versioning-1.7.1.tar.gz", hash = "sha256:7304b8459af7b7114cd83429827c4d3d8b7d29df4129dde8dff61c76f93faaa3"}, @@ -1613,6 +1667,7 @@ version = "4.1.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b"}, {file = "pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4"}, @@ -1631,6 +1686,7 @@ version = "6.1.1" description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +groups = ["main"] files = [ {file = "psutil-6.1.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9ccc4316f24409159897799b83004cb1e24f9819b0dcf9c0b68bdcb6cefee6a8"}, {file = "psutil-6.1.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ca9609c77ea3b8481ab005da74ed894035936223422dc591d6772b147421f777"}, @@ -1661,6 +1717,7 @@ version = "0.7.0" description = "Run a subprocess in a pseudo terminal" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, @@ -1672,6 +1729,7 @@ version = "1.28" description = "Pure python implementation of magic file detection" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "puremagic-1.28-py3-none-any.whl", hash = "sha256:e16cb9708ee2007142c37931c58f07f7eca956b3472489106a7245e5c3aa1241"}, {file = "puremagic-1.28.tar.gz", hash = "sha256:195893fc129657f611b86b959aab337207d6df7f25372209269ed9e303c1a8c0"}, @@ -1683,6 +1741,8 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "implementation_name == \"pypy\" or platform_python_implementation != \"PyPy\"" files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -1694,6 +1754,7 @@ version = "3.21.0" description = "Cryptographic library for Python" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +groups = ["main"] files = [ {file = "pycryptodome-3.21.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:dad9bf36eda068e89059d1f07408e397856be9511d7113ea4b586642a429a4fd"}, {file = "pycryptodome-3.21.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:a1752eca64c60852f38bb29e2c86fca30d7672c024128ef5d70cc15868fa10f4"}, @@ -1735,6 +1796,7 @@ version = "2.10.6" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, @@ -1755,6 +1817,7 @@ version = "2.27.2" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, @@ -1867,6 +1930,7 @@ version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" +groups = ["docs"] files = [ {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, @@ -1881,6 +1945,7 @@ version = "2.10.1" description = "JSON Web Token implementation in Python" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb"}, {file = "pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953"}, @@ -1898,6 +1963,7 @@ version = "10.14.1" description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.8" +groups = ["docs"] files = [ {file = "pymdown_extensions-10.14.1-py3-none-any.whl", hash = "sha256:637951cbfbe9874ba28134fb3ce4b8bcadd6aca89ac4998ec29dcbafd554ae08"}, {file = "pymdown_extensions-10.14.1.tar.gz", hash = "sha256:b65801996a0cd4f42a3110810c306c45b7313c09b0610a6f773730f2a9e3c96b"}, @@ -1916,6 +1982,7 @@ version = "3.2.1" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.9" +groups = ["docs"] files = [ {file = "pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1"}, {file = "pyparsing-3.2.1.tar.gz", hash = "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a"}, @@ -1930,6 +1997,7 @@ version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, @@ -1952,6 +2020,7 @@ version = "0.25.2" description = "Pytest support for asyncio" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "pytest_asyncio-0.25.2-py3-none-any.whl", hash = "sha256:0d0bb693f7b99da304a0634afc0a4b19e49d5e0de2d670f38dc4bfa5727c5075"}, {file = "pytest_asyncio-0.25.2.tar.gz", hash = "sha256:3f8ef9a98f45948ea91a0ed3dc4268b5326c0e7bce73892acc654df4262ad45f"}, @@ -1970,6 +2039,7 @@ version = "6.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, @@ -1988,6 +2058,7 @@ version = "1.1.5" description = "pytest plugin that allows you to add environment variables." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest_env-1.1.5-py3-none-any.whl", hash = "sha256:ce90cf8772878515c24b31cd97c7fa1f4481cd68d588419fd45f10ecaee6bc30"}, {file = "pytest_env-1.1.5.tar.gz", hash = "sha256:91209840aa0e43385073ac464a554ad2947cc2fd663a9debf88d03b01e0cc1cf"}, @@ -2006,6 +2077,7 @@ version = "1.1.1" description = "pytest-httpserver is a httpserver for pytest" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest_httpserver-1.1.1-py3-none-any.whl", hash = "sha256:aadc744bfac773a2ea93d05c2ef51fa23c087e3cc5dace3ea9d45cdd4bfe1fe8"}, {file = "pytest_httpserver-1.1.1.tar.gz", hash = "sha256:e5c46c62c0aa65e5d4331228cb2cb7db846c36e429c3e74ca806f284806bf7c6"}, @@ -2020,6 +2092,7 @@ version = "0.34.0" description = "Send responses to httpx." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "pytest_httpx-0.34.0-py3-none-any.whl", hash = "sha256:42cf0a66f7b71b9111db2897e8b38a903abd33a27b11c48aff4a3c7650313af2"}, {file = "pytest_httpx-0.34.0.tar.gz", hash = "sha256:3ca4b0975c0f93b985f17df19e76430c1086b5b0cce32b1af082d8901296a735"}, @@ -2038,6 +2111,7 @@ version = "15.0" description = "pytest plugin to re-run tests to eliminate flaky failures" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "pytest-rerunfailures-15.0.tar.gz", hash = "sha256:2d9ac7baf59f4c13ac730b47f6fa80e755d1ba0581da45ce30b72fb3542b4474"}, {file = "pytest_rerunfailures-15.0-py3-none-any.whl", hash = "sha256:dd150c4795c229ef44320adc9a0c0532c51b78bb7a6843a8c53556b9a611df1a"}, @@ -2053,6 +2127,7 @@ version = "2.3.1" description = "pytest plugin to abort hanging tests" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "pytest-timeout-2.3.1.tar.gz", hash = "sha256:12397729125c6ecbdaca01035b9e5239d4db97352320af155b3f5de1ba5165d9"}, {file = "pytest_timeout-2.3.1-py3-none-any.whl", hash = "sha256:68188cb703edfc6a18fad98dc25a3c61e9f24d644b0b70f33af545219fc7813e"}, @@ -2067,6 +2142,7 @@ version = "3.1.2" description = "Library to implement a well-behaved Unix daemon process." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "python_daemon-3.1.2-py3-none-any.whl", hash = "sha256:b906833cef63502994ad48e2eab213259ed9bb18d54fa8774dcba2ff7864cec6"}, {file = "python_daemon-3.1.2.tar.gz", hash = "sha256:f7b04335adc473de877f5117e26d5f1142f4c9f7cd765408f0877757be5afbf4"}, @@ -2088,6 +2164,7 @@ version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["docs"] files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -2102,6 +2179,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" +groups = ["main", "dev", "docs"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -2164,6 +2242,7 @@ version = "0.1" description = "A custom YAML tag for referencing environment variables in YAML files. " optional = false python-versions = ">=3.6" +groups = ["docs"] files = [ {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, @@ -2178,6 +2257,7 @@ version = "26.2.0" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:ddf33d97d2f52d89f6e6e7ae66ee35a4d9ca6f36eda89c24591b0c40205a3629"}, {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dacd995031a01d16eec825bf30802fceb2c3791ef24bcce48fa98ce40918c27b"}, @@ -2299,6 +2379,7 @@ version = "3.0.15" description = "Check whether an IP address belongs to a cloud provider" optional = false python-versions = "<4.0,>=3.9" +groups = ["main"] files = [ {file = "radixtarget-3.0.15-py3-none-any.whl", hash = "sha256:1e1d0dd3e8742ffcfc42084eb238f31f6785626b876ab63a9f28a29e97bd3bb0"}, {file = "radixtarget-3.0.15.tar.gz", hash = "sha256:dedfad3aea1e973f261b7bc0d8936423f59ae4d082648fd496c6cdfdfa069fea"}, @@ -2310,6 +2391,7 @@ version = "2024.11.6" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" +groups = ["main", "docs"] files = [ {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, @@ -2413,6 +2495,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main", "docs"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -2434,6 +2517,7 @@ version = "2.1.0" description = "File transport adapter for Requests" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "requests_file-2.1.0-py2.py3-none-any.whl", hash = "sha256:cf270de5a4c5874e84599fc5778303d496c10ae5e870bfa378818f35d21bda5c"}, {file = "requests_file-2.1.0.tar.gz", hash = "sha256:0f549a3f3b0699415ac04d167e9cb39bccfb730cb832b4d20be3d9867356e658"}, @@ -2448,6 +2532,7 @@ version = "1.0.1" description = "Resolve abstract dependencies into concrete ones" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "resolvelib-1.0.1-py2.py3-none-any.whl", hash = "sha256:d2da45d1a8dfee81bdd591647783e340ef3bcb104b54c383f70d422ef5cc7dbf"}, {file = "resolvelib-1.0.1.tar.gz", hash = "sha256:04ce76cbd63fded2078ce224785da6ecd42b9564b1390793f64ddecbe997b309"}, @@ -2465,6 +2550,7 @@ version = "0.9.3" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "ruff-0.9.3-py3-none-linux_armv6l.whl", hash = "sha256:7f39b879064c7d9670197d91124a75d118d00b0990586549949aae80cdc16624"}, {file = "ruff-0.9.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a187171e7c09efa4b4cc30ee5d0d55a8d6c5311b3e1b74ac5cb96cc89bafc43c"}, @@ -2492,6 +2578,7 @@ version = "1.3.4" description = "A Python module to customize the process title" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "setproctitle-1.3.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0f6661a69c68349172ba7b4d5dd65fec2b0917abc99002425ad78c3e58cf7595"}, {file = "setproctitle-1.3.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:754bac5e470adac7f7ec2239c485cd0b75f8197ca8a5b86ffb20eb3a3676cc42"}, @@ -2589,6 +2676,7 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["docs"] files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -2600,6 +2688,7 @@ version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, @@ -2611,6 +2700,7 @@ version = "1.0.0" description = "Sans-I/O implementation of SOCKS4, SOCKS4A, and SOCKS5." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "socksio-1.0.0-py3-none-any.whl", hash = "sha256:95dc1f15f9b34e8d7b16f06d74b8ccf48f609af32ab33c608d08761c5dcbb1f3"}, {file = "socksio-1.0.0.tar.gz", hash = "sha256:f88beb3da5b5c38b9890469de67d0cb0f9d494b78b106ca1845f96c10b91c4ac"}, @@ -2622,6 +2712,7 @@ version = "2.6" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.8" +groups = ["main", "docs"] files = [ {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, @@ -2633,6 +2724,7 @@ version = "0.45.3" description = "The little ASGI library that shines." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "starlette-0.45.3-py3-none-any.whl", hash = "sha256:dfb6d332576f136ec740296c7e8bb8c8a7125044e7c6da30744718880cdd059d"}, {file = "starlette-0.45.3.tar.gz", hash = "sha256:2cbcba2a75806f8a41c722141486f37c28e30a0921c5f6fe4346cb0dcee1302f"}, @@ -2651,6 +2743,7 @@ version = "0.8.10" description = "Pretty-print tabular data" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["main"] files = [ {file = "tabulate-0.8.10-py3-none-any.whl", hash = "sha256:0ba055423dbaa164b9e456abe7920c5e8ed33fcc16f6d1b2f2d152c8e1e8b4fc"}, {file = "tabulate-0.8.10.tar.gz", hash = "sha256:6c57f3f3dd7ac2782770155f3adb2db0b1a269637e42f27599925e64b114f519"}, @@ -2665,6 +2758,7 @@ version = "5.1.3" description = "Accurately separates a URL's subdomain, domain, and public suffix, using the Public Suffix List (PSL). By default, this includes the public ICANN TLDs and their exceptions. You can optionally support the Public Suffix List's private domains as well." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "tldextract-5.1.3-py3-none-any.whl", hash = "sha256:78de310cc2ca018692de5ddf320f9d6bd7c5cf857d0fd4f2175f0cdf4440ea75"}, {file = "tldextract-5.1.3.tar.gz", hash = "sha256:d43c7284c23f5dc8a42fd0fee2abede2ff74cc622674e4cb07f514ab3330c338"}, @@ -2686,6 +2780,8 @@ version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_full_version <= \"3.11.0a6\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -2727,6 +2823,7 @@ version = "0.13.2" description = "Style preserving TOML library" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, @@ -2738,6 +2835,7 @@ version = "6.4.2" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." optional = false python-versions = ">=3.8" +groups = ["docs"] files = [ {file = "tornado-6.4.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1"}, {file = "tornado-6.4.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803"}, @@ -2758,10 +2856,12 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main", "dev", "docs"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] +markers = {docs = "python_version < \"3.10\""} [[package]] name = "unidecode" @@ -2769,6 +2869,7 @@ version = "1.3.8" description = "ASCII transliterations of Unicode text" optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "Unidecode-1.3.8-py3-none-any.whl", hash = "sha256:d130a61ce6696f8148a3bd8fe779c99adeb4b870584eeb9526584e9aa091fd39"}, {file = "Unidecode-1.3.8.tar.gz", hash = "sha256:cfdb349d46ed3873ece4586b96aa75258726e2fa8ec21d6f00a591d98806c2f4"}, @@ -2780,6 +2881,7 @@ version = "2.3.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" +groups = ["main", "dev", "docs"] files = [ {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, @@ -2797,6 +2899,7 @@ version = "0.34.0" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4"}, {file = "uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9"}, @@ -2816,6 +2919,7 @@ version = "0.1.0" description = "Flexible version handling" optional = false python-versions = "*" +groups = ["docs"] files = [ {file = "verspec-0.1.0-py3-none-any.whl", hash = "sha256:741877d5633cc9464c45a469ae2a31e801e6dbbaa85b9675d481cda100f11c31"}, {file = "verspec-0.1.0.tar.gz", hash = "sha256:c4504ca697b2056cdb4bfa7121461f5a0e81809255b41c03dda4ba823637c01e"}, @@ -2830,6 +2934,7 @@ version = "20.29.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "virtualenv-20.29.1-py3-none-any.whl", hash = "sha256:4e4cb403c0b0da39e13b46b1b2476e505cb0046b25f242bee80f62bf990b2779"}, {file = "virtualenv-20.29.1.tar.gz", hash = "sha256:b8b8970138d32fb606192cb97f6cd4bb644fa486be9308fb9b63f81091b5dc35"}, @@ -2850,6 +2955,7 @@ version = "6.0.0" description = "Filesystem events monitoring" optional = false python-versions = ">=3.9" +groups = ["docs"] files = [ {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"}, {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"}, @@ -2892,6 +2998,7 @@ version = "14.2" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "websockets-14.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e8179f95323b9ab1c11723e5d91a89403903f7b001828161b480a7810b334885"}, {file = "websockets-14.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d8c3e2cdb38f31d8bd7d9d28908005f6fa9def3324edb9bf336d7e4266fd397"}, @@ -2970,6 +3077,7 @@ version = "3.1.3" description = "The comprehensive WSGI web application library." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"}, {file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"}, @@ -2987,6 +3095,7 @@ version = "2.0.0" description = "Probabilistically split concatenated words using NLP based on English Wikipedia uni-gram frequencies." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "wordninja-2.0.0.tar.gz", hash = "sha256:1a1cc7ec146ad19d6f71941ee82aef3d31221700f0d8bf844136cf8df79d281a"}, ] @@ -2997,6 +3106,7 @@ version = "0.14.2" description = "Makes working with XML feel like you are working with JSON" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "xmltodict-0.14.2-py2.py3-none-any.whl", hash = "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac"}, {file = "xmltodict-0.14.2.tar.gz", hash = "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553"}, @@ -3008,6 +3118,7 @@ version = "2.0.3" description = "A Python module and cli tool to quickly convert xml text or files into json" optional = false python-versions = "<4.0,>=3.7" +groups = ["main"] files = [ {file = "xmltojson-2.0.3-py3-none-any.whl", hash = "sha256:1b68519bd14fbf3e28baa630b8c9116b5d3aa8976648f277a78ae3448498889a"}, {file = "xmltojson-2.0.3.tar.gz", hash = "sha256:68a0022272adf70b8f2639186172c808e9502cd03c0b851a65e0760561c7801d"}, @@ -3022,6 +3133,7 @@ version = "4.5.1" description = "Python interface for YARA" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "yara_python-4.5.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c92219bf91caea277bc2736df70dda3709834c297a4a5906f1d9a46cd03579a"}, {file = "yara_python-4.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6e8e9eb5a49a70a013bf45e0ec97210b7cb124813271fddc666c3cfb1308a2d5"}, @@ -3111,10 +3223,12 @@ version = "3.21.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" +groups = ["main", "docs"] files = [ {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, ] +markers = {main = "python_version < \"3.10\""} [package.extras] check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] @@ -3125,6 +3239,6 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", type = ["pytest-mypy"] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.9" -content-hash = "3261a0d0a6b9e2b5c700abbb60fc1381527d877ff82377cb0dcbc08ad5351931" +content-hash = "812f9f7b3c0596c85254c09481d16cebda2744bf7ae1dc5d17813c78049ceca3" diff --git a/pyproject.toml b/pyproject.toml index b6946ba968..2c32dd48c0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,6 @@ deepdiff = "^8.0.0" xmltojson = "^2.0.2" pycryptodome = "^3.17" idna = "^3.4" -ansible = ">=7.3,<9.0" tabulate = "0.8.10" websockets = ">=11.0.2,<15.0.0" pyjwt = "^2.7.0" @@ -57,6 +56,7 @@ pydantic = "^2.9.2" radixtarget = "^3.0.13" cloudcheck = "^7.0.12" orjson = "^3.10.12" +ansible-core = "^2.15.13" [tool.poetry.group.dev.dependencies] poetry-dynamic-versioning = ">=0.21.4,<1.8.0" From 5b7a4f5669627c950c17cfe07e21c5585f2084f6 Mon Sep 17 00:00:00 2001 From: Dom Whewell Date: Sat, 8 Feb 2025 15:15:25 +0000 Subject: [PATCH 25/79] Fixed downloading with directory listing enabled --- bbot/modules/gitdumper.py | 139 ++++++++++++++++++++++++++++++++------ 1 file changed, 120 insertions(+), 19 deletions(-) diff --git a/bbot/modules/gitdumper.py b/bbot/modules/gitdumper.py index 6cf3f7c1ac..3079009ac9 100644 --- a/bbot/modules/gitdumper.py +++ b/bbot/modules/gitdumper.py @@ -1,5 +1,5 @@ -import git_dumper from pathlib import Path +from subprocess import CalledProcessError from bbot.modules.base import BaseModule @@ -14,18 +14,11 @@ class gitdumper(BaseModule): } options = { "output_folder": "", - "jobs": 10, - "retry": 3, - "timeout": 3, } options_desc = { "output_folder": "Folder to download repositories to", - "jobs": "Number of concurrent jobs to run", - "retry": "Number of retries for each request", - "timeout": "Request timeout", } - deps_pip = ["git-dumper~=1.0.8"] deps_apt = ["git"] scope_distance_modifier = 2 @@ -37,9 +30,49 @@ async def setup(self): else: self.output_dir = self.scan.home / "git_repos" self.helpers.mkdir(self.output_dir) - self.jobs = self.config.get("jobs", 10) - self.retry = self.config.get("retry", 3) - self.timeout = self.config.get("timeout", 3) + self.git_files = [ + ".git/", + "config", + "hooks/", + "hooks/applypatch-msg", + "hooks/commit-msg", + "hooks/fsmonitor-watchman", + "hooks/post-update", + "hooks/pre-applypatch", + "hooks/pre-commit", + "hooks/pre-merge-commit", + "hooks/pre-push", + "hooks/pre-rebase", + "hooks/pre-receive", + "hooks/prepare-commit-msg", + "hooks/update", + "COMMIT_EDITMSG", + "description", + "FETCH_HEAD", + "HEAD", + "index", + "info/", + "info/exclude", + "logs", + "logs/HEAD", + "logs/refs/", + "logs/refs/remotes/", + "logs/refs/remotes/origin/", + "logs/refs/remotes/origin/HEAD", + "logs/refs/stash", + "ORIG_HEAD", + "packed-refs", + "refs/", + "refs/remotes/", + "refs/remotes/origin/", + "refs/remotes/origin/HEAD", + "refs/stash", + "objects/", + "objects/info/", + "objects/info/alternates", + "objects/info/http-alternates", + "objects/info/packs", + ] return await super().setup() async def filter_event(self, event): @@ -53,8 +86,19 @@ async def handle_event(self, event): self.verbose(f"Processing leaked .git directory at {repo_url}") repo_folder = self.output_dir / self.helpers.tagify(repo_url) self.helpers.mkdir(repo_folder) - error = await self.helpers.run_in_executor(self.process_repo, repo_url, repo_folder) - if not error: + dir_listing = await self.directory_listing_enabled(repo_url) + if dir_listing: + urls = await self.recursive_dir_list(dir_listing) + result = await self.download_files(urls, repo_folder) + else: + self.debug("Directory listing not enabled, fuzzing common git files") + url_list = [] + for file in self.git_files: + url = self.helpers.urljoin(repo_url, file) + url_list.append(self.helpers.urlparse(url)) + result = await self.download_files(url_list, repo_folder) + if result: + await self.git_checkout(repo_folder) codebase_event = self.make_event({"path": str(repo_folder)}, "FILESYSTEM", tags=["git"], parent=event) await self.emit_event( codebase_event, @@ -63,9 +107,66 @@ async def handle_event(self, event): else: self.helpers.rm_rf(repo_folder) - def process_repo(self, repo_url, repo_folder): - http_headers = {"User-Agent": self.scan.useragent} - for hk, hv in self.scan.custom_http_headers.items(): - http_headers[hk] = hv - result = git_dumper.fetch_git(repo_url, repo_folder, self.jobs, self.retry, self.timeout, http_headers) - return result + async def directory_listing_enabled(self, repo_url): + response = await self.helpers.request(repo_url) + if "Index of" in response.text: + self.info(f"Directory listing enabled at {repo_url}") + return response + return None + + async def recursive_dir_list(self, dir_listing): + file_list = [] + soup = self.helpers.beautifulsoup(dir_listing.text, "html.parser") + links = soup.find_all("a") + for link in links: + href = link["href"] + if href == "../" or href == "/": + continue + if href.endswith("/"): + folder_url = self.helpers.urljoin(str(dir_listing.url), href) + url = self.helpers.urlparse(folder_url) + file_list.append(url) + response = await self.helpers.request(folder_url) + if response.status_code == 200: + file_list.extend(await self.recursive_dir_list(response)) + else: + file_url = self.helpers.urljoin(str(dir_listing.url), href) + # Ensure the file is in the same domain as the directory listing + if file_url.startswith(str(dir_listing.url)): + url = self.helpers.urlparse(file_url) + file_list.append(url) + return file_list + + async def add_git_files(self, repo_url): + url_list = [] + self.debug("Adding basic git files to fuzz list") + for file in self.git_files: + url = self.helpers.urljoin(repo_url, file) + url_list.append(url) + return url_list + + async def download_files(self, urls, folder): + self.verbose(f"Downloading the git files to {folder}") + for url in urls: + git_index = url.path.find(".git") + if url.path.endswith("/"): + self.helpers.mkdir(folder / url.path[git_index:]) + else: + file_url = url.geturl() + filename = str(folder / url.path[git_index:]) + self.debug(f"Downloading {file_url} to {filename}") + await self.helpers.download(file_url, filename=filename) + if any(folder.rglob("*")): + return True + else: + self.debug(f"Unable to download git files to {folder}") + return False + + async def git_checkout(self, folder): + self.verbose(f"Running git checkout to reconstruct the git repository at {folder}") + command = ["git", "checkout"] + try: + await self.run_process(command, env={"GIT_TERMINAL_PROMPT": "0"}, cwd=folder, check=True) + except CalledProcessError as e: + # Still emit the event even if the checkout fails + self.debug(f"Error running git checkout in {folder}. STDERR: {repr(e.stderr)}") From 3fcc24e8499bd21397e593b4f0ef2727962712d5 Mon Sep 17 00:00:00 2001 From: github-actions <github-actions@github.com> Date: Sat, 8 Feb 2025 12:14:30 -0500 Subject: [PATCH 26/79] add tech detect preset --- bbot/presets/tech-detect.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 bbot/presets/tech-detect.yml diff --git a/bbot/presets/tech-detect.yml b/bbot/presets/tech-detect.yml new file mode 100644 index 0000000000..5943dacae9 --- /dev/null +++ b/bbot/presets/tech-detect.yml @@ -0,0 +1,11 @@ +description: Detect technologies via Wappalyzer, Nuclei, and FingerprintX + +modules: + - nuclei + - wappalyzer + - fingerprintx + +config: + modules: + nuclei: + tags: tech From 4768fdb61a07bd24c9cd8bea4cb3c603dfcdad59 Mon Sep 17 00:00:00 2001 From: Dom Whewell <dom.whewell@sage.com> Date: Sun, 9 Feb 2025 18:30:34 +0000 Subject: [PATCH 27/79] Add recursive object function --- bbot/modules/gitdumper.py | 121 +++++++++++++++++++++++++------------- 1 file changed, 81 insertions(+), 40 deletions(-) diff --git a/bbot/modules/gitdumper.py b/bbot/modules/gitdumper.py index 3079009ac9..6d6cf8c2be 100644 --- a/bbot/modules/gitdumper.py +++ b/bbot/modules/gitdumper.py @@ -1,3 +1,4 @@ +import regex as re from pathlib import Path from subprocess import CalledProcessError from bbot.modules.base import BaseModule @@ -32,45 +33,40 @@ async def setup(self): self.helpers.mkdir(self.output_dir) self.git_files = [ ".git/", + "HEAD", + "description", "config", - "hooks/", - "hooks/applypatch-msg", - "hooks/commit-msg", - "hooks/fsmonitor-watchman", - "hooks/post-update", - "hooks/pre-applypatch", - "hooks/pre-commit", - "hooks/pre-merge-commit", - "hooks/pre-push", - "hooks/pre-rebase", - "hooks/pre-receive", - "hooks/prepare-commit-msg", - "hooks/update", "COMMIT_EDITMSG", - "description", - "FETCH_HEAD", - "HEAD", "index", + "packed-refs", "info/", + "info/refs", "info/exclude", - "logs", - "logs/HEAD", - "logs/refs/", - "logs/refs/remotes/", - "logs/refs/remotes/origin/", - "logs/refs/remotes/origin/HEAD", - "logs/refs/stash", - "ORIG_HEAD", - "packed-refs", "refs/", + "refs/stash", + "refs/heads/", + "refs/heads/master", "refs/remotes/", "refs/remotes/origin/", "refs/remotes/origin/HEAD", - "refs/stash", + "refs/wip/", + "refs/wip/index/", + "refs/wip/index/refs/", + "refs/wip/index/refs/heads/", + "refs/wip/index/refs/heads/master", + "refs/wip/wtree/", + "refs/wip/wtree/refs/", + "refs/wip/wtree/refs/heads/", + "refs/wip/wtree/refs/heads/master", + "logs/", + "logs/HEAD", + "logs/refs/", + "logs/refs/heads/", + "logs/refs/heads/master", + "logs/refs/remotes/", + "logs/refs/remotes/origin/HEAD", "objects/", "objects/info/", - "objects/info/alternates", - "objects/info/http-alternates", "objects/info/packs", ] return await super().setup() @@ -91,12 +87,7 @@ async def handle_event(self, event): urls = await self.recursive_dir_list(dir_listing) result = await self.download_files(urls, repo_folder) else: - self.debug("Directory listing not enabled, fuzzing common git files") - url_list = [] - for file in self.git_files: - url = self.helpers.urljoin(repo_url, file) - url_list.append(self.helpers.urlparse(url)) - result = await self.download_files(url_list, repo_folder) + result = await self.git_fuzz(repo_url, repo_folder) if result: await self.git_checkout(repo_folder) codebase_event = self.make_event({"path": str(repo_folder)}, "FILESYSTEM", tags=["git"], parent=event) @@ -137,13 +128,52 @@ async def recursive_dir_list(self, dir_listing): file_list.append(url) return file_list - async def add_git_files(self, repo_url): + async def git_fuzz(self, repo_url, repo_folder): + self.debug("Directory listing not enabled, fuzzing common git files") url_list = [] - self.debug("Adding basic git files to fuzz list") for file in self.git_files: - url = self.helpers.urljoin(repo_url, file) - url_list.append(url) - return url_list + url_list.append(self.helpers.urlparse(self.helpers.urljoin(repo_url, file))) + result = await self.download_files(url_list, repo_folder) + if result: + for object in await self.regex_files(r"[a-f0-9]{40}", folder=repo_folder): + await self.download_object(object, repo_url, repo_folder) + return True + else: + return False + + async def regex_files(self, regex_pattern, folder=Path(), file=Path(), files=[]): + regex = re.compile(regex_pattern) + results = [] + if folder: + if folder.is_dir(): + for file_path in folder.rglob("*"): + if file_path.is_file(): + results.extend(await self.regex_file(regex, file_path)) + if files: + for file in files: + results.extend(await self.regex_file(regex, file)) + if file: + results.extend(await self.regex_file(regex, file)) + return results + + async def regex_file(self, regex, file=Path()): + self.debug(f"Searching {file} for regex pattern {regex.pattern}") + if file.exists() and file.is_file(): + with file.open("r", encoding="utf-8", errors="ignore") as file: + content = file.read() + matches = await self.helpers.re.findall(regex, content) + if matches: + return matches + return [] + + async def download_object(self, object, repo_url, repo_folder): + regex = re.compile(r"[a-f0-9]{40}") + output = await self.git_catfile(object, option="-p", folder=repo_folder) + for obj in await self.helpers.re.findall(regex, output): + await self.download_files( + [self.helpers.urlparse(self.helpers.urljoin(repo_url, f"objects/{obj[:2]}/{obj[2:]}"))], repo_folder + ) + await self.download_object(obj, repo_url, repo_folder) async def download_files(self, urls, folder): self.verbose(f"Downloading the git files to {folder}") @@ -155,13 +185,24 @@ async def download_files(self, urls, folder): file_url = url.geturl() filename = str(folder / url.path[git_index:]) self.debug(f"Downloading {file_url} to {filename}") - await self.helpers.download(file_url, filename=filename) + await self.helpers.download(file_url, filename=filename, warn=False) if any(folder.rglob("*")): return True else: self.debug(f"Unable to download git files to {folder}") return False + async def git_catfile(self, hash, option="-t", folder=Path()): + command = ["git", "cat-file", option, hash] + try: + output = await self.run_process(command, env={"GIT_TERMINAL_PROMPT": "0"}, cwd=folder, check=True) + except CalledProcessError as e: + # Still emit the event even if the checkout fails + self.debug(f"Error running git checkout in {folder}. STDERR: {repr(e.stderr)}") + return "" + + return output.strip() + async def git_checkout(self, folder): self.verbose(f"Running git checkout to reconstruct the git repository at {folder}") command = ["git", "checkout"] From 5f54a19e93c0e59d9ead87d14172a891e542ce0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 04:28:00 +0000 Subject: [PATCH 28/79] Bump mkdocstrings from 0.27.0 to 0.28.0 Bumps [mkdocstrings](https://github.com/mkdocstrings/mkdocstrings) from 0.27.0 to 0.28.0. - [Release notes](https://github.com/mkdocstrings/mkdocstrings/releases) - [Changelog](https://github.com/mkdocstrings/mkdocstrings/blob/main/CHANGELOG.md) - [Commits](https://github.com/mkdocstrings/mkdocstrings/compare/0.27.0...0.28.0) --- updated-dependencies: - dependency-name: mkdocstrings dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> --- poetry.lock | 13 ++++++------- pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/poetry.lock b/poetry.lock index 0bf09a941c..8c43f752f5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1241,24 +1241,23 @@ files = [ [[package]] name = "mkdocstrings" -version = "0.27.0" +version = "0.28.0" description = "Automatic documentation from sources, for MkDocs." optional = false python-versions = ">=3.9" files = [ - {file = "mkdocstrings-0.27.0-py3-none-any.whl", hash = "sha256:6ceaa7ea830770959b55a16203ac63da24badd71325b96af950e59fd37366332"}, - {file = "mkdocstrings-0.27.0.tar.gz", hash = "sha256:16adca6d6b0a1f9e0c07ff0b02ced8e16f228a9d65a37c063ec4c14d7b76a657"}, + {file = "mkdocstrings-0.28.0-py3-none-any.whl", hash = "sha256:84cf3dc910614781fe0fee46ce8006fde7df6cc7cca2e3f799895fb8a9170b39"}, + {file = "mkdocstrings-0.28.0.tar.gz", hash = "sha256:df20afef1eafe36ba466ae20732509ecb74237653a585f5061937e54b553b4e0"}, ] [package.dependencies] -click = ">=7.0" importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} Jinja2 = ">=2.11.1" Markdown = ">=3.6" MarkupSafe = ">=1.1" mkdocs = ">=1.4" -mkdocs-autorefs = ">=1.2" -platformdirs = ">=2.2" +mkdocs-autorefs = ">=1.3" +mkdocs-get-deps = ">=0.2" pymdown-extensions = ">=6.3" typing-extensions = {version = ">=4.1", markers = "python_version < \"3.10\""} @@ -3127,4 +3126,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "3261a0d0a6b9e2b5c700abbb60fc1381527d877ff82377cb0dcbc08ad5351931" +content-hash = "d78a98669a2234a86f26c00082105c18a77d054bd6b425f6856f0957ceb5af85" diff --git a/pyproject.toml b/pyproject.toml index b6946ba968..d2ae23c010 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,7 +80,7 @@ mkdocs = "^1.5.2" mkdocs-extra-sass-plugin = "^0.1.0" mkdocs-material = "^9.2.5" mkdocs-material-extensions = "^1.1.1" -mkdocstrings = ">=0.22,<0.28" +mkdocstrings = ">=0.22,<0.29" mkdocstrings-python = "^1.6.0" livereload = "^2.6.3" mike = "^2.1.2" From 9d69473961eb6374f293fc5a28d74a28eb632ccc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 04:28:47 +0000 Subject: [PATCH 29/79] Bump mkdocs-material from 9.5.50 to 9.6.3 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.50 to 9.6.3. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.50...9.6.3) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 0bf09a941c..4d079ffc64 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1201,13 +1201,13 @@ pyyaml = ">=5.1" [[package]] name = "mkdocs-material" -version = "9.5.50" +version = "9.6.3" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.50-py3-none-any.whl", hash = "sha256:f24100f234741f4d423a9d672a909d859668a4f404796be3cf035f10d6050385"}, - {file = "mkdocs_material-9.5.50.tar.gz", hash = "sha256:ae5fe16f3d7c9ccd05bb6916a7da7420cf99a9ce5e33debd9d40403a090d5825"}, + {file = "mkdocs_material-9.6.3-py3-none-any.whl", hash = "sha256:1125622067e26940806701219303b27c0933e04533560725d97ec26fd16a39cf"}, + {file = "mkdocs_material-9.6.3.tar.gz", hash = "sha256:c87f7d1c39ce6326da5e10e232aed51bae46252e646755900f4b0fc9192fa832"}, ] [package.dependencies] From 0970793b0b545d62b46def069cb8333ec74988d0 Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Mon, 10 Feb 2025 11:15:30 -0500 Subject: [PATCH 30/79] fixing telerik FP bug --- bbot/modules/telerik.py | 3 ++- bbot/test/test_step_2/module_tests/test_module_telerik.py | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/bbot/modules/telerik.py b/bbot/modules/telerik.py index e437e08938..efcba1a90f 100644 --- a/bbot/modules/telerik.py +++ b/bbot/modules/telerik.py @@ -1,6 +1,7 @@ from sys import executable from urllib.parse import urlparse +from bbot.core.helpers import helper from bbot.modules.base import BaseModule @@ -316,7 +317,7 @@ async def handle_event(self, event): # The standard behavior for the spellcheck handler without parameters is a 500 if status_code == 500: # Sometimes webapps will just return 500 for everything, so rule out the false positive - validate_result, _ = await self.test_detector(base_url, self.helpers.rand_string()) + validate_result, _ = await self.test_detector(base_url, f"{self.helpers.rand_string()}.axd") self.debug(validate_result) validate_status_code = getattr(validate_result, "status_code", 0) if validate_status_code not in (0, 500): diff --git a/bbot/test/test_step_2/module_tests/test_module_telerik.py b/bbot/test/test_step_2/module_tests/test_module_telerik.py index 8b18f44425..66afa5aa5f 100644 --- a/bbot/test/test_step_2/module_tests/test_module_telerik.py +++ b/bbot/test/test_step_2/module_tests/test_module_telerik.py @@ -1,4 +1,6 @@ import re + +from bbot.core.helpers import helper from .base import ModuleTestBase, tempwordlist @@ -33,6 +35,11 @@ async def setup_before_prep(self, module_test): respond_args = {"status": 500} module_test.set_expect_requests(expect_args=expect_args, respond_args=respond_args) + # Simulate SpellCheckHandler false positive detection + expect_args = {"method": "GET", "uri": "/AAAAAAAAAAAAAA.axd"} + respond_args = {"status": 200} + module_test.set_expect_requests(expect_args=expect_args, respond_args=respond_args) + # Simulate DialogHandler detection expect_args = {"method": "GET", "uri": "/App_Master/Telerik.Web.UI.DialogHandler.aspx"} respond_args = { @@ -64,6 +71,7 @@ async def setup_before_prep(self, module_test): module_test.set_expect_requests(expect_args=expect_args, respond_args=respond_args) async def setup_after_prep(self, module_test): + module_test.scan.modules["telerik"].helpers.rand_string = lambda *args, **kwargs: "AAAAAAAAAAAAAA" module_test.scan.modules["telerik"].telerikVersions = ["2014.2.724", "2014.3.1024", "2015.1.204"] module_test.scan.modules["telerik"].DialogHandlerUrls = [ "Admin/ServerSide/Telerik.Web.UI.DialogHandler.aspx", From 7d952398e8d5aaf0bd14899cda5d640c7d2cfd73 Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Mon, 10 Feb 2025 11:18:49 -0500 Subject: [PATCH 31/79] unnecessary import remove --- bbot/modules/telerik.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bbot/modules/telerik.py b/bbot/modules/telerik.py index efcba1a90f..78874e303f 100644 --- a/bbot/modules/telerik.py +++ b/bbot/modules/telerik.py @@ -1,7 +1,6 @@ from sys import executable from urllib.parse import urlparse -from bbot.core.helpers import helper from bbot.modules.base import BaseModule From ff945319a607f9c099c73ffd5c6390824c164470 Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Mon, 10 Feb 2025 11:37:10 -0500 Subject: [PATCH 32/79] removing import --- bbot/test/test_step_2/module_tests/test_module_telerik.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bbot/test/test_step_2/module_tests/test_module_telerik.py b/bbot/test/test_step_2/module_tests/test_module_telerik.py index 66afa5aa5f..a1c5e77075 100644 --- a/bbot/test/test_step_2/module_tests/test_module_telerik.py +++ b/bbot/test/test_step_2/module_tests/test_module_telerik.py @@ -1,6 +1,5 @@ import re -from bbot.core.helpers import helper from .base import ModuleTestBase, tempwordlist From 7c5ef1f2986f7506f5b7ec17e8f6fd8e606a0c7b Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Mon, 10 Feb 2025 11:37:56 -0500 Subject: [PATCH 33/79] ocd --- bbot/test/test_step_2/module_tests/test_module_telerik.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bbot/test/test_step_2/module_tests/test_module_telerik.py b/bbot/test/test_step_2/module_tests/test_module_telerik.py index a1c5e77075..5302d72573 100644 --- a/bbot/test/test_step_2/module_tests/test_module_telerik.py +++ b/bbot/test/test_step_2/module_tests/test_module_telerik.py @@ -1,5 +1,4 @@ import re - from .base import ModuleTestBase, tempwordlist From 03833c0b84ac3e23ee1b82649ec8e227fdc567ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 16:45:19 +0000 Subject: [PATCH 34/79] Bump deepdiff from 8.1.1 to 8.2.0 Bumps [deepdiff](https://github.com/seperman/deepdiff) from 8.1.1 to 8.2.0. - [Release notes](https://github.com/seperman/deepdiff/releases) - [Changelog](https://github.com/seperman/deepdiff/blob/master/docs/changelog.rst) - [Commits](https://github.com/seperman/deepdiff/compare/8.1.1...8.2.0) --- updated-dependencies: - dependency-name: deepdiff dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> --- poetry.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/poetry.lock b/poetry.lock index 0bf09a941c..7f92936b8e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -512,20 +512,20 @@ test-randomorder = ["pytest-randomly"] [[package]] name = "deepdiff" -version = "8.1.1" +version = "8.2.0" description = "Deep Difference and Search of any Python object/data. Recreate objects by adding adding deltas to each other." optional = false python-versions = ">=3.8" files = [ - {file = "deepdiff-8.1.1-py3-none-any.whl", hash = "sha256:b0231fa3afb0f7184e82535f2b4a36636442ed21e94a0cf3aaa7982157e7ebca"}, - {file = "deepdiff-8.1.1.tar.gz", hash = "sha256:dd7bc7d5c8b51b5b90f01b0e2fe23c801fd8b4c6a7ee7e31c5a3c3663fcc7ceb"}, + {file = "deepdiff-8.2.0-py3-none-any.whl", hash = "sha256:5091f2cdfd372b1b9f6bfd8065ba323ae31118dc4e42594371b38c8bea3fd0a4"}, + {file = "deepdiff-8.2.0.tar.gz", hash = "sha256:6ec78f65031485735545ffbe7a61e716c3c2d12ca6416886d5e9291fc76c46c3"}, ] [package.dependencies] -orderly-set = ">=5.2.3,<6" +orderly-set = ">=5.3.0,<6" [package.extras] -cli = ["click (==8.1.7)", "pyyaml (==6.0.2)"] +cli = ["click (==8.1.8)", "pyyaml (==6.0.2)"] optimize = ["orjson"] [[package]] @@ -1409,13 +1409,13 @@ PyYAML = ">=5.1.0" [[package]] name = "orderly-set" -version = "5.2.3" +version = "5.3.0" description = "Orderly set" optional = false python-versions = ">=3.8" files = [ - {file = "orderly_set-5.2.3-py3-none-any.whl", hash = "sha256:d357cedcf67f4ebff0d4cbd5b0997e98eeb65dd24fdf5c990a501ae9e82c7d34"}, - {file = "orderly_set-5.2.3.tar.gz", hash = "sha256:571ed97c5a5fca7ddeb6b2d26c19aca896b0ed91f334d9c109edd2f265fb3017"}, + {file = "orderly_set-5.3.0-py3-none-any.whl", hash = "sha256:c2c0bfe604f5d3d9b24e8262a06feb612594f37aa3845650548befd7772945d1"}, + {file = "orderly_set-5.3.0.tar.gz", hash = "sha256:80b3d8fdd3d39004d9aad389eaa0eab02c71f0a0511ba3a6d54a935a6c6a0acc"}, ] [[package]] From f3aac433eec08df9c540f9e37d700860ae8f1635 Mon Sep 17 00:00:00 2001 From: Dom Whewell <dom.whewell@sage.com> Date: Mon, 10 Feb 2025 19:16:40 +0000 Subject: [PATCH 35/79] Corrected errors when no dirlisting. Todo fix dirlisting, user messages --- bbot/modules/gitdumper.py | 43 +++++++++------------------------------ 1 file changed, 10 insertions(+), 33 deletions(-) diff --git a/bbot/modules/gitdumper.py b/bbot/modules/gitdumper.py index 6d6cf8c2be..0532b64bed 100644 --- a/bbot/modules/gitdumper.py +++ b/bbot/modules/gitdumper.py @@ -32,41 +32,22 @@ async def setup(self): self.output_dir = self.scan.home / "git_repos" self.helpers.mkdir(self.output_dir) self.git_files = [ - ".git/", "HEAD", "description", "config", "COMMIT_EDITMSG", "index", "packed-refs", - "info/", "info/refs", "info/exclude", - "refs/", "refs/stash", - "refs/heads/", "refs/heads/master", - "refs/remotes/", - "refs/remotes/origin/", "refs/remotes/origin/HEAD", - "refs/wip/", - "refs/wip/index/", - "refs/wip/index/refs/", - "refs/wip/index/refs/heads/", "refs/wip/index/refs/heads/master", - "refs/wip/wtree/", - "refs/wip/wtree/refs/", - "refs/wip/wtree/refs/heads/", "refs/wip/wtree/refs/heads/master", - "logs/", "logs/HEAD", - "logs/refs/", - "logs/refs/heads/", "logs/refs/heads/master", - "logs/refs/remotes/", "logs/refs/remotes/origin/HEAD", - "objects/", - "objects/info/", "objects/info/packs", ] return await super().setup() @@ -115,8 +96,6 @@ async def recursive_dir_list(self, dir_listing): continue if href.endswith("/"): folder_url = self.helpers.urljoin(str(dir_listing.url), href) - url = self.helpers.urlparse(folder_url) - file_list.append(url) response = await self.helpers.request(folder_url) if response.status_code == 200: file_list.extend(await self.recursive_dir_list(response)) @@ -167,25 +146,23 @@ async def regex_file(self, regex, file=Path()): return [] async def download_object(self, object, repo_url, repo_folder): + await self.download_files( + [self.helpers.urlparse(self.helpers.urljoin(repo_url, f"objects/{object[:2]}/{object[2:]}"))], repo_folder + ) regex = re.compile(r"[a-f0-9]{40}") output = await self.git_catfile(object, option="-p", folder=repo_folder) for obj in await self.helpers.re.findall(regex, output): - await self.download_files( - [self.helpers.urlparse(self.helpers.urljoin(repo_url, f"objects/{obj[:2]}/{obj[2:]}"))], repo_folder - ) await self.download_object(obj, repo_url, repo_folder) async def download_files(self, urls, folder): self.verbose(f"Downloading the git files to {folder}") for url in urls: git_index = url.path.find(".git") - if url.path.endswith("/"): - self.helpers.mkdir(folder / url.path[git_index:]) - else: - file_url = url.geturl() - filename = str(folder / url.path[git_index:]) - self.debug(f"Downloading {file_url} to {filename}") - await self.helpers.download(file_url, filename=filename, warn=False) + file_url = url.geturl() + filename = folder / url.path[git_index:] + self.helpers.mkdir(filename.parent) + self.debug(f"Downloading {file_url} to {filename}") + await self.helpers.download(file_url, filename=filename, warn=False) if any(folder.rglob("*")): return True else: @@ -198,10 +175,10 @@ async def git_catfile(self, hash, option="-t", folder=Path()): output = await self.run_process(command, env={"GIT_TERMINAL_PROMPT": "0"}, cwd=folder, check=True) except CalledProcessError as e: # Still emit the event even if the checkout fails - self.debug(f"Error running git checkout in {folder}. STDERR: {repr(e.stderr)}") + self.debug(f"Error running git cat-file in {folder}. STDERR: {repr(e.stderr)}") return "" - return output.strip() + return output.stdout async def git_checkout(self, folder): self.verbose(f"Running git checkout to reconstruct the git repository at {folder}") From 90028c09e3aa901c724b2bb154309d9eecd66c2f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 23:01:47 +0000 Subject: [PATCH 36/79] Bump pytest-asyncio from 0.25.2 to 0.25.3 Bumps [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio) from 0.25.2 to 0.25.3. - [Release notes](https://github.com/pytest-dev/pytest-asyncio/releases) - [Commits](https://github.com/pytest-dev/pytest-asyncio/compare/v0.25.2...v0.25.3) --- updated-dependencies: - dependency-name: pytest-asyncio dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4d6fb957d1..667606f276 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1947,13 +1947,13 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments [[package]] name = "pytest-asyncio" -version = "0.25.2" +version = "0.25.3" description = "Pytest support for asyncio" optional = false python-versions = ">=3.9" files = [ - {file = "pytest_asyncio-0.25.2-py3-none-any.whl", hash = "sha256:0d0bb693f7b99da304a0634afc0a4b19e49d5e0de2d670f38dc4bfa5727c5075"}, - {file = "pytest_asyncio-0.25.2.tar.gz", hash = "sha256:3f8ef9a98f45948ea91a0ed3dc4268b5326c0e7bce73892acc654df4262ad45f"}, + {file = "pytest_asyncio-0.25.3-py3-none-any.whl", hash = "sha256:9e89518e0f9bd08928f97a3482fdc4e244df17529460bc038291ccaf8f85c7c3"}, + {file = "pytest_asyncio-0.25.3.tar.gz", hash = "sha256:fc1da2cf9f125ada7e710b4ddad05518d4cee187ae9412e9ac9271003497f07a"}, ] [package.dependencies] @@ -3126,4 +3126,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "d78a98669a2234a86f26c00082105c18a77d054bd6b425f6856f0957ceb5af85" +content-hash = "32061c19f17a8ee5ae1b7c41a5de6401045fae519df88d41fb19e082be005763" diff --git a/pyproject.toml b/pyproject.toml index d2ae23c010..e96c0140ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,7 +69,7 @@ pytest-rerunfailures = ">=14,<16" pytest-timeout = "^2.3.1" pytest-httpserver = "^1.0.11" pytest = "^8.3.1" -pytest-asyncio = "0.25.2" +pytest-asyncio = "0.25.3" uvicorn = ">=0.32,<0.35" fastapi = "^0.115.5" pytest-httpx = ">=0.33,<0.35" From feedda27be50a149157c9eba0db298edffdd6407 Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Tue, 11 Feb 2025 10:58:20 -0500 Subject: [PATCH 37/79] increasing again to stop rare race condition --- bbot/modules/generic_ssrf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/modules/generic_ssrf.py b/bbot/modules/generic_ssrf.py index 6ccde510b9..dba56b5c4b 100644 --- a/bbot/modules/generic_ssrf.py +++ b/bbot/modules/generic_ssrf.py @@ -254,7 +254,7 @@ async def cleanup(self): async def finish(self): if self.scan.config.get("interactsh_disable", False) is False: - await self.helpers.sleep(5) + await self.helpers.sleep(10) try: for r in await self.interactsh_instance.poll(): await self.interactsh_callback(r) From 96270df2f76c417a4af6eb0d09000ed120bc9a0a Mon Sep 17 00:00:00 2001 From: Dom Whewell <dom.whewell@sage.com> Date: Tue, 11 Feb 2025 16:18:21 +0000 Subject: [PATCH 38/79] Corrected git checkout command and missing index file from test --- bbot/modules/gitdumper.py | 2 +- bbot/test/test_step_2/module_tests/test_module_gitdumper.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/bbot/modules/gitdumper.py b/bbot/modules/gitdumper.py index 0532b64bed..7426ca885f 100644 --- a/bbot/modules/gitdumper.py +++ b/bbot/modules/gitdumper.py @@ -182,7 +182,7 @@ async def git_catfile(self, hash, option="-t", folder=Path()): async def git_checkout(self, folder): self.verbose(f"Running git checkout to reconstruct the git repository at {folder}") - command = ["git", "checkout"] + command = ["git", "checkout", "."] try: await self.run_process(command, env={"GIT_TERMINAL_PROMPT": "0"}, cwd=folder, check=True) except CalledProcessError as e: diff --git a/bbot/test/test_step_2/module_tests/test_module_gitdumper.py b/bbot/test/test_step_2/module_tests/test_module_gitdumper.py index 66995793c0..ad8c237a6b 100644 --- a/bbot/test/test_step_2/module_tests/test_module_gitdumper.py +++ b/bbot/test/test_step_2/module_tests/test_module_gitdumper.py @@ -25,6 +25,7 @@ class TestGitDumper_Dirlisting(ModuleTestBase): <tr><td><a href='/test/.git/hooks/'><hooks></a></td><td></td></tr> <tr><td><a href='/test/.git/info/'><info></a></td><td></td></tr> <tr><td><a href='/test/.git/objects/'><objects></a></td><td></td></tr> + <tr><td><a href='/test/.git/index'>index</a></td><td></td></tr> <tr><td><a href='/test/.git/refs/'><refs></a></td><td></td></tr> <tr><td><a href='/test/.git/logs/'><logs></a></td><td></td></tr> </table> From 447c1b5a615bb5393433a8622627aaa674212f55 Mon Sep 17 00:00:00 2001 From: github-actions <github-actions@github.com> Date: Tue, 11 Feb 2025 11:19:44 -0500 Subject: [PATCH 39/79] fix debug logs --- .github/workflows/distro_tests.yml | 2 +- .github/workflows/tests.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/distro_tests.yml b/.github/workflows/distro_tests.yml index 21f40c8133..b0cf1b69c2 100644 --- a/.github/workflows/distro_tests.yml +++ b/.github/workflows/distro_tests.yml @@ -67,4 +67,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: pytest-debug-logs-${{ matrix.os }} - path: pytest_debug_${{ matrix.os }}.log + path: pytest_debug.log diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c0443b350f..5670d3750a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,4 +1,4 @@ -name: Tests (Python Versions) +name: Tests on: push: branches: @@ -40,7 +40,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: pytest-debug-logs-${{ matrix.python-version }} - path: pytest_debug_${{ matrix.python-version }}.log + path: pytest_debug.log - name: Upload Code Coverage uses: codecov/codecov-action@v5 with: From 4283a842a0d8e7c5c69a6a0358c04f7694683984 Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Tue, 11 Feb 2025 11:48:49 -0500 Subject: [PATCH 40/79] make sure a minimal timeout is set if none specified --- bbot/core/helpers/web/web.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/bbot/core/helpers/web/web.py b/bbot/core/helpers/web/web.py index 8de7a64f17..8dfd0dcb13 100644 --- a/bbot/core/helpers/web/web.py +++ b/bbot/core/helpers/web/web.py @@ -336,6 +336,7 @@ async def curl(self, *args, **kwargs): ignore_bbot_global_settings = kwargs.get("ignore_bbot_global_settings", False) if ignore_bbot_global_settings: + http_timeout = 20 # setting 20 as a worse-case setting log.debug("ignore_bbot_global_settings enabled. Global settings will not be applied") else: http_timeout = self.parent_helper.web_config.get("http_timeout", 20) @@ -349,12 +350,12 @@ async def curl(self, *args, **kwargs): for hk, hv in self.web_config.get("http_headers", {}).items(): headers[hk] = hv - # add the timeout - if "timeout" not in kwargs: - timeout = http_timeout + # add the timeout + if "timeout" not in kwargs: + timeout = http_timeout - curl_command.append("-m") - curl_command.append(str(timeout)) + curl_command.append("-m") + curl_command.append(str(timeout)) for k, v in headers.items(): if isinstance(v, list): @@ -400,7 +401,7 @@ async def curl(self, *args, **kwargs): if raw_body: curl_command.append("-d") curl_command.append(raw_body) - + log.critical(f"Running curl command: {curl_command}") output = (await self.parent_helper.run(curl_command)).stdout return output From ad1d0886a6301bf89b8074a5053d3584faacccdd Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Tue, 11 Feb 2025 11:50:22 -0500 Subject: [PATCH 41/79] verbose --- bbot/core/helpers/web/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/core/helpers/web/web.py b/bbot/core/helpers/web/web.py index 8dfd0dcb13..f34e14c861 100644 --- a/bbot/core/helpers/web/web.py +++ b/bbot/core/helpers/web/web.py @@ -401,7 +401,7 @@ async def curl(self, *args, **kwargs): if raw_body: curl_command.append("-d") curl_command.append(raw_body) - log.critical(f"Running curl command: {curl_command}") + log.verbose(f"Running curl command: {curl_command}") output = (await self.parent_helper.run(curl_command)).stdout return output From 1e5c35db0360c79922fcec078944ceaaea8aa1a0 Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Tue, 11 Feb 2025 12:34:34 -0500 Subject: [PATCH 42/79] fix parsing relative urls with fragments --- bbot/modules/internal/excavate.py | 4 ++-- bbot/test/test_step_2/module_tests/test_module_excavate.py | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/bbot/modules/internal/excavate.py b/bbot/modules/internal/excavate.py index ada3bda796..69a1a32be3 100644 --- a/bbot/modules/internal/excavate.py +++ b/bbot/modules/internal/excavate.py @@ -9,7 +9,7 @@ import bbot.core.helpers.regexes as bbot_regexes from bbot.modules.base import BaseInterceptModule from bbot.modules.internal.base import BaseInternalModule -from urllib.parse import urlparse, urljoin, parse_qs, urlunparse +from urllib.parse import urlparse, urljoin, parse_qs, urlunparse, urldefrag def find_subclasses(obj, base_class): @@ -736,7 +736,7 @@ async def process(self, yara_results, event, yara_rule_settings, discovery_conte continue unescaped_url = html.unescape(m.group(1)) source_url = event.parsed_url.geturl() - final_url = urljoin(source_url, unescaped_url) + final_url = urldefrag(urljoin(source_url, unescaped_url)).url if not await self.helpers.re.search(self.full_url_regex_strict, final_url): self.excavate.debug( f"Rejecting reconstructed URL [{final_url}] as did not match full_url_regex_strict" diff --git a/bbot/test/test_step_2/module_tests/test_module_excavate.py b/bbot/test/test_step_2/module_tests/test_module_excavate.py index 1410b695ad..1e1e8db436 100644 --- a/bbot/test/test_step_2/module_tests/test_module_excavate.py +++ b/bbot/test/test_step_2/module_tests/test_module_excavate.py @@ -30,6 +30,7 @@ async def setup_before_prep(self, module_test): <a href="/a_relative.txt"> <link href="/link_relative.txt"> <a href="mailto:bob@evilcorp.org?subject=help">Help</a> + <li class="toctree-l3"><a class="reference internal" href="miscellaneous.html#x50-uart-driver">16x50 UART Driver</a></li> """ expect_args = {"method": "GET", "uri": "/"} respond_args = {"response_data": response_data} @@ -103,6 +104,11 @@ def check(self, module_test, events): for e in events ) + assert any( + e.type == "URL_UNVERIFIED" and "miscellaneous.html" in e.data and "x50-uart-driver" not in e.data + for e in events + ) + class TestExcavate2(TestExcavate): targets = ["http://127.0.0.1:8888/", "test.notreal", "http://127.0.0.1:8888/subdir/"] From a3af65852b336206280ed528dda320383f533063 Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Tue, 11 Feb 2025 13:24:56 -0500 Subject: [PATCH 43/79] make mock_interaction async --- bbot/test/conftest.py | 5 +++-- .../test_step_2/module_tests/test_module_dotnetnuke.py | 3 ++- .../test_step_2/module_tests/test_module_generic_ssrf.py | 7 ++++--- .../test_step_2/module_tests/test_module_host_header.py | 3 ++- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/bbot/test/conftest.py b/bbot/test/conftest.py index f6c2ebff98..b8902fbc21 100644 --- a/bbot/test/conftest.py +++ b/bbot/test/conftest.py @@ -134,11 +134,12 @@ def __init__(self, name): self.poll_task = None self.lock = asyncio.Lock() - def mock_interaction(self, subdomain_tag, msg=None): + async def mock_interaction(self, subdomain_tag, msg=None): self.log.info(f"Mocking interaction to subdomain tag: {subdomain_tag}") if msg is not None: self.log.info(msg) - self.interactions.append(subdomain_tag) + async with self.lock: + self.interactions.append(subdomain_tag) async def register(self, callback=None): if callable(callback): diff --git a/bbot/test/test_step_2/module_tests/test_module_dotnetnuke.py b/bbot/test/test_step_2/module_tests/test_module_dotnetnuke.py index fc666b64eb..1bfdfc7946 100644 --- a/bbot/test/test_step_2/module_tests/test_module_dotnetnuke.py +++ b/bbot/test/test_step_2/module_tests/test_module_dotnetnuke.py @@ -1,3 +1,4 @@ +import asyncio import re from .base import ModuleTestBase from werkzeug.wrappers import Response @@ -142,7 +143,7 @@ def request_handler(self, request): subdomain_tag = None subdomain_tag = extract_subdomain_tag(request.full_path) if subdomain_tag: - self.interactsh_mock_instance.mock_interaction(subdomain_tag) + asyncio.run(self.interactsh_mock_instance.mock_interaction(subdomain_tag)) return Response("alive", status=200) async def setup_before_prep(self, module_test): diff --git a/bbot/test/test_step_2/module_tests/test_module_generic_ssrf.py b/bbot/test/test_step_2/module_tests/test_module_generic_ssrf.py index d0cc5255c3..919e4360f8 100644 --- a/bbot/test/test_step_2/module_tests/test_module_generic_ssrf.py +++ b/bbot/test/test_step_2/module_tests/test_module_generic_ssrf.py @@ -1,4 +1,5 @@ import re +import asyncio from werkzeug.wrappers import Response from .base import ModuleTestBase @@ -23,15 +24,15 @@ def request_handler(self, request): elif request.method == "POST": subdomain_tag = extract_subdomain_tag(request.data.decode()) if subdomain_tag: - self.interactsh_mock_instance.mock_interaction( + asyncio.run(self.interactsh_mock_instance.mock_interaction( subdomain_tag, msg=f"{request.method}: {request.data.decode()}" - ) + )) return Response("alive", status=200) async def setup_before_prep(self, module_test): self.interactsh_mock_instance = module_test.mock_interactsh("generic_ssrf") - self.interactsh_mock_instance.mock_interaction("asdf") + asyncio.run(self.interactsh_mock_instance.mock_interaction("asdf")) module_test.monkeypatch.setattr( module_test.scan.helpers, "interactsh", lambda *args, **kwargs: self.interactsh_mock_instance ) diff --git a/bbot/test/test_step_2/module_tests/test_module_host_header.py b/bbot/test/test_step_2/module_tests/test_module_host_header.py index a2d69e9b57..ffb6c05fa6 100644 --- a/bbot/test/test_step_2/module_tests/test_module_host_header.py +++ b/bbot/test/test_step_2/module_tests/test_module_host_header.py @@ -1,3 +1,4 @@ +import asyncio import re from werkzeug.wrappers import Response @@ -23,7 +24,7 @@ def request_handler(self, request): # Standard (with reflection) if subdomain_tag: - self.interactsh_mock_instance.mock_interaction(subdomain_tag) + asyncio.run(self.interactsh_mock_instance.mock_interaction(subdomain_tag)) return Response(f"Alive, host is: {subdomain_tag}.{self.fake_host}", status=200) # Host Header Overrides From 4d2c88ae1d093ef9f130c321a7f27217ebc92cbe Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Tue, 11 Feb 2025 13:25:32 -0500 Subject: [PATCH 44/79] ruff format --- .../test_step_2/module_tests/test_module_generic_ssrf.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bbot/test/test_step_2/module_tests/test_module_generic_ssrf.py b/bbot/test/test_step_2/module_tests/test_module_generic_ssrf.py index 919e4360f8..627e2c42a4 100644 --- a/bbot/test/test_step_2/module_tests/test_module_generic_ssrf.py +++ b/bbot/test/test_step_2/module_tests/test_module_generic_ssrf.py @@ -24,9 +24,11 @@ def request_handler(self, request): elif request.method == "POST": subdomain_tag = extract_subdomain_tag(request.data.decode()) if subdomain_tag: - asyncio.run(self.interactsh_mock_instance.mock_interaction( - subdomain_tag, msg=f"{request.method}: {request.data.decode()}" - )) + asyncio.run( + self.interactsh_mock_instance.mock_interaction( + subdomain_tag, msg=f"{request.method}: {request.data.decode()}" + ) + ) return Response("alive", status=200) From 2d0c35c753740daa84af3a43b67f1aa1e5130038 Mon Sep 17 00:00:00 2001 From: Dom Whewell <dom.whewell@sage.com> Date: Tue, 11 Feb 2025 19:00:57 +0000 Subject: [PATCH 45/79] Add sub to regex helper --- bbot/core/helpers/regex.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bbot/core/helpers/regex.py b/bbot/core/helpers/regex.py index 97d8cbe6ec..044684b83e 100644 --- a/bbot/core/helpers/regex.py +++ b/bbot/core/helpers/regex.py @@ -31,6 +31,10 @@ async def search(self, compiled_regex, *args, **kwargs): self.ensure_compiled_regex(compiled_regex) return await self.parent_helper.run_in_executor(compiled_regex.search, *args, **kwargs) + async def sub(self, compiled_regex, *args, **kwargs): + self.ensure_compiled_regex(compiled_regex) + return await self.parent_helper.run_in_executor(compiled_regex.sub, *args, **kwargs) + async def findall(self, compiled_regex, *args, **kwargs): self.ensure_compiled_regex(compiled_regex) return await self.parent_helper.run_in_executor(compiled_regex.findall, *args, **kwargs) From d56e7c2954f9ac5cc09628d63277c6922648456d Mon Sep 17 00:00:00 2001 From: Dom Whewell <dom.whewell@sage.com> Date: Tue, 11 Feb 2025 19:02:25 +0000 Subject: [PATCH 46/79] Add option to fuzz tags / branches and ensure we only download files once --- bbot/modules/gitdumper.py | 108 +++++++++++++++++++++++++++++++------- 1 file changed, 90 insertions(+), 18 deletions(-) diff --git a/bbot/modules/gitdumper.py b/bbot/modules/gitdumper.py index 7426ca885f..46ef69d50d 100644 --- a/bbot/modules/gitdumper.py +++ b/bbot/modules/gitdumper.py @@ -10,14 +10,18 @@ class gitdumper(BaseModule): flags = ["passive", "safe", "slow", "code-enum"] meta = { "description": "Download a leaked .git folder recursively or by fuzzing common names", - "created_date": "", + "created_date": "2025-02-11", "author": "@domwhewell-sage", } options = { "output_folder": "", + "fuzz_tags": False, + "max_semanic_version": 10, } options_desc = { "output_folder": "Folder to download repositories to", + "fuzz_tags": "Fuzz for common git tag names (v0.0.1, 0.0.2, etc.) up to the max_semanic_version", + "max_semanic_version": "Maximum version number to fuzz for (default < v10.10.10)", } deps_apt = ["git"] @@ -25,12 +29,17 @@ class gitdumper(BaseModule): scope_distance_modifier = 2 async def setup(self): + self.urls_downloaded = set() output_folder = self.config.get("output_folder") if output_folder: self.output_dir = Path(output_folder) / "git_repos" else: self.output_dir = self.scan.home / "git_repos" self.helpers.mkdir(self.output_dir) + self.unsafe_regex = self.helpers.re.compile(r"^\s*fsmonitor|sshcommand|askpass|editor|pager", re.IGNORECASE) + self.ref_regex = self.helpers.re.compile(r"ref: refs/heads/([a-zA-Z\d_-]+)") + self.obj_regex = self.helpers.re.compile(r"[a-f0-9]{40}") + self.pack_regex = self.helpers.re.compile(r"pack-([a-f0-9]{40})\.pack") self.git_files = [ "HEAD", "description", @@ -41,15 +50,53 @@ async def setup(self): "info/refs", "info/exclude", "refs/stash", - "refs/heads/master", - "refs/remotes/origin/HEAD", "refs/wip/index/refs/heads/master", "refs/wip/wtree/refs/heads/master", "logs/HEAD", - "logs/refs/heads/master", - "logs/refs/remotes/origin/HEAD", "objects/info/packs", ] + self.debug("Adding common git branch names to fuzz list") + branch_names = [ + "daily", + "dev", + "feature", + "feat", + "fix", + "hotfix", + "issue", + "main", + "master", + "ng", + "quickfix", + "release", + "test", + "testing", + "wip", + ] + url_patterns = [ + "logs/refs/heads/{branch}", + "logs/refs/remotes/origin/{branch}", + "refs/remotes/origin/{branch}", + "refs/heads/{branch}", + ] + for branch in branch_names: + for pattern in url_patterns: + self.git_files.append(pattern.format(branch=branch)) + self.fuzz_tags = self.config.get("fuzz_tags", "10") + self.max_semanic_version = self.config.get("max_semanic_version", "10") + if self.fuzz_tags: + self.debug("Adding symantec version tags to fuzz list") + for major in range(self.max_semanic_version): + for minor in range(self.max_semanic_version): + for patch in range(self.max_semanic_version): + self.debug(f"{major}.{minor}.{patch}") + self.git_files.append(f"refs/tags/{major}.{minor}.{patch}") + self.debug(f"v{major}.{minor}.{patch}") + self.git_files.append(f"refs/tags/v{major}.{minor}.{patch}") + else: + self.debug("Adding symantec version tags to fuzz list (v0.0.1, 0.0.1, v1.0.0, 1.0.0)") + for path in ["refs/tags/v0.0.1", "refs/tags/0.0.1", "refs/tags/v1.0.0", "refs/tags/1.0.0"]: + self.git_files.append(path) return await super().setup() async def filter_event(self, event): @@ -70,6 +117,7 @@ async def handle_event(self, event): else: result = await self.git_fuzz(repo_url, repo_folder) if result: + await self.sanitize_config(repo_folder) await self.git_checkout(repo_folder) codebase_event = self.make_event({"path": str(repo_folder)}, "FILESYSTEM", tags=["git"], parent=event) await self.emit_event( @@ -114,14 +162,32 @@ async def git_fuzz(self, repo_url, repo_folder): url_list.append(self.helpers.urlparse(self.helpers.urljoin(repo_url, file))) result = await self.download_files(url_list, repo_folder) if result: - for object in await self.regex_files(r"[a-f0-9]{40}", folder=repo_folder): - await self.download_object(object, repo_url, repo_folder) + await self.download_current_branch(repo_url, repo_folder) + await self.download_git_objects(repo_url, repo_folder) + await self.download_git_packs(repo_url, repo_folder) return True else: return False - async def regex_files(self, regex_pattern, folder=Path(), file=Path(), files=[]): - regex = re.compile(regex_pattern) + async def download_current_branch(self, repo_url, repo_folder): + for branch in await self.regex_files(self.ref_regex, file=repo_folder / ".git/HEAD"): + await self.download_files( + [self.helpers.urlparse(self.helpers.urljoin(repo_url, f"refs/heads/{branch}"))], repo_folder + ) + + async def download_git_objects(self, url, folder): + for object in await self.regex_files(self.obj_regex, folder=folder): + await self.download_object(object, url, folder) + + async def download_git_packs(self, url, folder): + url_list = [] + for sha1 in await self.regex_files(self.pack_regex, file=folder / ".git/objects/info/packs"): + url_list.append(self.helpers.urlparse(self.helpers.urljoin(url, f"objects/pack/pack-{sha1}.idx"))) + url_list.append(self.helpers.urlparse(self.helpers.urljoin(url, f"objects/pack/pack-{sha1}.pack"))) + if url_list: + await self.download_files(url_list, folder) + + async def regex_files(self, regex, folder=Path(), file=Path(), files=[]): results = [] if folder: if folder.is_dir(): @@ -136,7 +202,6 @@ async def regex_files(self, regex_pattern, folder=Path(), file=Path(), files=[]) return results async def regex_file(self, regex, file=Path()): - self.debug(f"Searching {file} for regex pattern {regex.pattern}") if file.exists() and file.is_file(): with file.open("r", encoding="utf-8", errors="ignore") as file: content = file.read() @@ -149,33 +214,40 @@ async def download_object(self, object, repo_url, repo_folder): await self.download_files( [self.helpers.urlparse(self.helpers.urljoin(repo_url, f"objects/{object[:2]}/{object[2:]}"))], repo_folder ) - regex = re.compile(r"[a-f0-9]{40}") output = await self.git_catfile(object, option="-p", folder=repo_folder) - for obj in await self.helpers.re.findall(regex, output): + for obj in await self.helpers.re.findall(self.obj_regex, output): await self.download_object(obj, repo_url, repo_folder) async def download_files(self, urls, folder): - self.verbose(f"Downloading the git files to {folder}") for url in urls: git_index = url.path.find(".git") file_url = url.geturl() filename = folder / url.path[git_index:] self.helpers.mkdir(filename.parent) - self.debug(f"Downloading {file_url} to {filename}") - await self.helpers.download(file_url, filename=filename, warn=False) + if hash(str(file_url)) not in self.urls_downloaded: + self.debug(f"Downloading {file_url} to {filename}") + await self.helpers.download(file_url, filename=filename, warn=False) + self.urls_downloaded.add(hash(str(file_url))) if any(folder.rglob("*")): return True else: self.debug(f"Unable to download git files to {folder}") return False + async def sanitize_config(self, folder): + config_file = folder / ".git/config" + if config_file.exists(): + with config_file.open("r", encoding="utf-8", errors="ignore") as file: + content = file.read() + sanitized = await self.helpers.re.sub(self.unsafe_regex, "# \g<0>", content) + with config_file.open("w", encoding="utf-8") as file: + file.write(sanitized) + async def git_catfile(self, hash, option="-t", folder=Path()): command = ["git", "cat-file", option, hash] try: output = await self.run_process(command, env={"GIT_TERMINAL_PROMPT": "0"}, cwd=folder, check=True) - except CalledProcessError as e: - # Still emit the event even if the checkout fails - self.debug(f"Error running git cat-file in {folder}. STDERR: {repr(e.stderr)}") + except CalledProcessError: return "" return output.stdout From 406470aee643840e5d71871a604f2adad8efe76f Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Tue, 11 Feb 2025 14:02:46 -0500 Subject: [PATCH 47/79] why was this here? --- bbot/test/test_step_2/module_tests/test_module_generic_ssrf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bbot/test/test_step_2/module_tests/test_module_generic_ssrf.py b/bbot/test/test_step_2/module_tests/test_module_generic_ssrf.py index 627e2c42a4..c0911fd661 100644 --- a/bbot/test/test_step_2/module_tests/test_module_generic_ssrf.py +++ b/bbot/test/test_step_2/module_tests/test_module_generic_ssrf.py @@ -34,7 +34,6 @@ def request_handler(self, request): async def setup_before_prep(self, module_test): self.interactsh_mock_instance = module_test.mock_interactsh("generic_ssrf") - asyncio.run(self.interactsh_mock_instance.mock_interaction("asdf")) module_test.monkeypatch.setattr( module_test.scan.helpers, "interactsh", lambda *args, **kwargs: self.interactsh_mock_instance ) From 11bffa30516bce2168a2a43c35758bb74a065a38 Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Tue, 11 Feb 2025 15:01:58 -0500 Subject: [PATCH 48/79] better mocking of poll function --- bbot/test/conftest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bbot/test/conftest.py b/bbot/test/conftest.py index b8902fbc21..6693c7eb1e 100644 --- a/bbot/test/conftest.py +++ b/bbot/test/conftest.py @@ -163,13 +163,13 @@ async def poll_loop(self, callback=None): async def poll(self, callback=None): async with self.lock: poll_results = [] - for subdomain_tag in self.interactions: + while self.interactions: + subdomain_tag = self.interactions.pop(0) for protocol in ["HTTP", "DNS"]: result = {"full-id": f"{subdomain_tag}.fakedomain.fakeinteractsh.com", "protocol": protocol} poll_results.append(result) if callback is not None: await execute_sync_or_async(callback, result) - self.interactions = [] return poll_results From 369034704487e33bace7b1da3be9525f6f1f8dd1 Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Tue, 11 Feb 2025 15:45:33 -0500 Subject: [PATCH 49/79] test debugging --- bbot/test/conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bbot/test/conftest.py b/bbot/test/conftest.py index 6693c7eb1e..ba9cb51214 100644 --- a/bbot/test/conftest.py +++ b/bbot/test/conftest.py @@ -147,6 +147,7 @@ async def register(self, callback=None): return "fakedomain.fakeinteractsh.com" async def deregister(self, callback=None): + await asyncio.sleep(2) self.stop = True if self.poll_task is not None: self.poll_task.cancel() From 6ba057a9ea828f5dddd5bfbb0695f5f9a2b1b79b Mon Sep 17 00:00:00 2001 From: Dom Whewell <dom.whewell@sage.com> Date: Tue, 11 Feb 2025 20:48:15 +0000 Subject: [PATCH 50/79] Set info, verbose and debug messages --- bbot/modules/gitdumper.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/bbot/modules/gitdumper.py b/bbot/modules/gitdumper.py index 46ef69d50d..d24e5c3ced 100644 --- a/bbot/modules/gitdumper.py +++ b/bbot/modules/gitdumper.py @@ -55,9 +55,10 @@ async def setup(self): "logs/HEAD", "objects/info/packs", ] - self.debug("Adding common git branch names to fuzz list") + self.info("Compiling fuzz list with common branch names") branch_names = [ "daily", + "stable", "dev", "feature", "feat", @@ -85,16 +86,16 @@ async def setup(self): self.fuzz_tags = self.config.get("fuzz_tags", "10") self.max_semanic_version = self.config.get("max_semanic_version", "10") if self.fuzz_tags: - self.debug("Adding symantec version tags to fuzz list") + self.info("Adding symantec version tags to fuzz list") for major in range(self.max_semanic_version): for minor in range(self.max_semanic_version): for patch in range(self.max_semanic_version): - self.debug(f"{major}.{minor}.{patch}") + self.verbose(f"{major}.{minor}.{patch}") self.git_files.append(f"refs/tags/{major}.{minor}.{patch}") - self.debug(f"v{major}.{minor}.{patch}") + self.verbose(f"v{major}.{minor}.{patch}") self.git_files.append(f"refs/tags/v{major}.{minor}.{patch}") else: - self.debug("Adding symantec version tags to fuzz list (v0.0.1, 0.0.1, v1.0.0, 1.0.0)") + self.info("Adding symantec version tags to fuzz list (v0.0.1, 0.0.1, v1.0.0, 1.0.0)") for path in ["refs/tags/v0.0.1", "refs/tags/0.0.1", "refs/tags/v1.0.0", "refs/tags/1.0.0"]: self.git_files.append(path) return await super().setup() @@ -107,7 +108,7 @@ async def filter_event(self, event): async def handle_event(self, event): repo_url = event.data.get("url") - self.verbose(f"Processing leaked .git directory at {repo_url}") + self.info(f"Processing leaked .git directory at {repo_url}") repo_folder = self.output_dir / self.helpers.tagify(repo_url) self.helpers.mkdir(repo_folder) dir_listing = await self.directory_listing_enabled(repo_url) @@ -156,7 +157,7 @@ async def recursive_dir_list(self, dir_listing): return file_list async def git_fuzz(self, repo_url, repo_folder): - self.debug("Directory listing not enabled, fuzzing common git files") + self.info("Directory listing not enabled, fuzzing common git files") url_list = [] for file in self.git_files: url_list.append(self.helpers.urlparse(self.helpers.urljoin(repo_url, file))) From f8e1d2d28cde873694d04ee522771796afd32631 Mon Sep 17 00:00:00 2001 From: Dom Whewell <dom.whewell@sage.com> Date: Tue, 11 Feb 2025 20:51:07 +0000 Subject: [PATCH 51/79] Changed the download message to verbose --- bbot/modules/gitdumper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/modules/gitdumper.py b/bbot/modules/gitdumper.py index d24e5c3ced..76f089b9c8 100644 --- a/bbot/modules/gitdumper.py +++ b/bbot/modules/gitdumper.py @@ -226,7 +226,7 @@ async def download_files(self, urls, folder): filename = folder / url.path[git_index:] self.helpers.mkdir(filename.parent) if hash(str(file_url)) not in self.urls_downloaded: - self.debug(f"Downloading {file_url} to {filename}") + self.verbose(f"Downloading {file_url} to {filename}") await self.helpers.download(file_url, filename=filename, warn=False) self.urls_downloaded.add(hash(str(file_url))) if any(folder.rglob("*")): From b9a926a786d630827411fdfb331a56d197b4aef6 Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Tue, 11 Feb 2025 17:04:47 -0500 Subject: [PATCH 52/79] async queues <3 --- bbot/modules/generic_ssrf.py | 2 +- bbot/test/conftest.py | 30 ++++++++++++++---------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/bbot/modules/generic_ssrf.py b/bbot/modules/generic_ssrf.py index dba56b5c4b..6ccde510b9 100644 --- a/bbot/modules/generic_ssrf.py +++ b/bbot/modules/generic_ssrf.py @@ -254,7 +254,7 @@ async def cleanup(self): async def finish(self): if self.scan.config.get("interactsh_disable", False) is False: - await self.helpers.sleep(10) + await self.helpers.sleep(5) try: for r in await self.interactsh_instance.poll(): await self.interactsh_callback(r) diff --git a/bbot/test/conftest.py b/bbot/test/conftest.py index ba9cb51214..3e5a061f11 100644 --- a/bbot/test/conftest.py +++ b/bbot/test/conftest.py @@ -128,18 +128,16 @@ class Interactsh_mock: def __init__(self, name): self.name = name self.log = logging.getLogger(f"bbot.interactsh.{self.name}") - self.interactions = [] + self.interactions = asyncio.Queue() # Use asyncio.Queue for safe concurrent access self.correlation_id = "deadbeef-dead-beef-dead-beefdeadbeef" self.stop = False self.poll_task = None - self.lock = asyncio.Lock() async def mock_interaction(self, subdomain_tag, msg=None): self.log.info(f"Mocking interaction to subdomain tag: {subdomain_tag}") if msg is not None: self.log.info(msg) - async with self.lock: - self.interactions.append(subdomain_tag) + await self.interactions.put(subdomain_tag) # Add to the queue async def register(self, callback=None): if callable(callback): @@ -147,7 +145,6 @@ async def register(self, callback=None): return "fakedomain.fakeinteractsh.com" async def deregister(self, callback=None): - await asyncio.sleep(2) self.stop = True if self.poll_task is not None: self.poll_task.cancel() @@ -158,20 +155,21 @@ async def poll_loop(self, callback=None): while not self.stop: data_list = await self.poll(callback) if not data_list: - await asyncio.sleep(1) + await asyncio.sleep(0.5) continue + await asyncio.sleep(2) + await self.poll(callback) async def poll(self, callback=None): - async with self.lock: - poll_results = [] - while self.interactions: - subdomain_tag = self.interactions.pop(0) - for protocol in ["HTTP", "DNS"]: - result = {"full-id": f"{subdomain_tag}.fakedomain.fakeinteractsh.com", "protocol": protocol} - poll_results.append(result) - if callback is not None: - await execute_sync_or_async(callback, result) - return poll_results + poll_results = [] + while not self.interactions.empty(): + subdomain_tag = await self.interactions.get() # Pop from the queue + for protocol in ["HTTP", "DNS"]: + result = {"full-id": f"{subdomain_tag}.fakedomain.fakeinteractsh.com", "protocol": protocol} + poll_results.append(result) + if callback is not None: + await execute_sync_or_async(callback, result) + return poll_results import threading From 4db19ca50701a4d6d7572802b784b425145f13fb Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Tue, 11 Feb 2025 17:32:22 -0500 Subject: [PATCH 53/79] fix test --- bbot/test/test_step_2/module_tests/test_module_dotnetnuke.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/bbot/test/test_step_2/module_tests/test_module_dotnetnuke.py b/bbot/test/test_step_2/module_tests/test_module_dotnetnuke.py index 1bfdfc7946..097fc91633 100644 --- a/bbot/test/test_step_2/module_tests/test_module_dotnetnuke.py +++ b/bbot/test/test_step_2/module_tests/test_module_dotnetnuke.py @@ -172,7 +172,5 @@ def check(self, module_test, events): if e.type == "VULNERABILITY" and "DotNetNuke Blind-SSRF (CVE 2017-0929)" in e.data["description"]: dnn_dnnimagehandler_blindssrf = True - assert self.interactsh_mock_instance.interactions == [] - assert dnn_technology_detection, "DNN Technology Detection Failed" assert dnn_dnnimagehandler_blindssrf, "dnnimagehandler.ashx Blind SSRF Detection Failed" From 0a812de479329384388c9b607269dc2c7d00eff2 Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Tue, 11 Feb 2025 18:24:14 -0500 Subject: [PATCH 54/79] more adjustments --- bbot/test/conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bbot/test/conftest.py b/bbot/test/conftest.py index 3e5a061f11..8fa3264520 100644 --- a/bbot/test/conftest.py +++ b/bbot/test/conftest.py @@ -145,6 +145,7 @@ async def register(self, callback=None): return "fakedomain.fakeinteractsh.com" async def deregister(self, callback=None): + await asyncio.sleep(2) self.stop = True if self.poll_task is not None: self.poll_task.cancel() From 422c490987f7f6ca3106a6acd369b056b5fe84f9 Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Tue, 11 Feb 2025 19:06:42 -0500 Subject: [PATCH 55/79] more adjustments --- bbot/test/conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bbot/test/conftest.py b/bbot/test/conftest.py index 8fa3264520..da1f69dd2e 100644 --- a/bbot/test/conftest.py +++ b/bbot/test/conftest.py @@ -170,6 +170,7 @@ async def poll(self, callback=None): poll_results.append(result) if callback is not None: await execute_sync_or_async(callback, result) + await asyncio.sleep(0.5) return poll_results From 61ec71f03d2be66c10858cfc40d2e082a8ac6d2b Mon Sep 17 00:00:00 2001 From: github-actions <github-actions@github.com> Date: Wed, 12 Feb 2025 10:49:45 -0500 Subject: [PATCH 56/79] fix github workflows --- .github/workflows/distro_tests.yml | 2 +- .github/workflows/tests.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/distro_tests.yml b/.github/workflows/distro_tests.yml index b0cf1b69c2..bbbaad90e1 100644 --- a/.github/workflows/distro_tests.yml +++ b/.github/workflows/distro_tests.yml @@ -66,5 +66,5 @@ jobs: if: always() uses: actions/upload-artifact@v4 with: - name: pytest-debug-logs-${{ matrix.os }} + name: pytest-debug-logs-${{ matrix.os | replace(':', '_') }} path: pytest_debug.log diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5670d3750a..99f53cf8bc 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -39,7 +39,7 @@ jobs: if: always() uses: actions/upload-artifact@v4 with: - name: pytest-debug-logs-${{ matrix.python-version }} + name: pytest-debug-logs-${{ matrix.python-version | replace(':', '_') }} path: pytest_debug.log - name: Upload Code Coverage uses: codecov/codecov-action@v5 From 0bb3510a29dc25e78c049be129a9f9d777d9b120 Mon Sep 17 00:00:00 2001 From: github-actions <github-actions@github.com> Date: Wed, 12 Feb 2025 13:04:14 -0500 Subject: [PATCH 57/79] fix errors --- .github/workflows/distro_tests.yml | 4 +++- .github/workflows/tests.yml | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/distro_tests.yml b/.github/workflows/distro_tests.yml index bbbaad90e1..64ceb05be2 100644 --- a/.github/workflows/distro_tests.yml +++ b/.github/workflows/distro_tests.yml @@ -53,6 +53,8 @@ jobs: python3.11 -m pipx ensurepath pipx install poetry " + - name: Set OS Environment Variable + run: echo "OS_NAME=${{ matrix.os }}" | sed 's/:/_/g' >> $GITHUB_ENV - name: Run tests run: | export PATH="$HOME/.local/bin:$PATH" @@ -66,5 +68,5 @@ jobs: if: always() uses: actions/upload-artifact@v4 with: - name: pytest-debug-logs-${{ matrix.os | replace(':', '_') }} + name: pytest-debug-logs-${{ env.OS_NAME }} path: pytest_debug.log diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 99f53cf8bc..3dbee8d9be 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -24,6 +24,8 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + - name: Set Python Version Environment Variable + run: echo "PYTHON_VERSION=${{ matrix.python-version }}" | sed 's/:/_/g' >> $GITHUB_ENV - name: Install dependencies run: | pip install poetry @@ -39,7 +41,7 @@ jobs: if: always() uses: actions/upload-artifact@v4 with: - name: pytest-debug-logs-${{ matrix.python-version | replace(':', '_') }} + name: pytest-debug-logs-${{ env.PYTHON_VERSION }} path: pytest_debug.log - name: Upload Code Coverage uses: codecov/codecov-action@v5 From bc711589adf3558f40b034c350bf3ba8eb428919 Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Wed, 12 Feb 2025 14:01:10 -0500 Subject: [PATCH 58/79] removing asyncio.run weirdness --- bbot/test/conftest.py | 21 ++++++++++++------- .../module_tests/test_module_dotnetnuke.py | 2 +- .../module_tests/test_module_host_header.py | 2 +- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/bbot/test/conftest.py b/bbot/test/conftest.py index da1f69dd2e..95b9c1fbc1 100644 --- a/bbot/test/conftest.py +++ b/bbot/test/conftest.py @@ -9,6 +9,7 @@ from omegaconf import OmegaConf from pytest_httpserver import HTTPServer import time +import queue from bbot.core import CORE from bbot.core.helpers.misc import execute_sync_or_async @@ -128,16 +129,17 @@ class Interactsh_mock: def __init__(self, name): self.name = name self.log = logging.getLogger(f"bbot.interactsh.{self.name}") - self.interactions = asyncio.Queue() # Use asyncio.Queue for safe concurrent access + self.interactions = queue.Queue() # Use a thread-safe queue for sync access + self.async_interactions = asyncio.Queue() # Use an asyncio queue for async access self.correlation_id = "deadbeef-dead-beef-dead-beefdeadbeef" self.stop = False self.poll_task = None - async def mock_interaction(self, subdomain_tag, msg=None): + def mock_interaction(self, subdomain_tag, msg=None): self.log.info(f"Mocking interaction to subdomain tag: {subdomain_tag}") if msg is not None: self.log.info(msg) - await self.interactions.put(subdomain_tag) # Add to the queue + self.interactions.put(subdomain_tag) # Add to the thread-safe queue async def register(self, callback=None): if callable(callback): @@ -145,7 +147,7 @@ async def register(self, callback=None): return "fakedomain.fakeinteractsh.com" async def deregister(self, callback=None): - await asyncio.sleep(2) + await asyncio.sleep(1) self.stop = True if self.poll_task is not None: self.poll_task.cancel() @@ -158,19 +160,24 @@ async def poll_loop(self, callback=None): if not data_list: await asyncio.sleep(0.5) continue - await asyncio.sleep(2) + await asyncio.sleep(1) await self.poll(callback) async def poll(self, callback=None): poll_results = [] + # Transfer items from the thread-safe queue to the asyncio queue (thanks pytest!) while not self.interactions.empty(): - subdomain_tag = await self.interactions.get() # Pop from the queue + subdomain_tag = self.interactions.get() + await self.async_interactions.put(subdomain_tag) + + while not self.async_interactions.empty(): + subdomain_tag = await self.async_interactions.get() # Get the first element from the asyncio queue for protocol in ["HTTP", "DNS"]: result = {"full-id": f"{subdomain_tag}.fakedomain.fakeinteractsh.com", "protocol": protocol} poll_results.append(result) if callback is not None: await execute_sync_or_async(callback, result) - await asyncio.sleep(0.5) + await asyncio.sleep(0.1) return poll_results diff --git a/bbot/test/test_step_2/module_tests/test_module_dotnetnuke.py b/bbot/test/test_step_2/module_tests/test_module_dotnetnuke.py index 097fc91633..8035316de7 100644 --- a/bbot/test/test_step_2/module_tests/test_module_dotnetnuke.py +++ b/bbot/test/test_step_2/module_tests/test_module_dotnetnuke.py @@ -143,7 +143,7 @@ def request_handler(self, request): subdomain_tag = None subdomain_tag = extract_subdomain_tag(request.full_path) if subdomain_tag: - asyncio.run(self.interactsh_mock_instance.mock_interaction(subdomain_tag)) + self.interactsh_mock_instance.mock_interaction(subdomain_tag) return Response("alive", status=200) async def setup_before_prep(self, module_test): diff --git a/bbot/test/test_step_2/module_tests/test_module_host_header.py b/bbot/test/test_step_2/module_tests/test_module_host_header.py index ffb6c05fa6..2c4cf5a7d3 100644 --- a/bbot/test/test_step_2/module_tests/test_module_host_header.py +++ b/bbot/test/test_step_2/module_tests/test_module_host_header.py @@ -24,7 +24,7 @@ def request_handler(self, request): # Standard (with reflection) if subdomain_tag: - asyncio.run(self.interactsh_mock_instance.mock_interaction(subdomain_tag)) + self.interactsh_mock_instance.mock_interaction(subdomain_tag) return Response(f"Alive, host is: {subdomain_tag}.{self.fake_host}", status=200) # Host Header Overrides From f61943d628e2dd446d7025a96cdb7ec05cfe7eac Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Wed, 12 Feb 2025 14:58:59 -0500 Subject: [PATCH 59/79] reworking wordlist download, removing deps --- bbot/core/helpers/web/web.py | 19 +++++++++++++++++- bbot/modules/ffuf_shortnames.py | 34 +++++++++++---------------------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/bbot/core/helpers/web/web.py b/bbot/core/helpers/web/web.py index 8de7a64f17..6504cc5ada 100644 --- a/bbot/core/helpers/web/web.py +++ b/bbot/core/helpers/web/web.py @@ -232,7 +232,7 @@ async def download(self, url, **kwargs): if success: return filename - async def wordlist(self, path, lines=None, **kwargs): + async def wordlist(self, path, lines=None, zip=False, zip_filename=None, **kwargs): """ Asynchronous function for retrieving wordlists, either from a local path or a URL. Allows for optional line-based truncation and caching. Returns the full path of the wordlist @@ -242,6 +242,9 @@ async def wordlist(self, path, lines=None, **kwargs): path (str): The local or remote path of the wordlist. lines (int, optional): Number of lines to read from the wordlist. If specified, will return a truncated wordlist with this many lines. + zip (bool, optional): Whether to unzip the file after downloading. Defaults to False. + zip_filename (str, optional): The name of the file to extract from the ZIP archive. + Required if zip is True. cache_hrs (float, optional): Number of hours to cache the downloaded wordlist. Defaults to 720 hours (30 days) for remote wordlists. **kwargs: Additional keyword arguments to pass to the 'download' function for remote wordlists. @@ -259,6 +262,8 @@ async def wordlist(self, path, lines=None, **kwargs): Fetching and truncating to the first 100 lines >>> wordlist_path = await self.helpers.wordlist("/root/rockyou.txt", lines=100) """ + import zipfile + if not path: raise WordlistError(f"Invalid wordlist: {path}") if "cache_hrs" not in kwargs: @@ -272,6 +277,18 @@ async def wordlist(self, path, lines=None, **kwargs): if not filename.is_file(): raise WordlistError(f"Unable to find wordlist at {path}") + if zip: + if not zip_filename: + raise WordlistError("zip_filename must be specified when zip is True") + try: + with zipfile.ZipFile(filename, 'r') as zip_ref: + if zip_filename not in zip_ref.namelist(): + raise WordlistError(f"File {zip_filename} not found in the zip archive {filename}") + zip_ref.extract(zip_filename, filename.parent) + filename = filename.parent / zip_filename + except Exception as e: + raise WordlistError(f"Error unzipping file {filename}: {e}") + if lines is None: return filename else: diff --git a/bbot/modules/ffuf_shortnames.py b/bbot/modules/ffuf_shortnames.py index c3f4e46fb0..ce4cd2ec2b 100644 --- a/bbot/modules/ffuf_shortnames.py +++ b/bbot/modules/ffuf_shortnames.py @@ -12,7 +12,6 @@ class ffuf_shortnames(ffuf): watched_events = ["URL_HINT"] produced_events = ["URL_UNVERIFIED"] - deps_pip = ["numpy", "nltk"] flags = ["aggressive", "active", "iis-shortnames", "web-thorough"] meta = { "description": "Use ffuf in combination IIS shortnames", @@ -21,7 +20,6 @@ class ffuf_shortnames(ffuf): } options = { - "wordlist": "", # default is defined within setup function "wordlist_extensions": "", # default is defined within setup function "max_depth": 1, "version": "2.0.0", @@ -34,7 +32,6 @@ class ffuf_shortnames(ffuf): } options_desc = { - "wordlist": "Specify wordlist to use when finding directories", "wordlist_extensions": "Specify wordlist to use when making extension lists", "max_depth": "the maximum directory depth to attempt to solve", "version": "ffuf version", @@ -124,13 +121,14 @@ def find_class(self, module, name): if name == "MinimalWordPredictor": return MinimalWordPredictor return super().find_class(module, name) - + self.info("Loading ffuf_shortnames prediction models, could take a while if not cached") endpoint_model = await self.helpers.wordlist( "https://raw.githubusercontent.com/blacklanternsecurity/wordpredictor/refs/heads/main/trained_models/endpoints.bin" ) directory_model = await self.helpers.wordlist( "https://raw.githubusercontent.com/blacklanternsecurity/wordpredictor/refs/heads/main/trained_models/directories.bin" ) + self.debug(f"Loading endpoint model from: {endpoint_model}") with open(endpoint_model, "rb") as f: unpickler = CustomUnpickler(f) @@ -143,25 +141,15 @@ def find_class(self, module, name): self.subword_list = [] if self.find_subwords: - self.debug("find_subwords is enabled, checking for nltk data") - self.nltk_dir = self.helpers.tools_dir / "nltk_data" - - # Ensure the directory exists - os.makedirs(self.nltk_dir, exist_ok=True) - - # Set the NLTK data path to include self.nltk_dir - nltk.data.path.append(str(self.nltk_dir)) - - try: - nltk.data.find("corpora/words.zip") - self.debug("NLTK words data already present") - except LookupError: - self.debug("NLTK words data not found, downloading") - nltk.download("words", download_dir=self.nltk_dir, quiet=True) - - from nltk.corpus import words - - self.subword_list = {word.lower() for word in words.words() if 3 <= len(word) <= 5} + self.debug("Acquiring ffuf_shortnames subword list") + subwords = await self.helpers.wordlist( + "https://raw.githubusercontent.com/nltk/nltk_data/refs/heads/gh-pages/packages/corpora/words.zip", + zip=True, + zip_filename="words/en" + ) + with open(subwords, 'r') as f: + subword_list_content = f.readlines() + self.subword_list = {word.lower().strip() for word in subword_list_content if 3 <= len(word.strip()) <= 5} self.debug(f"Created subword_list with {len(self.subword_list)} words") self.subword_list = self.subword_list.union(self.supplementary_words) self.debug(f"Extended subword_list with supplementary words, total size: {len(self.subword_list)}") From 74839d5d5f9b92b12e0180489b67395ca403a466 Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Wed, 12 Feb 2025 15:25:54 -0500 Subject: [PATCH 60/79] ruff format --- bbot/core/helpers/web/web.py | 2 +- bbot/modules/ffuf_shortnames.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/bbot/core/helpers/web/web.py b/bbot/core/helpers/web/web.py index 6504cc5ada..9de79c6709 100644 --- a/bbot/core/helpers/web/web.py +++ b/bbot/core/helpers/web/web.py @@ -281,7 +281,7 @@ async def wordlist(self, path, lines=None, zip=False, zip_filename=None, **kwarg if not zip_filename: raise WordlistError("zip_filename must be specified when zip is True") try: - with zipfile.ZipFile(filename, 'r') as zip_ref: + with zipfile.ZipFile(filename, "r") as zip_ref: if zip_filename not in zip_ref.namelist(): raise WordlistError(f"File {zip_filename} not found in the zip archive {filename}") zip_ref.extract(zip_filename, filename.parent) diff --git a/bbot/modules/ffuf_shortnames.py b/bbot/modules/ffuf_shortnames.py index ce4cd2ec2b..e401f5a462 100644 --- a/bbot/modules/ffuf_shortnames.py +++ b/bbot/modules/ffuf_shortnames.py @@ -121,6 +121,7 @@ def find_class(self, module, name): if name == "MinimalWordPredictor": return MinimalWordPredictor return super().find_class(module, name) + self.info("Loading ffuf_shortnames prediction models, could take a while if not cached") endpoint_model = await self.helpers.wordlist( "https://raw.githubusercontent.com/blacklanternsecurity/wordpredictor/refs/heads/main/trained_models/endpoints.bin" @@ -145,9 +146,9 @@ def find_class(self, module, name): subwords = await self.helpers.wordlist( "https://raw.githubusercontent.com/nltk/nltk_data/refs/heads/gh-pages/packages/corpora/words.zip", zip=True, - zip_filename="words/en" + zip_filename="words/en", ) - with open(subwords, 'r') as f: + with open(subwords, "r") as f: subword_list_content = f.readlines() self.subword_list = {word.lower().strip() for word in subword_list_content if 3 <= len(word.strip()) <= 5} self.debug(f"Created subword_list with {len(self.subword_list)} words") From b1e8d11f3c04ae484288f8ed6214e8a6304e0201 Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Wed, 12 Feb 2025 15:36:47 -0500 Subject: [PATCH 61/79] cleaner --- bbot/test/conftest.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/bbot/test/conftest.py b/bbot/test/conftest.py index 95b9c1fbc1..ed9aec159d 100644 --- a/bbot/test/conftest.py +++ b/bbot/test/conftest.py @@ -129,8 +129,7 @@ class Interactsh_mock: def __init__(self, name): self.name = name self.log = logging.getLogger(f"bbot.interactsh.{self.name}") - self.interactions = queue.Queue() # Use a thread-safe queue for sync access - self.async_interactions = asyncio.Queue() # Use an asyncio queue for async access + self.interactions = asyncio.Queue() # Use an asyncio queue for async access self.correlation_id = "deadbeef-dead-beef-dead-beefdeadbeef" self.stop = False self.poll_task = None @@ -139,7 +138,7 @@ def mock_interaction(self, subdomain_tag, msg=None): self.log.info(f"Mocking interaction to subdomain tag: {subdomain_tag}") if msg is not None: self.log.info(msg) - self.interactions.put(subdomain_tag) # Add to the thread-safe queue + self.interactions.put_nowait(subdomain_tag) # Add to the thread-safe queue async def register(self, callback=None): if callable(callback): @@ -165,13 +164,8 @@ async def poll_loop(self, callback=None): async def poll(self, callback=None): poll_results = [] - # Transfer items from the thread-safe queue to the asyncio queue (thanks pytest!) while not self.interactions.empty(): - subdomain_tag = self.interactions.get() - await self.async_interactions.put(subdomain_tag) - - while not self.async_interactions.empty(): - subdomain_tag = await self.async_interactions.get() # Get the first element from the asyncio queue + subdomain_tag = await self.interactions.get() # Get the first element from the asyncio queue for protocol in ["HTTP", "DNS"]: result = {"full-id": f"{subdomain_tag}.fakedomain.fakeinteractsh.com", "protocol": protocol} poll_results.append(result) From 14d986bd935ca55b3189ed2d4c61c42dee1254fb Mon Sep 17 00:00:00 2001 From: github-actions <github-actions@github.com> Date: Wed, 12 Feb 2025 15:39:08 -0500 Subject: [PATCH 62/79] handle slashes --- .github/workflows/distro_tests.yml | 2 +- .github/workflows/tests.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/distro_tests.yml b/.github/workflows/distro_tests.yml index 64ceb05be2..7cee967d7d 100644 --- a/.github/workflows/distro_tests.yml +++ b/.github/workflows/distro_tests.yml @@ -54,7 +54,7 @@ jobs: pipx install poetry " - name: Set OS Environment Variable - run: echo "OS_NAME=${{ matrix.os }}" | sed 's/:/_/g' >> $GITHUB_ENV + run: echo "OS_NAME=${{ matrix.os }}" | sed 's|[:/]|_|g' >> $GITHUB_ENV - name: Run tests run: | export PATH="$HOME/.local/bin:$PATH" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3dbee8d9be..40a8c27287 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -25,7 +25,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Set Python Version Environment Variable - run: echo "PYTHON_VERSION=${{ matrix.python-version }}" | sed 's/:/_/g' >> $GITHUB_ENV + run: echo "PYTHON_VERSION=${{ matrix.python-version }}" | sed 's|[:/]|_|g' >> $GITHUB_ENV - name: Install dependencies run: | pip install poetry From b70d1a0750c6ab9cc877e2ef5951ea00c30da7f0 Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Wed, 12 Feb 2025 15:49:35 -0500 Subject: [PATCH 63/79] removing unneeded imports --- bbot/modules/ffuf_shortnames.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/bbot/modules/ffuf_shortnames.py b/bbot/modules/ffuf_shortnames.py index e401f5a462..6a38acc9ff 100644 --- a/bbot/modules/ffuf_shortnames.py +++ b/bbot/modules/ffuf_shortnames.py @@ -1,10 +1,7 @@ import pickle -import nltk import re import random import string -import logging -import os from bbot.modules.deadly.ffuf import ffuf From b756df260ce7892fa6ae54101692f510f335ac65 Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Wed, 12 Feb 2025 17:46:20 -0500 Subject: [PATCH 64/79] readding numpy --- bbot/modules/ffuf_shortnames.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bbot/modules/ffuf_shortnames.py b/bbot/modules/ffuf_shortnames.py index 6a38acc9ff..2a4cb8d9cb 100644 --- a/bbot/modules/ffuf_shortnames.py +++ b/bbot/modules/ffuf_shortnames.py @@ -40,6 +40,7 @@ class ffuf_shortnames(ffuf): "max_predictions": "The maximum number of predictions to generate per shortname prefix", } + deps_pip = ["numpy"] deps_common = ["ffuf"] in_scope_only = True From e217006ba6135c09892df698813ad79aebc363e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Feb 2025 22:56:09 +0000 Subject: [PATCH 65/79] Bump ruff from 0.9.3 to 0.9.6 Bumps [ruff](https://github.com/astral-sh/ruff) from 0.9.3 to 0.9.6. - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/0.9.3...0.9.6) --- updated-dependencies: - dependency-name: ruff dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> --- poetry.lock | 40 ++++++++++++++++++++-------------------- pyproject.toml | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/poetry.lock b/poetry.lock index fecc235a29..2bb50b7300 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2460,29 +2460,29 @@ test = ["commentjson", "packaging", "pytest"] [[package]] name = "ruff" -version = "0.9.3" +version = "0.9.6" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.9.3-py3-none-linux_armv6l.whl", hash = "sha256:7f39b879064c7d9670197d91124a75d118d00b0990586549949aae80cdc16624"}, - {file = "ruff-0.9.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a187171e7c09efa4b4cc30ee5d0d55a8d6c5311b3e1b74ac5cb96cc89bafc43c"}, - {file = "ruff-0.9.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c59ab92f8e92d6725b7ded9d4a31be3ef42688a115c6d3da9457a5bda140e2b4"}, - {file = "ruff-0.9.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dc153c25e715be41bb228bc651c1e9b1a88d5c6e5ed0194fa0dfea02b026439"}, - {file = "ruff-0.9.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:646909a1e25e0dc28fbc529eab8eb7bb583079628e8cbe738192853dbbe43af5"}, - {file = "ruff-0.9.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a5a46e09355695fbdbb30ed9889d6cf1c61b77b700a9fafc21b41f097bfbba4"}, - {file = "ruff-0.9.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c4bb09d2bbb394e3730d0918c00276e79b2de70ec2a5231cd4ebb51a57df9ba1"}, - {file = "ruff-0.9.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96a87ec31dc1044d8c2da2ebbed1c456d9b561e7d087734336518181b26b3aa5"}, - {file = "ruff-0.9.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb7554aca6f842645022fe2d301c264e6925baa708b392867b7a62645304df4"}, - {file = "ruff-0.9.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cabc332b7075a914ecea912cd1f3d4370489c8018f2c945a30bcc934e3bc06a6"}, - {file = "ruff-0.9.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:33866c3cc2a575cbd546f2cd02bdd466fed65118e4365ee538a3deffd6fcb730"}, - {file = "ruff-0.9.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:006e5de2621304c8810bcd2ee101587712fa93b4f955ed0985907a36c427e0c2"}, - {file = "ruff-0.9.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ba6eea4459dbd6b1be4e6bfc766079fb9b8dd2e5a35aff6baee4d9b1514ea519"}, - {file = "ruff-0.9.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:90230a6b8055ad47d3325e9ee8f8a9ae7e273078a66401ac66df68943ced029b"}, - {file = "ruff-0.9.3-py3-none-win32.whl", hash = "sha256:eabe5eb2c19a42f4808c03b82bd313fc84d4e395133fb3fc1b1516170a31213c"}, - {file = "ruff-0.9.3-py3-none-win_amd64.whl", hash = "sha256:040ceb7f20791dfa0e78b4230ee9dce23da3b64dd5848e40e3bf3ab76468dcf4"}, - {file = "ruff-0.9.3-py3-none-win_arm64.whl", hash = "sha256:800d773f6d4d33b0a3c60e2c6ae8f4c202ea2de056365acfa519aa48acf28e0b"}, - {file = "ruff-0.9.3.tar.gz", hash = "sha256:8293f89985a090ebc3ed1064df31f3b4b56320cdfcec8b60d3295bddb955c22a"}, + {file = "ruff-0.9.6-py3-none-linux_armv6l.whl", hash = "sha256:2f218f356dd2d995839f1941322ff021c72a492c470f0b26a34f844c29cdf5ba"}, + {file = "ruff-0.9.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b908ff4df65dad7b251c9968a2e4560836d8f5487c2f0cc238321ed951ea0504"}, + {file = "ruff-0.9.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b109c0ad2ececf42e75fa99dc4043ff72a357436bb171900714a9ea581ddef83"}, + {file = "ruff-0.9.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1de4367cca3dac99bcbd15c161404e849bb0bfd543664db39232648dc00112dc"}, + {file = "ruff-0.9.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac3ee4d7c2c92ddfdaedf0bf31b2b176fa7aa8950efc454628d477394d35638b"}, + {file = "ruff-0.9.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dc1edd1775270e6aa2386119aea692039781429f0be1e0949ea5884e011aa8e"}, + {file = "ruff-0.9.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4a091729086dffa4bd070aa5dab7e39cc6b9d62eb2bef8f3d91172d30d599666"}, + {file = "ruff-0.9.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1bbc6808bf7b15796cef0815e1dfb796fbd383e7dbd4334709642649625e7c5"}, + {file = "ruff-0.9.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:589d1d9f25b5754ff230dce914a174a7c951a85a4e9270613a2b74231fdac2f5"}, + {file = "ruff-0.9.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc61dd5131742e21103fbbdcad683a8813be0e3c204472d520d9a5021ca8b217"}, + {file = "ruff-0.9.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5e2d9126161d0357e5c8f30b0bd6168d2c3872372f14481136d13de9937f79b6"}, + {file = "ruff-0.9.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:68660eab1a8e65babb5229a1f97b46e3120923757a68b5413d8561f8a85d4897"}, + {file = "ruff-0.9.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c4cae6c4cc7b9b4017c71114115db0445b00a16de3bcde0946273e8392856f08"}, + {file = "ruff-0.9.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:19f505b643228b417c1111a2a536424ddde0db4ef9023b9e04a46ed8a1cb4656"}, + {file = "ruff-0.9.6-py3-none-win32.whl", hash = "sha256:194d8402bceef1b31164909540a597e0d913c0e4952015a5b40e28c146121b5d"}, + {file = "ruff-0.9.6-py3-none-win_amd64.whl", hash = "sha256:03482d5c09d90d4ee3f40d97578423698ad895c87314c4de39ed2af945633caa"}, + {file = "ruff-0.9.6-py3-none-win_arm64.whl", hash = "sha256:0e2bb706a2be7ddfea4a4af918562fdc1bcb16df255e5fa595bbd800ce322a5a"}, + {file = "ruff-0.9.6.tar.gz", hash = "sha256:81761592f72b620ec8fa1068a6fd00e98a5ebee342a3642efd84454f3031dca9"}, ] [[package]] @@ -3126,4 +3126,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "32061c19f17a8ee5ae1b7c41a5de6401045fae519df88d41fb19e082be005763" +content-hash = "db7a5a114c4c2503f6c3251ac229dcb7edda47f99dde22d7e802c6f45d69690b" diff --git a/pyproject.toml b/pyproject.toml index e96c0140ad..1b4f203a5f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,7 +73,7 @@ pytest-asyncio = "0.25.3" uvicorn = ">=0.32,<0.35" fastapi = "^0.115.5" pytest-httpx = ">=0.33,<0.35" -ruff = "0.9.3" +ruff = "0.9.6" [tool.poetry.group.docs.dependencies] mkdocs = "^1.5.2" From 267caf1205b2794f88d693f55dd08af2b5c18a15 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Feb 2025 22:59:55 +0000 Subject: [PATCH 66/79] Bump pyzmq from 26.2.0 to 26.2.1 Bumps [pyzmq](https://github.com/zeromq/pyzmq) from 26.2.0 to 26.2.1. - [Release notes](https://github.com/zeromq/pyzmq/releases) - [Commits](https://github.com/zeromq/pyzmq/compare/v26.2.0...v26.2.1) --- updated-dependencies: - dependency-name: pyzmq dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> --- poetry.lock | 220 ++++++++++++++++++++++++++-------------------------- 1 file changed, 110 insertions(+), 110 deletions(-) diff --git a/poetry.lock b/poetry.lock index fecc235a29..1e4937ce27 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2173,120 +2173,120 @@ pyyaml = "*" [[package]] name = "pyzmq" -version = "26.2.0" +version = "26.2.1" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.7" files = [ - {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:ddf33d97d2f52d89f6e6e7ae66ee35a4d9ca6f36eda89c24591b0c40205a3629"}, - {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dacd995031a01d16eec825bf30802fceb2c3791ef24bcce48fa98ce40918c27b"}, - {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89289a5ee32ef6c439086184529ae060c741334b8970a6855ec0b6ad3ff28764"}, - {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5506f06d7dc6ecf1efacb4a013b1f05071bb24b76350832c96449f4a2d95091c"}, - {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ea039387c10202ce304af74def5021e9adc6297067f3441d348d2b633e8166a"}, - {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a2224fa4a4c2ee872886ed00a571f5e967c85e078e8e8c2530a2fb01b3309b88"}, - {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:28ad5233e9c3b52d76196c696e362508959741e1a005fb8fa03b51aea156088f"}, - {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1c17211bc037c7d88e85ed8b7d8f7e52db6dc8eca5590d162717c654550f7282"}, - {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b8f86dd868d41bea9a5f873ee13bf5551c94cf6bc51baebc6f85075971fe6eea"}, - {file = "pyzmq-26.2.0-cp310-cp310-win32.whl", hash = "sha256:46a446c212e58456b23af260f3d9fb785054f3e3653dbf7279d8f2b5546b21c2"}, - {file = "pyzmq-26.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:49d34ab71db5a9c292a7644ce74190b1dd5a3475612eefb1f8be1d6961441971"}, - {file = "pyzmq-26.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:bfa832bfa540e5b5c27dcf5de5d82ebc431b82c453a43d141afb1e5d2de025fa"}, - {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:8f7e66c7113c684c2b3f1c83cdd3376103ee0ce4c49ff80a648643e57fb22218"}, - {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3a495b30fc91db2db25120df5847d9833af237546fd59170701acd816ccc01c4"}, - {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77eb0968da535cba0470a5165468b2cac7772cfb569977cff92e240f57e31bef"}, - {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ace4f71f1900a548f48407fc9be59c6ba9d9aaf658c2eea6cf2779e72f9f317"}, - {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92a78853d7280bffb93df0a4a6a2498cba10ee793cc8076ef797ef2f74d107cf"}, - {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:689c5d781014956a4a6de61d74ba97b23547e431e9e7d64f27d4922ba96e9d6e"}, - {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0aca98bc423eb7d153214b2df397c6421ba6373d3397b26c057af3c904452e37"}, - {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f3496d76b89d9429a656293744ceca4d2ac2a10ae59b84c1da9b5165f429ad3"}, - {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5c2b3bfd4b9689919db068ac6c9911f3fcb231c39f7dd30e3138be94896d18e6"}, - {file = "pyzmq-26.2.0-cp311-cp311-win32.whl", hash = "sha256:eac5174677da084abf378739dbf4ad245661635f1600edd1221f150b165343f4"}, - {file = "pyzmq-26.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a509df7d0a83a4b178d0f937ef14286659225ef4e8812e05580776c70e155d5"}, - {file = "pyzmq-26.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0e6091b157d48cbe37bd67233318dbb53e1e6327d6fc3bb284afd585d141003"}, - {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:ded0fc7d90fe93ae0b18059930086c51e640cdd3baebdc783a695c77f123dcd9"}, - {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:17bf5a931c7f6618023cdacc7081f3f266aecb68ca692adac015c383a134ca52"}, - {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55cf66647e49d4621a7e20c8d13511ef1fe1efbbccf670811864452487007e08"}, - {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4661c88db4a9e0f958c8abc2b97472e23061f0bc737f6f6179d7a27024e1faa5"}, - {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea7f69de383cb47522c9c208aec6dd17697db7875a4674c4af3f8cfdac0bdeae"}, - {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7f98f6dfa8b8ccaf39163ce872bddacca38f6a67289116c8937a02e30bbe9711"}, - {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e3e0210287329272539eea617830a6a28161fbbd8a3271bf4150ae3e58c5d0e6"}, - {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6b274e0762c33c7471f1a7471d1a2085b1a35eba5cdc48d2ae319f28b6fc4de3"}, - {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:29c6a4635eef69d68a00321e12a7d2559fe2dfccfa8efae3ffb8e91cd0b36a8b"}, - {file = "pyzmq-26.2.0-cp312-cp312-win32.whl", hash = "sha256:989d842dc06dc59feea09e58c74ca3e1678c812a4a8a2a419046d711031f69c7"}, - {file = "pyzmq-26.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:2a50625acdc7801bc6f74698c5c583a491c61d73c6b7ea4dee3901bb99adb27a"}, - {file = "pyzmq-26.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:4d29ab8592b6ad12ebbf92ac2ed2bedcfd1cec192d8e559e2e099f648570e19b"}, - {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9dd8cd1aeb00775f527ec60022004d030ddc51d783d056e3e23e74e623e33726"}, - {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:28c812d9757fe8acecc910c9ac9dafd2ce968c00f9e619db09e9f8f54c3a68a3"}, - {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d80b1dd99c1942f74ed608ddb38b181b87476c6a966a88a950c7dee118fdf50"}, - {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c997098cc65e3208eca09303630e84d42718620e83b733d0fd69543a9cab9cb"}, - {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ad1bc8d1b7a18497dda9600b12dc193c577beb391beae5cd2349184db40f187"}, - {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:bea2acdd8ea4275e1278350ced63da0b166421928276c7c8e3f9729d7402a57b"}, - {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:23f4aad749d13698f3f7b64aad34f5fc02d6f20f05999eebc96b89b01262fb18"}, - {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a4f96f0d88accc3dbe4a9025f785ba830f968e21e3e2c6321ccdfc9aef755115"}, - {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ced65e5a985398827cc9276b93ef6dfabe0273c23de8c7931339d7e141c2818e"}, - {file = "pyzmq-26.2.0-cp313-cp313-win32.whl", hash = "sha256:31507f7b47cc1ead1f6e86927f8ebb196a0bab043f6345ce070f412a59bf87b5"}, - {file = "pyzmq-26.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:70fc7fcf0410d16ebdda9b26cbd8bf8d803d220a7f3522e060a69a9c87bf7bad"}, - {file = "pyzmq-26.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:c3789bd5768ab5618ebf09cef6ec2b35fed88709b104351748a63045f0ff9797"}, - {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:034da5fc55d9f8da09015d368f519478a52675e558c989bfcb5cf6d4e16a7d2a"}, - {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:c92d73464b886931308ccc45b2744e5968cbaade0b1d6aeb40d8ab537765f5bc"}, - {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:794a4562dcb374f7dbbfb3f51d28fb40123b5a2abadee7b4091f93054909add5"}, - {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aee22939bb6075e7afededabad1a56a905da0b3c4e3e0c45e75810ebe3a52672"}, - {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ae90ff9dad33a1cfe947d2c40cb9cb5e600d759ac4f0fd22616ce6540f72797"}, - {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:43a47408ac52647dfabbc66a25b05b6a61700b5165807e3fbd40063fcaf46386"}, - {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:25bf2374a2a8433633c65ccb9553350d5e17e60c8eb4de4d92cc6bd60f01d306"}, - {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:007137c9ac9ad5ea21e6ad97d3489af654381324d5d3ba614c323f60dab8fae6"}, - {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:470d4a4f6d48fb34e92d768b4e8a5cc3780db0d69107abf1cd7ff734b9766eb0"}, - {file = "pyzmq-26.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3b55a4229ce5da9497dd0452b914556ae58e96a4381bb6f59f1305dfd7e53fc8"}, - {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9cb3a6460cdea8fe8194a76de8895707e61ded10ad0be97188cc8463ffa7e3a8"}, - {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8ab5cad923cc95c87bffee098a27856c859bd5d0af31bd346035aa816b081fe1"}, - {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ed69074a610fad1c2fda66180e7b2edd4d31c53f2d1872bc2d1211563904cd9"}, - {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cccba051221b916a4f5e538997c45d7d136a5646442b1231b916d0164067ea27"}, - {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:0eaa83fc4c1e271c24eaf8fb083cbccef8fde77ec8cd45f3c35a9a123e6da097"}, - {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9edda2df81daa129b25a39b86cb57dfdfe16f7ec15b42b19bfac503360d27a93"}, - {file = "pyzmq-26.2.0-cp37-cp37m-win32.whl", hash = "sha256:ea0eb6af8a17fa272f7b98d7bebfab7836a0d62738e16ba380f440fceca2d951"}, - {file = "pyzmq-26.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4ff9dc6bc1664bb9eec25cd17506ef6672d506115095411e237d571e92a58231"}, - {file = "pyzmq-26.2.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2eb7735ee73ca1b0d71e0e67c3739c689067f055c764f73aac4cc8ecf958ee3f"}, - {file = "pyzmq-26.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a534f43bc738181aa7cbbaf48e3eca62c76453a40a746ab95d4b27b1111a7d2"}, - {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:aedd5dd8692635813368e558a05266b995d3d020b23e49581ddd5bbe197a8ab6"}, - {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8be4700cd8bb02cc454f630dcdf7cfa99de96788b80c51b60fe2fe1dac480289"}, - {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fcc03fa4997c447dce58264e93b5aa2d57714fbe0f06c07b7785ae131512732"}, - {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:402b190912935d3db15b03e8f7485812db350d271b284ded2b80d2e5704be780"}, - {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8685fa9c25ff00f550c1fec650430c4b71e4e48e8d852f7ddcf2e48308038640"}, - {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:76589c020680778f06b7e0b193f4b6dd66d470234a16e1df90329f5e14a171cd"}, - {file = "pyzmq-26.2.0-cp38-cp38-win32.whl", hash = "sha256:8423c1877d72c041f2c263b1ec6e34360448decfb323fa8b94e85883043ef988"}, - {file = "pyzmq-26.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:76589f2cd6b77b5bdea4fca5992dc1c23389d68b18ccc26a53680ba2dc80ff2f"}, - {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:b1d464cb8d72bfc1a3adc53305a63a8e0cac6bc8c5a07e8ca190ab8d3faa43c2"}, - {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4da04c48873a6abdd71811c5e163bd656ee1b957971db7f35140a2d573f6949c"}, - {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d049df610ac811dcffdc147153b414147428567fbbc8be43bb8885f04db39d98"}, - {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05590cdbc6b902101d0e65d6a4780af14dc22914cc6ab995d99b85af45362cc9"}, - {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c811cfcd6a9bf680236c40c6f617187515269ab2912f3d7e8c0174898e2519db"}, - {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6835dd60355593de10350394242b5757fbbd88b25287314316f266e24c61d073"}, - {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc6bee759a6bddea5db78d7dcd609397449cb2d2d6587f48f3ca613b19410cfc"}, - {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c530e1eecd036ecc83c3407f77bb86feb79916d4a33d11394b8234f3bd35b940"}, - {file = "pyzmq-26.2.0-cp39-cp39-win32.whl", hash = "sha256:367b4f689786fca726ef7a6c5ba606958b145b9340a5e4808132cc65759abd44"}, - {file = "pyzmq-26.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:e6fa2e3e683f34aea77de8112f6483803c96a44fd726d7358b9888ae5bb394ec"}, - {file = "pyzmq-26.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:7445be39143a8aa4faec43b076e06944b8f9d0701b669df4af200531b21e40bb"}, - {file = "pyzmq-26.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:706e794564bec25819d21a41c31d4df2d48e1cc4b061e8d345d7fb4dd3e94072"}, - {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b435f2753621cd36e7c1762156815e21c985c72b19135dac43a7f4f31d28dd1"}, - {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:160c7e0a5eb178011e72892f99f918c04a131f36056d10d9c1afb223fc952c2d"}, - {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4a71d5d6e7b28a47a394c0471b7e77a0661e2d651e7ae91e0cab0a587859ca"}, - {file = "pyzmq-26.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:90412f2db8c02a3864cbfc67db0e3dcdbda336acf1c469526d3e869394fe001c"}, - {file = "pyzmq-26.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2ea4ad4e6a12e454de05f2949d4beddb52460f3de7c8b9d5c46fbb7d7222e02c"}, - {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fc4f7a173a5609631bb0c42c23d12c49df3966f89f496a51d3eb0ec81f4519d6"}, - {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:878206a45202247781472a2d99df12a176fef806ca175799e1c6ad263510d57c"}, - {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17c412bad2eb9468e876f556eb4ee910e62d721d2c7a53c7fa31e643d35352e6"}, - {file = "pyzmq-26.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:0d987a3ae5a71c6226b203cfd298720e0086c7fe7c74f35fa8edddfbd6597eed"}, - {file = "pyzmq-26.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:39887ac397ff35b7b775db7201095fc6310a35fdbae85bac4523f7eb3b840e20"}, - {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fdb5b3e311d4d4b0eb8b3e8b4d1b0a512713ad7e6a68791d0923d1aec433d919"}, - {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:226af7dcb51fdb0109f0016449b357e182ea0ceb6b47dfb5999d569e5db161d5"}, - {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bed0e799e6120b9c32756203fb9dfe8ca2fb8467fed830c34c877e25638c3fc"}, - {file = "pyzmq-26.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:29c7947c594e105cb9e6c466bace8532dc1ca02d498684128b339799f5248277"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cdeabcff45d1c219636ee2e54d852262e5c2e085d6cb476d938aee8d921356b3"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35cffef589bcdc587d06f9149f8d5e9e8859920a071df5a2671de2213bef592a"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18c8dc3b7468d8b4bdf60ce9d7141897da103c7a4690157b32b60acb45e333e6"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7133d0a1677aec369d67dd78520d3fa96dd7f3dcec99d66c1762870e5ea1a50a"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6a96179a24b14fa6428cbfc08641c779a53f8fcec43644030328f44034c7f1f4"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4f78c88905461a9203eac9faac157a2a0dbba84a0fd09fd29315db27be40af9f"}, - {file = "pyzmq-26.2.0.tar.gz", hash = "sha256:070672c258581c8e4f640b5159297580a9974b026043bd4ab0470be9ed324f1f"}, + {file = "pyzmq-26.2.1-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:f39d1227e8256d19899d953e6e19ed2ccb689102e6d85e024da5acf410f301eb"}, + {file = "pyzmq-26.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a23948554c692df95daed595fdd3b76b420a4939d7a8a28d6d7dea9711878641"}, + {file = "pyzmq-26.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95f5728b367a042df146cec4340d75359ec6237beebf4a8f5cf74657c65b9257"}, + {file = "pyzmq-26.2.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95f7b01b3f275504011cf4cf21c6b885c8d627ce0867a7e83af1382ebab7b3ff"}, + {file = "pyzmq-26.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80a00370a2ef2159c310e662c7c0f2d030f437f35f478bb8b2f70abd07e26b24"}, + {file = "pyzmq-26.2.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:8531ed35dfd1dd2af95f5d02afd6545e8650eedbf8c3d244a554cf47d8924459"}, + {file = "pyzmq-26.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cdb69710e462a38e6039cf17259d328f86383a06c20482cc154327968712273c"}, + {file = "pyzmq-26.2.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e7eeaef81530d0b74ad0d29eec9997f1c9230c2f27242b8d17e0ee67662c8f6e"}, + {file = "pyzmq-26.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:361edfa350e3be1f987e592e834594422338d7174364763b7d3de5b0995b16f3"}, + {file = "pyzmq-26.2.1-cp310-cp310-win32.whl", hash = "sha256:637536c07d2fb6a354988b2dd1d00d02eb5dd443f4bbee021ba30881af1c28aa"}, + {file = "pyzmq-26.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:45fad32448fd214fbe60030aa92f97e64a7140b624290834cc9b27b3a11f9473"}, + {file = "pyzmq-26.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:d9da0289d8201c8a29fd158aaa0dfe2f2e14a181fd45e2dc1fbf969a62c1d594"}, + {file = "pyzmq-26.2.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:c059883840e634a21c5b31d9b9a0e2b48f991b94d60a811092bc37992715146a"}, + {file = "pyzmq-26.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed038a921df836d2f538e509a59cb638df3e70ca0fcd70d0bf389dfcdf784d2a"}, + {file = "pyzmq-26.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9027a7fcf690f1a3635dc9e55e38a0d6602dbbc0548935d08d46d2e7ec91f454"}, + {file = "pyzmq-26.2.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d75fcb00a1537f8b0c0bb05322bc7e35966148ffc3e0362f0369e44a4a1de99"}, + {file = "pyzmq-26.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0019cc804ac667fb8c8eaecdb66e6d4a68acf2e155d5c7d6381a5645bd93ae4"}, + {file = "pyzmq-26.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:f19dae58b616ac56b96f2e2290f2d18730a898a171f447f491cc059b073ca1fa"}, + {file = "pyzmq-26.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f5eeeb82feec1fc5cbafa5ee9022e87ffdb3a8c48afa035b356fcd20fc7f533f"}, + {file = "pyzmq-26.2.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:000760e374d6f9d1a3478a42ed0c98604de68c9e94507e5452951e598ebecfba"}, + {file = "pyzmq-26.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:817fcd3344d2a0b28622722b98500ae9c8bfee0f825b8450932ff19c0b15bebd"}, + {file = "pyzmq-26.2.1-cp311-cp311-win32.whl", hash = "sha256:88812b3b257f80444a986b3596e5ea5c4d4ed4276d2b85c153a6fbc5ca457ae7"}, + {file = "pyzmq-26.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:ef29630fde6022471d287c15c0a2484aba188adbfb978702624ba7a54ddfa6c1"}, + {file = "pyzmq-26.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:f32718ee37c07932cc336096dc7403525301fd626349b6eff8470fe0f996d8d7"}, + {file = "pyzmq-26.2.1-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:a6549ecb0041dafa55b5932dcbb6c68293e0bd5980b5b99f5ebb05f9a3b8a8f3"}, + {file = "pyzmq-26.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0250c94561f388db51fd0213cdccbd0b9ef50fd3c57ce1ac937bf3034d92d72e"}, + {file = "pyzmq-26.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36ee4297d9e4b34b5dc1dd7ab5d5ea2cbba8511517ef44104d2915a917a56dc8"}, + {file = "pyzmq-26.2.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2a9cb17fd83b7a3a3009901aca828feaf20aa2451a8a487b035455a86549c09"}, + {file = "pyzmq-26.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:786dd8a81b969c2081b31b17b326d3a499ddd1856e06d6d79ad41011a25148da"}, + {file = "pyzmq-26.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:2d88ba221a07fc2c5581565f1d0fe8038c15711ae79b80d9462e080a1ac30435"}, + {file = "pyzmq-26.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1c84c1297ff9f1cd2440da4d57237cb74be21fdfe7d01a10810acba04e79371a"}, + {file = "pyzmq-26.2.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:46d4ebafc27081a7f73a0f151d0c38d4291656aa134344ec1f3d0199ebfbb6d4"}, + {file = "pyzmq-26.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:91e2bfb8e9a29f709d51b208dd5f441dc98eb412c8fe75c24ea464734ccdb48e"}, + {file = "pyzmq-26.2.1-cp312-cp312-win32.whl", hash = "sha256:4a98898fdce380c51cc3e38ebc9aa33ae1e078193f4dc641c047f88b8c690c9a"}, + {file = "pyzmq-26.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:a0741edbd0adfe5f30bba6c5223b78c131b5aa4a00a223d631e5ef36e26e6d13"}, + {file = "pyzmq-26.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:e5e33b1491555843ba98d5209439500556ef55b6ab635f3a01148545498355e5"}, + {file = "pyzmq-26.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:099b56ef464bc355b14381f13355542e452619abb4c1e57a534b15a106bf8e23"}, + {file = "pyzmq-26.2.1-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:651726f37fcbce9f8dd2a6dab0f024807929780621890a4dc0c75432636871be"}, + {file = "pyzmq-26.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57dd4d91b38fa4348e237a9388b4423b24ce9c1695bbd4ba5a3eada491e09399"}, + {file = "pyzmq-26.2.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d51a7bfe01a48e1064131f3416a5439872c533d756396be2b39e3977b41430f9"}, + {file = "pyzmq-26.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7154d228502e18f30f150b7ce94f0789d6b689f75261b623f0fdc1eec642aab"}, + {file = "pyzmq-26.2.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:f1f31661a80cc46aba381bed475a9135b213ba23ca7ff6797251af31510920ce"}, + {file = "pyzmq-26.2.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:290c96f479504439b6129a94cefd67a174b68ace8a8e3f551b2239a64cfa131a"}, + {file = "pyzmq-26.2.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:f2c307fbe86e18ab3c885b7e01de942145f539165c3360e2af0f094dd440acd9"}, + {file = "pyzmq-26.2.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:b314268e716487bfb86fcd6f84ebbe3e5bec5fac75fdf42bc7d90fdb33f618ad"}, + {file = "pyzmq-26.2.1-cp313-cp313-win32.whl", hash = "sha256:edb550616f567cd5603b53bb52a5f842c0171b78852e6fc7e392b02c2a1504bb"}, + {file = "pyzmq-26.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:100a826a029c8ef3d77a1d4c97cbd6e867057b5806a7276f2bac1179f893d3bf"}, + {file = "pyzmq-26.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:6991ee6c43e0480deb1b45d0c7c2bac124a6540cba7db4c36345e8e092da47ce"}, + {file = "pyzmq-26.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:25e720dba5b3a3bb2ad0ad5d33440babd1b03438a7a5220511d0c8fa677e102e"}, + {file = "pyzmq-26.2.1-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:9ec6abfb701437142ce9544bd6a236addaf803a32628d2260eb3dbd9a60e2891"}, + {file = "pyzmq-26.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e1eb9d2bfdf5b4e21165b553a81b2c3bd5be06eeddcc4e08e9692156d21f1f6"}, + {file = "pyzmq-26.2.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:90dc731d8e3e91bcd456aa7407d2eba7ac6f7860e89f3766baabb521f2c1de4a"}, + {file = "pyzmq-26.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b6a93d684278ad865fc0b9e89fe33f6ea72d36da0e842143891278ff7fd89c3"}, + {file = "pyzmq-26.2.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:c1bb37849e2294d519117dd99b613c5177934e5c04a5bb05dd573fa42026567e"}, + {file = "pyzmq-26.2.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:632a09c6d8af17b678d84df442e9c3ad8e4949c109e48a72f805b22506c4afa7"}, + {file = "pyzmq-26.2.1-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:fc409c18884eaf9ddde516d53af4f2db64a8bc7d81b1a0c274b8aa4e929958e8"}, + {file = "pyzmq-26.2.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:17f88622b848805d3f6427ce1ad5a2aa3cf61f12a97e684dab2979802024d460"}, + {file = "pyzmq-26.2.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3ef584f13820d2629326fe20cc04069c21c5557d84c26e277cfa6235e523b10f"}, + {file = "pyzmq-26.2.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:160194d1034902937359c26ccfa4e276abffc94937e73add99d9471e9f555dd6"}, + {file = "pyzmq-26.2.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:574b285150afdbf0a0424dddf7ef9a0d183988eb8d22feacb7160f7515e032cb"}, + {file = "pyzmq-26.2.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44dba28c34ce527cf687156c81f82bf1e51f047838d5964f6840fd87dfecf9fe"}, + {file = "pyzmq-26.2.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9fbdb90b85c7624c304f72ec7854659a3bd901e1c0ffb2363163779181edeb68"}, + {file = "pyzmq-26.2.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a7ad34a2921e8f76716dc7205c9bf46a53817e22b9eec2e8a3e08ee4f4a72468"}, + {file = "pyzmq-26.2.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:866c12b7c90dd3a86983df7855c6f12f9407c8684db6aa3890fc8027462bda82"}, + {file = "pyzmq-26.2.1-cp37-cp37m-win32.whl", hash = "sha256:eeb37f65350d5c5870517f02f8bbb2ac0fbec7b416c0f4875219fef305a89a45"}, + {file = "pyzmq-26.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4eb3197f694dfb0ee6af29ef14a35f30ae94ff67c02076eef8125e2d98963cd0"}, + {file = "pyzmq-26.2.1-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:36d4e7307db7c847fe37413f333027d31c11d5e6b3bacbb5022661ac635942ba"}, + {file = "pyzmq-26.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1c6ae0e95d0a4b0cfe30f648a18e764352d5415279bdf34424decb33e79935b8"}, + {file = "pyzmq-26.2.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5b4fc44f5360784cc02392f14235049665caaf7c0fe0b04d313e763d3338e463"}, + {file = "pyzmq-26.2.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:51431f6b2750eb9b9d2b2952d3cc9b15d0215e1b8f37b7a3239744d9b487325d"}, + {file = "pyzmq-26.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdbc78ae2065042de48a65f1421b8af6b76a0386bb487b41955818c3c1ce7bed"}, + {file = "pyzmq-26.2.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d14f50d61a89b0925e4d97a0beba6053eb98c426c5815d949a43544f05a0c7ec"}, + {file = "pyzmq-26.2.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:004837cb958988c75d8042f5dac19a881f3d9b3b75b2f574055e22573745f841"}, + {file = "pyzmq-26.2.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0b2007f28ce1b8acebdf4812c1aab997a22e57d6a73b5f318b708ef9bcabbe95"}, + {file = "pyzmq-26.2.1-cp38-cp38-win32.whl", hash = "sha256:269c14904da971cb5f013100d1aaedb27c0a246728c341d5d61ddd03f463f2f3"}, + {file = "pyzmq-26.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:31fff709fef3b991cfe7189d2cfe0c413a1d0e82800a182cfa0c2e3668cd450f"}, + {file = "pyzmq-26.2.1-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:a4bffcadfd40660f26d1b3315a6029fd4f8f5bf31a74160b151f5c577b2dc81b"}, + {file = "pyzmq-26.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e76ad4729c2f1cf74b6eb1bdd05f6aba6175999340bd51e6caee49a435a13bf5"}, + {file = "pyzmq-26.2.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8b0f5bab40a16e708e78a0c6ee2425d27e1a5d8135c7a203b4e977cee37eb4aa"}, + {file = "pyzmq-26.2.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e8e47050412f0ad3a9b2287779758073cbf10e460d9f345002d4779e43bb0136"}, + {file = "pyzmq-26.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f18ce33f422d119b13c1363ed4cce245b342b2c5cbbb76753eabf6aa6f69c7d"}, + {file = "pyzmq-26.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ceb0d78b7ef106708a7e2c2914afe68efffc0051dc6a731b0dbacd8b4aee6d68"}, + {file = "pyzmq-26.2.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ebdd96bd637fd426d60e86a29ec14b8c1ab64b8d972f6a020baf08a30d1cf46"}, + {file = "pyzmq-26.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:03719e424150c6395b9513f53a5faadcc1ce4b92abdf68987f55900462ac7eec"}, + {file = "pyzmq-26.2.1-cp39-cp39-win32.whl", hash = "sha256:ef5479fac31df4b304e96400fc67ff08231873ee3537544aa08c30f9d22fce38"}, + {file = "pyzmq-26.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:f92a002462154c176dac63a8f1f6582ab56eb394ef4914d65a9417f5d9fde218"}, + {file = "pyzmq-26.2.1-cp39-cp39-win_arm64.whl", hash = "sha256:1fd4b3efc6f62199886440d5e27dd3ccbcb98dfddf330e7396f1ff421bfbb3c2"}, + {file = "pyzmq-26.2.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:380816d298aed32b1a97b4973a4865ef3be402a2e760204509b52b6de79d755d"}, + {file = "pyzmq-26.2.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97cbb368fd0debdbeb6ba5966aa28e9a1ae3396c7386d15569a6ca4be4572b99"}, + {file = "pyzmq-26.2.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf7b5942c6b0dafcc2823ddd9154f419147e24f8df5b41ca8ea40a6db90615c"}, + {file = "pyzmq-26.2.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fe6e28a8856aea808715f7a4fc11f682b9d29cac5d6262dd8fe4f98edc12d53"}, + {file = "pyzmq-26.2.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bd8fdee945b877aa3bffc6a5a8816deb048dab0544f9df3731ecd0e54d8c84c9"}, + {file = "pyzmq-26.2.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ee7152f32c88e0e1b5b17beb9f0e2b14454235795ef68c0c120b6d3d23d12833"}, + {file = "pyzmq-26.2.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:baa1da72aecf6a490b51fba7a51f1ce298a1e0e86d0daef8265c8f8f9848eb77"}, + {file = "pyzmq-26.2.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:49135bb327fca159262d8fd14aa1f4a919fe071b04ed08db4c7c37d2f0647162"}, + {file = "pyzmq-26.2.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8bacc1a10c150d58e8a9ee2b2037a70f8d903107e0f0b6e079bf494f2d09c091"}, + {file = "pyzmq-26.2.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:09dac387ce62d69bec3f06d51610ca1d660e7849eb45f68e38e7f5cf1f49cbcb"}, + {file = "pyzmq-26.2.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:70b3a46ecd9296e725ccafc17d732bfc3cdab850b54bd913f843a0a54dfb2c04"}, + {file = "pyzmq-26.2.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:59660e15c797a3b7a571c39f8e0b62a1f385f98ae277dfe95ca7eaf05b5a0f12"}, + {file = "pyzmq-26.2.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0f50db737d688e96ad2a083ad2b453e22865e7e19c7f17d17df416e91ddf67eb"}, + {file = "pyzmq-26.2.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a003200b6cd64e89b5725ff7e284a93ab24fd54bbac8b4fa46b1ed57be693c27"}, + {file = "pyzmq-26.2.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:f9ba5def063243793dec6603ad1392f735255cbc7202a3a484c14f99ec290705"}, + {file = "pyzmq-26.2.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1238c2448c58b9c8d6565579393148414a42488a5f916b3f322742e561f6ae0d"}, + {file = "pyzmq-26.2.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8eddb3784aed95d07065bcf94d07e8c04024fdb6b2386f08c197dfe6b3528fda"}, + {file = "pyzmq-26.2.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0f19c2097fffb1d5b07893d75c9ee693e9cbc809235cf3f2267f0ef6b015f24"}, + {file = "pyzmq-26.2.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0995fd3530f2e89d6b69a2202e340bbada3191014352af978fa795cb7a446331"}, + {file = "pyzmq-26.2.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:7c6160fe513654e65665332740f63de29ce0d165e053c0c14a161fa60dd0da01"}, + {file = "pyzmq-26.2.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8ec8e3aea6146b761d6c57fcf8f81fcb19f187afecc19bf1701a48db9617a217"}, + {file = "pyzmq-26.2.1.tar.gz", hash = "sha256:17d72a74e5e9ff3829deb72897a175333d3ef5b5413948cae3cf7ebf0b02ecca"}, ] [package.dependencies] From 1c5fc995b2c8f48ece2a0f7f4e65f40488ce7fd1 Mon Sep 17 00:00:00 2001 From: blsaccess <info@blacklanternsecurity.com> Date: Fri, 14 Feb 2025 00:22:47 +0000 Subject: [PATCH 67/79] Update nuclei --- bbot/modules/deadly/nuclei.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/modules/deadly/nuclei.py b/bbot/modules/deadly/nuclei.py index d5370fff85..b973c714bc 100644 --- a/bbot/modules/deadly/nuclei.py +++ b/bbot/modules/deadly/nuclei.py @@ -15,7 +15,7 @@ class nuclei(BaseModule): } options = { - "version": "3.3.8", + "version": "3.3.9", "tags": "", "templates": "", "severity": "", From 41fbc0babfe7776980e2c9b06522bcdc4605bed2 Mon Sep 17 00:00:00 2001 From: blsaccess <info@blacklanternsecurity.com> Date: Sat, 15 Feb 2025 00:22:33 +0000 Subject: [PATCH 68/79] Update trufflehog --- bbot/modules/trufflehog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/modules/trufflehog.py b/bbot/modules/trufflehog.py index c992285637..c16162d79a 100644 --- a/bbot/modules/trufflehog.py +++ b/bbot/modules/trufflehog.py @@ -13,7 +13,7 @@ class trufflehog(BaseModule): } options = { - "version": "3.88.4", + "version": "3.88.9", "config": "", "only_verified": True, "concurrency": 8, From 63ad4cba3e2eb6f57fba411211fe47d73af89f0c Mon Sep 17 00:00:00 2001 From: Dom Whewell <dom.whewell@sage.com> Date: Sat, 15 Feb 2025 20:27:06 +0000 Subject: [PATCH 69/79] Change trufflehog status from info to verbose --- bbot/modules/trufflehog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/modules/trufflehog.py b/bbot/modules/trufflehog.py index c992285637..76dc6e97a7 100644 --- a/bbot/modules/trufflehog.py +++ b/bbot/modules/trufflehog.py @@ -209,4 +209,4 @@ def log_trufflehog_status(self, line): message = line.get("msg", "") ts = line.get("ts", "") status = f"Message: {message} | Timestamp: {ts}" - self.info(status) + self.verbose(status) From 666776a2b2cb41336d08a52ddeab285258cec9bf Mon Sep 17 00:00:00 2001 From: Dom Whewell <dom.whewell@sage.com> Date: Sat, 15 Feb 2025 20:58:35 +0000 Subject: [PATCH 70/79] Add the current target to the trufflehog status --- bbot/modules/trufflehog.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bbot/modules/trufflehog.py b/bbot/modules/trufflehog.py index 76dc6e97a7..eb77935cee 100644 --- a/bbot/modules/trufflehog.py +++ b/bbot/modules/trufflehog.py @@ -1,4 +1,5 @@ import json +from functools import partial from bbot.modules.base import BaseModule @@ -174,7 +175,7 @@ async def execute_trufflehog(self, module, path=None, string=None): command.append("--delete-cached-data") command.append("--token=" + self.github_token) - stats_file = self.helpers.tempfile_tail(callback=self.log_trufflehog_status) + stats_file = self.helpers.tempfile_tail(callback=partial(self.log_trufflehog_status, path)) try: with open(stats_file, "w") as stats_fh: async for line in self.helpers.run_live(command, stderr=stats_fh): @@ -200,7 +201,7 @@ async def execute_trufflehog(self, module, path=None, string=None): finally: stats_file.unlink() - def log_trufflehog_status(self, line): + def log_trufflehog_status(self, path, line): try: line = json.loads(line) except Exception: @@ -209,4 +210,5 @@ def log_trufflehog_status(self, line): message = line.get("msg", "") ts = line.get("ts", "") status = f"Message: {message} | Timestamp: {ts}" + self.verbose(f"Current scan target: {path}") self.verbose(status) From 90e3e863f06c922975feaef5faf624b933caf267 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 05:03:39 +0000 Subject: [PATCH 71/79] Bump lxml from 5.3.0 to 5.3.1 Bumps [lxml](https://github.com/lxml/lxml) from 5.3.0 to 5.3.1. - [Release notes](https://github.com/lxml/lxml/releases) - [Changelog](https://github.com/lxml/lxml/blob/master/CHANGES.txt) - [Commits](https://github.com/lxml/lxml/compare/lxml-5.3.0...lxml-5.3.1) --- updated-dependencies: - dependency-name: lxml dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> --- poetry.lock | 282 ++++++++++++++++++++++++++-------------------------- 1 file changed, 141 insertions(+), 141 deletions(-) diff --git a/poetry.lock b/poetry.lock index a14e8b2b83..344fd0ccaf 100644 --- a/poetry.lock +++ b/poetry.lock @@ -843,157 +843,157 @@ files = [ [[package]] name = "lxml" -version = "5.3.0" +version = "5.3.1" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false python-versions = ">=3.6" files = [ - {file = "lxml-5.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dd36439be765e2dde7660212b5275641edbc813e7b24668831a5c8ac91180656"}, - {file = "lxml-5.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ae5fe5c4b525aa82b8076c1a59d642c17b6e8739ecf852522c6321852178119d"}, - {file = "lxml-5.3.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:501d0d7e26b4d261fca8132854d845e4988097611ba2531408ec91cf3fd9d20a"}, - {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb66442c2546446944437df74379e9cf9e9db353e61301d1a0e26482f43f0dd8"}, - {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e41506fec7a7f9405b14aa2d5c8abbb4dbbd09d88f9496958b6d00cb4d45330"}, - {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f7d4a670107d75dfe5ad080bed6c341d18c4442f9378c9f58e5851e86eb79965"}, - {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41ce1f1e2c7755abfc7e759dc34d7d05fd221723ff822947132dc934d122fe22"}, - {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:44264ecae91b30e5633013fb66f6ddd05c006d3e0e884f75ce0b4755b3e3847b"}, - {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:3c174dc350d3ec52deb77f2faf05c439331d6ed5e702fc247ccb4e6b62d884b7"}, - {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:2dfab5fa6a28a0b60a20638dc48e6343c02ea9933e3279ccb132f555a62323d8"}, - {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b1c8c20847b9f34e98080da785bb2336ea982e7f913eed5809e5a3c872900f32"}, - {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2c86bf781b12ba417f64f3422cfc302523ac9cd1d8ae8c0f92a1c66e56ef2e86"}, - {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c162b216070f280fa7da844531169be0baf9ccb17263cf5a8bf876fcd3117fa5"}, - {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:36aef61a1678cb778097b4a6eeae96a69875d51d1e8f4d4b491ab3cfb54b5a03"}, - {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f65e5120863c2b266dbcc927b306c5b78e502c71edf3295dfcb9501ec96e5fc7"}, - {file = "lxml-5.3.0-cp310-cp310-win32.whl", hash = "sha256:ef0c1fe22171dd7c7c27147f2e9c3e86f8bdf473fed75f16b0c2e84a5030ce80"}, - {file = "lxml-5.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:052d99051e77a4f3e8482c65014cf6372e61b0a6f4fe9edb98503bb5364cfee3"}, - {file = "lxml-5.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74bcb423462233bc5d6066e4e98b0264e7c1bed7541fff2f4e34fe6b21563c8b"}, - {file = "lxml-5.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a3d819eb6f9b8677f57f9664265d0a10dd6551d227afb4af2b9cd7bdc2ccbf18"}, - {file = "lxml-5.3.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b8f5db71b28b8c404956ddf79575ea77aa8b1538e8b2ef9ec877945b3f46442"}, - {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3406b63232fc7e9b8783ab0b765d7c59e7c59ff96759d8ef9632fca27c7ee4"}, - {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ecdd78ab768f844c7a1d4a03595038c166b609f6395e25af9b0f3f26ae1230f"}, - {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168f2dfcfdedf611eb285efac1516c8454c8c99caf271dccda8943576b67552e"}, - {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa617107a410245b8660028a7483b68e7914304a6d4882b5ff3d2d3eb5948d8c"}, - {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:69959bd3167b993e6e710b99051265654133a98f20cec1d9b493b931942e9c16"}, - {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:bd96517ef76c8654446fc3db9242d019a1bb5fe8b751ba414765d59f99210b79"}, - {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:ab6dd83b970dc97c2d10bc71aa925b84788c7c05de30241b9e96f9b6d9ea3080"}, - {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eec1bb8cdbba2925bedc887bc0609a80e599c75b12d87ae42ac23fd199445654"}, - {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a7095eeec6f89111d03dabfe5883a1fd54da319c94e0fb104ee8f23616b572d"}, - {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6f651ebd0b21ec65dfca93aa629610a0dbc13dbc13554f19b0113da2e61a4763"}, - {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f422a209d2455c56849442ae42f25dbaaba1c6c3f501d58761c619c7836642ec"}, - {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62f7fdb0d1ed2065451f086519865b4c90aa19aed51081979ecd05a21eb4d1be"}, - {file = "lxml-5.3.0-cp311-cp311-win32.whl", hash = "sha256:c6379f35350b655fd817cd0d6cbeef7f265f3ae5fedb1caae2eb442bbeae9ab9"}, - {file = "lxml-5.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c52100e2c2dbb0649b90467935c4b0de5528833c76a35ea1a2691ec9f1ee7a1"}, - {file = "lxml-5.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e99f5507401436fdcc85036a2e7dc2e28d962550afe1cbfc07c40e454256a859"}, - {file = "lxml-5.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:384aacddf2e5813a36495233b64cb96b1949da72bef933918ba5c84e06af8f0e"}, - {file = "lxml-5.3.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a216bf6afaf97c263b56371434e47e2c652d215788396f60477540298218f"}, - {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65ab5685d56914b9a2a34d67dd5488b83213d680b0c5d10b47f81da5a16b0b0e"}, - {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aac0bbd3e8dd2d9c45ceb82249e8bdd3ac99131a32b4d35c8af3cc9db1657179"}, - {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b369d3db3c22ed14c75ccd5af429086f166a19627e84a8fdade3f8f31426e52a"}, - {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24037349665434f375645fa9d1f5304800cec574d0310f618490c871fd902b3"}, - {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:62d172f358f33a26d6b41b28c170c63886742f5b6772a42b59b4f0fa10526cb1"}, - {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:c1f794c02903c2824fccce5b20c339a1a14b114e83b306ff11b597c5f71a1c8d"}, - {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:5d6a6972b93c426ace71e0be9a6f4b2cfae9b1baed2eed2006076a746692288c"}, - {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:3879cc6ce938ff4eb4900d901ed63555c778731a96365e53fadb36437a131a99"}, - {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:74068c601baff6ff021c70f0935b0c7bc528baa8ea210c202e03757c68c5a4ff"}, - {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ecd4ad8453ac17bc7ba3868371bffb46f628161ad0eefbd0a855d2c8c32dd81a"}, - {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7e2f58095acc211eb9d8b5771bf04df9ff37d6b87618d1cbf85f92399c98dae8"}, - {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e63601ad5cd8f860aa99d109889b5ac34de571c7ee902d6812d5d9ddcc77fa7d"}, - {file = "lxml-5.3.0-cp312-cp312-win32.whl", hash = "sha256:17e8d968d04a37c50ad9c456a286b525d78c4a1c15dd53aa46c1d8e06bf6fa30"}, - {file = "lxml-5.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:c1a69e58a6bb2de65902051d57fde951febad631a20a64572677a1052690482f"}, - {file = "lxml-5.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c72e9563347c7395910de6a3100a4840a75a6f60e05af5e58566868d5eb2d6a"}, - {file = "lxml-5.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e92ce66cd919d18d14b3856906a61d3f6b6a8500e0794142338da644260595cd"}, - {file = "lxml-5.3.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51"}, - {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b"}, - {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002"}, - {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:562e7494778a69086f0312ec9689f6b6ac1c6b65670ed7d0267e49f57ffa08c4"}, - {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:423b121f7e6fa514ba0c7918e56955a1d4470ed35faa03e3d9f0e3baa4c7e492"}, - {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c00f323cc00576df6165cc9d21a4c21285fa6b9989c5c39830c3903dc4303ef3"}, - {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:1fdc9fae8dd4c763e8a31e7630afef517eab9f5d5d31a278df087f307bf601f4"}, - {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:658f2aa69d31e09699705949b5fc4719cbecbd4a97f9656a232e7d6c7be1a367"}, - {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1473427aff3d66a3fa2199004c3e601e6c4500ab86696edffdbc84954c72d832"}, - {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a87de7dd873bf9a792bf1e58b1c3887b9264036629a5bf2d2e6579fe8e73edff"}, - {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd"}, - {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb"}, - {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b"}, - {file = "lxml-5.3.0-cp313-cp313-win32.whl", hash = "sha256:c802e1c2ed9f0c06a65bc4ed0189d000ada8049312cfeab6ca635e39c9608957"}, - {file = "lxml-5.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:406246b96d552e0503e17a1006fd27edac678b3fcc9f1be71a2f94b4ff61528d"}, - {file = "lxml-5.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8f0de2d390af441fe8b2c12626d103540b5d850d585b18fcada58d972b74a74e"}, - {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1afe0a8c353746e610bd9031a630a95bcfb1a720684c3f2b36c4710a0a96528f"}, - {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56b9861a71575f5795bde89256e7467ece3d339c9b43141dbdd54544566b3b94"}, - {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:9fb81d2824dff4f2e297a276297e9031f46d2682cafc484f49de182aa5e5df99"}, - {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2c226a06ecb8cdef28845ae976da407917542c5e6e75dcac7cc33eb04aaeb237"}, - {file = "lxml-5.3.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:7d3d1ca42870cdb6d0d29939630dbe48fa511c203724820fc0fd507b2fb46577"}, - {file = "lxml-5.3.0-cp36-cp36m-win32.whl", hash = "sha256:094cb601ba9f55296774c2d57ad68730daa0b13dc260e1f941b4d13678239e70"}, - {file = "lxml-5.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:eafa2c8658f4e560b098fe9fc54539f86528651f61849b22111a9b107d18910c"}, - {file = "lxml-5.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cb83f8a875b3d9b458cada4f880fa498646874ba4011dc974e071a0a84a1b033"}, - {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25f1b69d41656b05885aa185f5fdf822cb01a586d1b32739633679699f220391"}, - {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23e0553b8055600b3bf4a00b255ec5c92e1e4aebf8c2c09334f8368e8bd174d6"}, - {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ada35dd21dc6c039259596b358caab6b13f4db4d4a7f8665764d616daf9cc1d"}, - {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:81b4e48da4c69313192d8c8d4311e5d818b8be1afe68ee20f6385d0e96fc9512"}, - {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:2bc9fd5ca4729af796f9f59cd8ff160fe06a474da40aca03fcc79655ddee1a8b"}, - {file = "lxml-5.3.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:07da23d7ee08577760f0a71d67a861019103e4812c87e2fab26b039054594cc5"}, - {file = "lxml-5.3.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:ea2e2f6f801696ad7de8aec061044d6c8c0dd4037608c7cab38a9a4d316bfb11"}, - {file = "lxml-5.3.0-cp37-cp37m-win32.whl", hash = "sha256:5c54afdcbb0182d06836cc3d1be921e540be3ebdf8b8a51ee3ef987537455f84"}, - {file = "lxml-5.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:f2901429da1e645ce548bf9171784c0f74f0718c3f6150ce166be39e4dd66c3e"}, - {file = "lxml-5.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c56a1d43b2f9ee4786e4658c7903f05da35b923fb53c11025712562d5cc02753"}, - {file = "lxml-5.3.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ee8c39582d2652dcd516d1b879451500f8db3fe3607ce45d7c5957ab2596040"}, - {file = "lxml-5.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdf3a3059611f7585a78ee10399a15566356116a4288380921a4b598d807a22"}, - {file = "lxml-5.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:146173654d79eb1fc97498b4280c1d3e1e5d58c398fa530905c9ea50ea849b22"}, - {file = "lxml-5.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:0a7056921edbdd7560746f4221dca89bb7a3fe457d3d74267995253f46343f15"}, - {file = "lxml-5.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:9e4b47ac0f5e749cfc618efdf4726269441014ae1d5583e047b452a32e221920"}, - {file = "lxml-5.3.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f914c03e6a31deb632e2daa881fe198461f4d06e57ac3d0e05bbcab8eae01945"}, - {file = "lxml-5.3.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:213261f168c5e1d9b7535a67e68b1f59f92398dd17a56d934550837143f79c42"}, - {file = "lxml-5.3.0-cp38-cp38-win32.whl", hash = "sha256:218c1b2e17a710e363855594230f44060e2025b05c80d1f0661258142b2add2e"}, - {file = "lxml-5.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:315f9542011b2c4e1d280e4a20ddcca1761993dda3afc7a73b01235f8641e903"}, - {file = "lxml-5.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1ffc23010330c2ab67fac02781df60998ca8fe759e8efde6f8b756a20599c5de"}, - {file = "lxml-5.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2b3778cb38212f52fac9fe913017deea2fdf4eb1a4f8e4cfc6b009a13a6d3fcc"}, - {file = "lxml-5.3.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b0c7a688944891086ba192e21c5229dea54382f4836a209ff8d0a660fac06be"}, - {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:747a3d3e98e24597981ca0be0fd922aebd471fa99d0043a3842d00cdcad7ad6a"}, - {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86a6b24b19eaebc448dc56b87c4865527855145d851f9fc3891673ff97950540"}, - {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b11a5d918a6216e521c715b02749240fb07ae5a1fefd4b7bf12f833bc8b4fe70"}, - {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68b87753c784d6acb8a25b05cb526c3406913c9d988d51f80adecc2b0775d6aa"}, - {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:109fa6fede314cc50eed29e6e56c540075e63d922455346f11e4d7a036d2b8cf"}, - {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:02ced472497b8362c8e902ade23e3300479f4f43e45f4105c85ef43b8db85229"}, - {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:6b038cc86b285e4f9fea2ba5ee76e89f21ed1ea898e287dc277a25884f3a7dfe"}, - {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:7437237c6a66b7ca341e868cda48be24b8701862757426852c9b3186de1da8a2"}, - {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7f41026c1d64043a36fda21d64c5026762d53a77043e73e94b71f0521939cc71"}, - {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:482c2f67761868f0108b1743098640fbb2a28a8e15bf3f47ada9fa59d9fe08c3"}, - {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:1483fd3358963cc5c1c9b122c80606a3a79ee0875bcac0204149fa09d6ff2727"}, - {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dec2d1130a9cda5b904696cec33b2cfb451304ba9081eeda7f90f724097300a"}, - {file = "lxml-5.3.0-cp39-cp39-win32.whl", hash = "sha256:a0eabd0a81625049c5df745209dc7fcef6e2aea7793e5f003ba363610aa0a3ff"}, - {file = "lxml-5.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:89e043f1d9d341c52bf2af6d02e6adde62e0a46e6755d5eb60dc6e4f0b8aeca2"}, - {file = "lxml-5.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7b1cd427cb0d5f7393c31b7496419da594fe600e6fdc4b105a54f82405e6626c"}, - {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51806cfe0279e06ed8500ce19479d757db42a30fd509940b1701be9c86a5ff9a"}, - {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee70d08fd60c9565ba8190f41a46a54096afa0eeb8f76bd66f2c25d3b1b83005"}, - {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8dc2c0395bea8254d8daebc76dcf8eb3a95ec2a46fa6fae5eaccee366bfe02ce"}, - {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6ba0d3dcac281aad8a0e5b14c7ed6f9fa89c8612b47939fc94f80b16e2e9bc83"}, - {file = "lxml-5.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:6e91cf736959057f7aac7adfc83481e03615a8e8dd5758aa1d95ea69e8931dba"}, - {file = "lxml-5.3.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:94d6c3782907b5e40e21cadf94b13b0842ac421192f26b84c45f13f3c9d5dc27"}, - {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c300306673aa0f3ed5ed9372b21867690a17dba38c68c44b287437c362ce486b"}, - {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d9b952e07aed35fe2e1a7ad26e929595412db48535921c5013edc8aa4a35ce"}, - {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:01220dca0d066d1349bd6a1726856a78f7929f3878f7e2ee83c296c69495309e"}, - {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2d9b8d9177afaef80c53c0a9e30fa252ff3036fb1c6494d427c066a4ce6a282f"}, - {file = "lxml-5.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:20094fc3f21ea0a8669dc4c61ed7fa8263bd37d97d93b90f28fc613371e7a875"}, - {file = "lxml-5.3.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ace2c2326a319a0bb8a8b0e5b570c764962e95818de9f259ce814ee666603f19"}, - {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92e67a0be1639c251d21e35fe74df6bcc40cba445c2cda7c4a967656733249e2"}, - {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd5350b55f9fecddc51385463a4f67a5da829bc741e38cf689f38ec9023f54ab"}, - {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c1fefd7e3d00921c44dc9ca80a775af49698bbfd92ea84498e56acffd4c5469"}, - {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:71a8dd38fbd2f2319136d4ae855a7078c69c9a38ae06e0c17c73fd70fc6caad8"}, - {file = "lxml-5.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:97acf1e1fd66ab53dacd2c35b319d7e548380c2e9e8c54525c6e76d21b1ae3b1"}, - {file = "lxml-5.3.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:68934b242c51eb02907c5b81d138cb977b2129a0a75a8f8b60b01cb8586c7b21"}, - {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b710bc2b8292966b23a6a0121f7a6c51d45d2347edcc75f016ac123b8054d3f2"}, - {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18feb4b93302091b1541221196a2155aa296c363fd233814fa11e181adebc52f"}, - {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:3eb44520c4724c2e1a57c0af33a379eee41792595023f367ba3952a2d96c2aab"}, - {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:609251a0ca4770e5a8768ff902aa02bf636339c5a93f9349b48eb1f606f7f3e9"}, - {file = "lxml-5.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:516f491c834eb320d6c843156440fe7fc0d50b33e44387fcec5b02f0bc118a4c"}, - {file = "lxml-5.3.0.tar.gz", hash = "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f"}, + {file = "lxml-5.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a4058f16cee694577f7e4dd410263cd0ef75644b43802a689c2b3c2a7e69453b"}, + {file = "lxml-5.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:364de8f57d6eda0c16dcfb999af902da31396949efa0e583e12675d09709881b"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:528f3a0498a8edc69af0559bdcf8a9f5a8bf7c00051a6ef3141fdcf27017bbf5"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db4743e30d6f5f92b6d2b7c86b3ad250e0bad8dee4b7ad8a0c44bfb276af89a3"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17b5d7f8acf809465086d498d62a981fa6a56d2718135bb0e4aa48c502055f5c"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:928e75a7200a4c09e6efc7482a1337919cc61fe1ba289f297827a5b76d8969c2"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a997b784a639e05b9d4053ef3b20c7e447ea80814a762f25b8ed5a89d261eac"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:7b82e67c5feb682dbb559c3e6b78355f234943053af61606af126df2183b9ef9"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:f1de541a9893cf8a1b1db9bf0bf670a2decab42e3e82233d36a74eda7822b4c9"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:de1fc314c3ad6bc2f6bd5b5a5b9357b8c6896333d27fdbb7049aea8bd5af2d79"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:7c0536bd9178f754b277a3e53f90f9c9454a3bd108b1531ffff720e082d824f2"}, + {file = "lxml-5.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:68018c4c67d7e89951a91fbd371e2e34cd8cfc71f0bb43b5332db38497025d51"}, + {file = "lxml-5.3.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:aa826340a609d0c954ba52fd831f0fba2a4165659ab0ee1a15e4aac21f302406"}, + {file = "lxml-5.3.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:796520afa499732191e39fc95b56a3b07f95256f2d22b1c26e217fb69a9db5b5"}, + {file = "lxml-5.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3effe081b3135237da6e4c4530ff2a868d3f80be0bda027e118a5971285d42d0"}, + {file = "lxml-5.3.1-cp310-cp310-win32.whl", hash = "sha256:a22f66270bd6d0804b02cd49dae2b33d4341015545d17f8426f2c4e22f557a23"}, + {file = "lxml-5.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:0bcfadea3cdc68e678d2b20cb16a16716887dd00a881e16f7d806c2138b8ff0c"}, + {file = "lxml-5.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e220f7b3e8656ab063d2eb0cd536fafef396829cafe04cb314e734f87649058f"}, + {file = "lxml-5.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f2cfae0688fd01f7056a17367e3b84f37c545fb447d7282cf2c242b16262607"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67d2f8ad9dcc3a9e826bdc7802ed541a44e124c29b7d95a679eeb58c1c14ade8"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db0c742aad702fd5d0c6611a73f9602f20aec2007c102630c06d7633d9c8f09a"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:198bb4b4dd888e8390afa4f170d4fa28467a7eaf857f1952589f16cfbb67af27"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2a3e412ce1849be34b45922bfef03df32d1410a06d1cdeb793a343c2f1fd666"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b8969dbc8d09d9cd2ae06362c3bad27d03f433252601ef658a49bd9f2b22d79"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5be8f5e4044146a69c96077c7e08f0709c13a314aa5315981185c1f00235fe65"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:133f3493253a00db2c870d3740bc458ebb7d937bd0a6a4f9328373e0db305709"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:52d82b0d436edd6a1d22d94a344b9a58abd6c68c357ed44f22d4ba8179b37629"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b6f92e35e2658a5ed51c6634ceb5ddae32053182851d8cad2a5bc102a359b33"}, + {file = "lxml-5.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:203b1d3eaebd34277be06a3eb880050f18a4e4d60861efba4fb946e31071a295"}, + {file = "lxml-5.3.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:155e1a5693cf4b55af652f5c0f78ef36596c7f680ff3ec6eb4d7d85367259b2c"}, + {file = "lxml-5.3.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22ec2b3c191f43ed21f9545e9df94c37c6b49a5af0a874008ddc9132d49a2d9c"}, + {file = "lxml-5.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7eda194dd46e40ec745bf76795a7cccb02a6a41f445ad49d3cf66518b0bd9cff"}, + {file = "lxml-5.3.1-cp311-cp311-win32.whl", hash = "sha256:fb7c61d4be18e930f75948705e9718618862e6fc2ed0d7159b2262be73f167a2"}, + {file = "lxml-5.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:c809eef167bf4a57af4b03007004896f5c60bd38dc3852fcd97a26eae3d4c9e6"}, + {file = "lxml-5.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e69add9b6b7b08c60d7ff0152c7c9a6c45b4a71a919be5abde6f98f1ea16421c"}, + {file = "lxml-5.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4e52e1b148867b01c05e21837586ee307a01e793b94072d7c7b91d2c2da02ffe"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4b382e0e636ed54cd278791d93fe2c4f370772743f02bcbe431a160089025c9"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2e49dc23a10a1296b04ca9db200c44d3eb32c8d8ec532e8c1fd24792276522a"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4399b4226c4785575fb20998dc571bc48125dc92c367ce2602d0d70e0c455eb0"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5412500e0dc5481b1ee9cf6b38bb3b473f6e411eb62b83dc9b62699c3b7b79f7"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c93ed3c998ea8472be98fb55aed65b5198740bfceaec07b2eba551e55b7b9ae"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:63d57fc94eb0bbb4735e45517afc21ef262991d8758a8f2f05dd6e4174944519"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:b450d7cabcd49aa7ab46a3c6aa3ac7e1593600a1a0605ba536ec0f1b99a04322"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:4df0ec814b50275ad6a99bc82a38b59f90e10e47714ac9871e1b223895825468"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d184f85ad2bb1f261eac55cddfcf62a70dee89982c978e92b9a74a1bfef2e367"}, + {file = "lxml-5.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b725e70d15906d24615201e650d5b0388b08a5187a55f119f25874d0103f90dd"}, + {file = "lxml-5.3.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a31fa7536ec1fb7155a0cd3a4e3d956c835ad0a43e3610ca32384d01f079ea1c"}, + {file = "lxml-5.3.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3c3c8b55c7fc7b7e8877b9366568cc73d68b82da7fe33d8b98527b73857a225f"}, + {file = "lxml-5.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d61ec60945d694df806a9aec88e8f29a27293c6e424f8ff91c80416e3c617645"}, + {file = "lxml-5.3.1-cp312-cp312-win32.whl", hash = "sha256:f4eac0584cdc3285ef2e74eee1513a6001681fd9753b259e8159421ed28a72e5"}, + {file = "lxml-5.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:29bfc8d3d88e56ea0a27e7c4897b642706840247f59f4377d81be8f32aa0cfbf"}, + {file = "lxml-5.3.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c093c7088b40d8266f57ed71d93112bd64c6724d31f0794c1e52cc4857c28e0e"}, + {file = "lxml-5.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b0884e3f22d87c30694e625b1e62e6f30d39782c806287450d9dc2fdf07692fd"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1637fa31ec682cd5760092adfabe86d9b718a75d43e65e211d5931809bc111e7"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a364e8e944d92dcbf33b6b494d4e0fb3499dcc3bd9485beb701aa4b4201fa414"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:779e851fd0e19795ccc8a9bb4d705d6baa0ef475329fe44a13cf1e962f18ff1e"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c4393600915c308e546dc7003d74371744234e8444a28622d76fe19b98fa59d1"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:673b9d8e780f455091200bba8534d5f4f465944cbdd61f31dc832d70e29064a5"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2e4a570f6a99e96c457f7bec5ad459c9c420ee80b99eb04cbfcfe3fc18ec6423"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:71f31eda4e370f46af42fc9f264fafa1b09f46ba07bdbee98f25689a04b81c20"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:42978a68d3825eaac55399eb37a4d52012a205c0c6262199b8b44fcc6fd686e8"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:8b1942b3e4ed9ed551ed3083a2e6e0772de1e5e3aca872d955e2e86385fb7ff9"}, + {file = "lxml-5.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:85c4f11be9cf08917ac2a5a8b6e1ef63b2f8e3799cec194417e76826e5f1de9c"}, + {file = "lxml-5.3.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:231cf4d140b22a923b1d0a0a4e0b4f972e5893efcdec188934cc65888fd0227b"}, + {file = "lxml-5.3.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5865b270b420eda7b68928d70bb517ccbe045e53b1a428129bb44372bf3d7dd5"}, + {file = "lxml-5.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dbf7bebc2275016cddf3c997bf8a0f7044160714c64a9b83975670a04e6d2252"}, + {file = "lxml-5.3.1-cp313-cp313-win32.whl", hash = "sha256:d0751528b97d2b19a388b302be2a0ee05817097bab46ff0ed76feeec24951f78"}, + {file = "lxml-5.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:91fb6a43d72b4f8863d21f347a9163eecbf36e76e2f51068d59cd004c506f332"}, + {file = "lxml-5.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:016b96c58e9a4528219bb563acf1aaaa8bc5452e7651004894a973f03b84ba81"}, + {file = "lxml-5.3.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82a4bb10b0beef1434fb23a09f001ab5ca87895596b4581fd53f1e5145a8934a"}, + {file = "lxml-5.3.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d68eeef7b4d08a25e51897dac29bcb62aba830e9ac6c4e3297ee7c6a0cf6439"}, + {file = "lxml-5.3.1-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:f12582b8d3b4c6be1d298c49cb7ae64a3a73efaf4c2ab4e37db182e3545815ac"}, + {file = "lxml-5.3.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2df7ed5edeb6bd5590914cd61df76eb6cce9d590ed04ec7c183cf5509f73530d"}, + {file = "lxml-5.3.1-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:585c4dc429deebc4307187d2b71ebe914843185ae16a4d582ee030e6cfbb4d8a"}, + {file = "lxml-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:06a20d607a86fccab2fc15a77aa445f2bdef7b49ec0520a842c5c5afd8381576"}, + {file = "lxml-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:057e30d0012439bc54ca427a83d458752ccda725c1c161cc283db07bcad43cf9"}, + {file = "lxml-5.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4867361c049761a56bd21de507cab2c2a608c55102311d142ade7dab67b34f32"}, + {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dddf0fb832486cc1ea71d189cb92eb887826e8deebe128884e15020bb6e3f61"}, + {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bcc211542f7af6f2dfb705f5f8b74e865592778e6cafdfd19c792c244ccce19"}, + {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaca5a812f050ab55426c32177091130b1e49329b3f002a32934cd0245571307"}, + {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:236610b77589faf462337b3305a1be91756c8abc5a45ff7ca8f245a71c5dab70"}, + {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:aed57b541b589fa05ac248f4cb1c46cbb432ab82cbd467d1c4f6a2bdc18aecf9"}, + {file = "lxml-5.3.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:75fa3d6946d317ffc7016a6fcc44f42db6d514b7fdb8b4b28cbe058303cb6e53"}, + {file = "lxml-5.3.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:96eef5b9f336f623ffc555ab47a775495e7e8846dde88de5f941e2906453a1ce"}, + {file = "lxml-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:ef45f31aec9be01379fc6c10f1d9c677f032f2bac9383c827d44f620e8a88407"}, + {file = "lxml-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0611da6b07dd3720f492db1b463a4d1175b096b49438761cc9f35f0d9eaaef5"}, + {file = "lxml-5.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b2aca14c235c7a08558fe0a4786a1a05873a01e86b474dfa8f6df49101853a4e"}, + {file = "lxml-5.3.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae82fce1d964f065c32c9517309f0c7be588772352d2f40b1574a214bd6e6098"}, + {file = "lxml-5.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7aae7a3d63b935babfdc6864b31196afd5145878ddd22f5200729006366bc4d5"}, + {file = "lxml-5.3.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8e0d177b1fe251c3b1b914ab64135475c5273c8cfd2857964b2e3bb0fe196a7"}, + {file = "lxml-5.3.1-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:6c4dd3bfd0c82400060896717dd261137398edb7e524527438c54a8c34f736bf"}, + {file = "lxml-5.3.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:f1208c1c67ec9e151d78aa3435aa9b08a488b53d9cfac9b699f15255a3461ef2"}, + {file = "lxml-5.3.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:c6aacf00d05b38a5069826e50ae72751cb5bc27bdc4d5746203988e429b385bb"}, + {file = "lxml-5.3.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5881aaa4bf3a2d086c5f20371d3a5856199a0d8ac72dd8d0dbd7a2ecfc26ab73"}, + {file = "lxml-5.3.1-cp38-cp38-win32.whl", hash = "sha256:45fbb70ccbc8683f2fb58bea89498a7274af1d9ec7995e9f4af5604e028233fc"}, + {file = "lxml-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:7512b4d0fc5339d5abbb14d1843f70499cab90d0b864f790e73f780f041615d7"}, + {file = "lxml-5.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5885bc586f1edb48e5d68e7a4b4757b5feb2a496b64f462b4d65950f5af3364f"}, + {file = "lxml-5.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1b92fe86e04f680b848fff594a908edfa72b31bfc3499ef7433790c11d4c8cd8"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a091026c3bf7519ab1e64655a3f52a59ad4a4e019a6f830c24d6430695b1cf6a"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ffb141361108e864ab5f1813f66e4e1164181227f9b1f105b042729b6c15125"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3715cdf0dd31b836433af9ee9197af10e3df41d273c19bb249230043667a5dfd"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88b72eb7222d918c967202024812c2bfb4048deeb69ca328363fb8e15254c549"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa59974880ab5ad8ef3afaa26f9bda148c5f39e06b11a8ada4660ecc9fb2feb3"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:3bb8149840daf2c3f97cebf00e4ed4a65a0baff888bf2605a8d0135ff5cf764e"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:0d6b2fa86becfa81f0a0271ccb9eb127ad45fb597733a77b92e8a35e53414914"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:136bf638d92848a939fd8f0e06fcf92d9f2e4b57969d94faae27c55f3d85c05b"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:89934f9f791566e54c1d92cdc8f8fd0009447a5ecdb1ec6b810d5f8c4955f6be"}, + {file = "lxml-5.3.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a8ade0363f776f87f982572c2860cc43c65ace208db49c76df0a21dde4ddd16e"}, + {file = "lxml-5.3.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:bfbbab9316330cf81656fed435311386610f78b6c93cc5db4bebbce8dd146675"}, + {file = "lxml-5.3.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:172d65f7c72a35a6879217bcdb4bb11bc88d55fb4879e7569f55616062d387c2"}, + {file = "lxml-5.3.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e3c623923967f3e5961d272718655946e5322b8d058e094764180cdee7bab1af"}, + {file = "lxml-5.3.1-cp39-cp39-win32.whl", hash = "sha256:ce0930a963ff593e8bb6fda49a503911accc67dee7e5445eec972668e672a0f0"}, + {file = "lxml-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:f7b64fcd670bca8800bc10ced36620c6bbb321e7bc1214b9c0c0df269c1dddc2"}, + {file = "lxml-5.3.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:afa578b6524ff85fb365f454cf61683771d0170470c48ad9d170c48075f86725"}, + {file = "lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67f5e80adf0aafc7b5454f2c1cb0cde920c9b1f2cbd0485f07cc1d0497c35c5d"}, + {file = "lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd0b80ac2d8f13ffc906123a6f20b459cb50a99222d0da492360512f3e50f84"}, + {file = "lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:422c179022ecdedbe58b0e242607198580804253da220e9454ffe848daa1cfd2"}, + {file = "lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:524ccfded8989a6595dbdda80d779fb977dbc9a7bc458864fc9a0c2fc15dc877"}, + {file = "lxml-5.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:48fd46bf7155def2e15287c6f2b133a2f78e2d22cdf55647269977b873c65499"}, + {file = "lxml-5.3.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:05123fad495a429f123307ac6d8fd6f977b71e9a0b6d9aeeb8f80c017cb17131"}, + {file = "lxml-5.3.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a243132767150a44e6a93cd1dde41010036e1cbc63cc3e9fe1712b277d926ce3"}, + {file = "lxml-5.3.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c92ea6d9dd84a750b2bae72ff5e8cf5fdd13e58dda79c33e057862c29a8d5b50"}, + {file = "lxml-5.3.1-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2f1be45d4c15f237209bbf123a0e05b5d630c8717c42f59f31ea9eae2ad89394"}, + {file = "lxml-5.3.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:a83d3adea1e0ee36dac34627f78ddd7f093bb9cfc0a8e97f1572a949b695cb98"}, + {file = "lxml-5.3.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3edbb9c9130bac05d8c3fe150c51c337a471cc7fdb6d2a0a7d3a88e88a829314"}, + {file = "lxml-5.3.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2f23cf50eccb3255b6e913188291af0150d89dab44137a69e14e4dcb7be981f1"}, + {file = "lxml-5.3.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df7e5edac4778127f2bf452e0721a58a1cfa4d1d9eac63bdd650535eb8543615"}, + {file = "lxml-5.3.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:094b28ed8a8a072b9e9e2113a81fda668d2053f2ca9f2d202c2c8c7c2d6516b1"}, + {file = "lxml-5.3.1-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:514fe78fc4b87e7a7601c92492210b20a1b0c6ab20e71e81307d9c2e377c64de"}, + {file = "lxml-5.3.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:8fffc08de02071c37865a155e5ea5fce0282e1546fd5bde7f6149fcaa32558ac"}, + {file = "lxml-5.3.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4b0d5cdba1b655d5b18042ac9c9ff50bda33568eb80feaaca4fc237b9c4fbfde"}, + {file = "lxml-5.3.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3031e4c16b59424e8d78522c69b062d301d951dc55ad8685736c3335a97fc270"}, + {file = "lxml-5.3.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb659702a45136c743bc130760c6f137870d4df3a9e14386478b8a0511abcfca"}, + {file = "lxml-5.3.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a11b16a33656ffc43c92a5343a28dc71eefe460bcc2a4923a96f292692709f6"}, + {file = "lxml-5.3.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c5ae125276f254b01daa73e2c103363d3e99e3e10505686ac7d9d2442dd4627a"}, + {file = "lxml-5.3.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c76722b5ed4a31ba103e0dc77ab869222ec36efe1a614e42e9bcea88a36186fe"}, + {file = "lxml-5.3.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:33e06717c00c788ab4e79bc4726ecc50c54b9bfb55355eae21473c145d83c2d2"}, + {file = "lxml-5.3.1.tar.gz", hash = "sha256:106b7b5d2977b339f1e97efe2778e2ab20e99994cbb0ec5e55771ed0795920c8"}, ] [package.extras] cssselect = ["cssselect (>=0.7)"] -html-clean = ["lxml-html-clean"] +html-clean = ["lxml_html_clean"] html5 = ["html5lib"] htmlsoup = ["BeautifulSoup4"] -source = ["Cython (>=3.0.11)"] +source = ["Cython (>=3.0.11,<3.1.0)"] [[package]] name = "markdown" From 032b5769ab35d0bc290970f6b2ef8fb0f171cb7d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 05:04:08 +0000 Subject: [PATCH 72/79] Bump mkdocs-material from 9.6.3 to 9.6.4 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.6.3 to 9.6.4. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.6.3...9.6.4) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index a14e8b2b83..53aa981adb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1201,13 +1201,13 @@ pyyaml = ">=5.1" [[package]] name = "mkdocs-material" -version = "9.6.3" +version = "9.6.4" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.6.3-py3-none-any.whl", hash = "sha256:1125622067e26940806701219303b27c0933e04533560725d97ec26fd16a39cf"}, - {file = "mkdocs_material-9.6.3.tar.gz", hash = "sha256:c87f7d1c39ce6326da5e10e232aed51bae46252e646755900f4b0fc9192fa832"}, + {file = "mkdocs_material-9.6.4-py3-none-any.whl", hash = "sha256:414e8376551def6d644b8e6f77226022868532a792eb2c9accf52199009f568f"}, + {file = "mkdocs_material-9.6.4.tar.gz", hash = "sha256:4d1d35e1c1d3e15294cb7fa5d02e0abaee70d408f75027dc7be6e30fb32e6867"}, ] [package.dependencies] From dc1718979fee700f21e9c7ac2a91144f2a334e87 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 05:04:56 +0000 Subject: [PATCH 73/79] Bump websockets from 14.2 to 15.0 Bumps [websockets](https://github.com/python-websockets/websockets) from 14.2 to 15.0. - [Release notes](https://github.com/python-websockets/websockets/releases) - [Commits](https://github.com/python-websockets/websockets/compare/14.2...15.0) --- updated-dependencies: - dependency-name: websockets dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> --- poetry.lock | 142 ++++++++++++++++++++++++------------------------- pyproject.toml | 2 +- 2 files changed, 72 insertions(+), 72 deletions(-) diff --git a/poetry.lock b/poetry.lock index a14e8b2b83..0f0aa1497d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2887,80 +2887,80 @@ watchmedo = ["PyYAML (>=3.10)"] [[package]] name = "websockets" -version = "14.2" +version = "15.0" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false python-versions = ">=3.9" files = [ - {file = "websockets-14.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e8179f95323b9ab1c11723e5d91a89403903f7b001828161b480a7810b334885"}, - {file = "websockets-14.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d8c3e2cdb38f31d8bd7d9d28908005f6fa9def3324edb9bf336d7e4266fd397"}, - {file = "websockets-14.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:714a9b682deb4339d39ffa674f7b674230227d981a37d5d174a4a83e3978a610"}, - {file = "websockets-14.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2e53c72052f2596fb792a7acd9704cbc549bf70fcde8a99e899311455974ca3"}, - {file = "websockets-14.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3fbd68850c837e57373d95c8fe352203a512b6e49eaae4c2f4088ef8cf21980"}, - {file = "websockets-14.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b27ece32f63150c268593d5fdb82819584831a83a3f5809b7521df0685cd5d8"}, - {file = "websockets-14.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4daa0faea5424d8713142b33825fff03c736f781690d90652d2c8b053345b0e7"}, - {file = "websockets-14.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:bc63cee8596a6ec84d9753fd0fcfa0452ee12f317afe4beae6b157f0070c6c7f"}, - {file = "websockets-14.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a570862c325af2111343cc9b0257b7119b904823c675b22d4ac547163088d0d"}, - {file = "websockets-14.2-cp310-cp310-win32.whl", hash = "sha256:75862126b3d2d505e895893e3deac0a9339ce750bd27b4ba515f008b5acf832d"}, - {file = "websockets-14.2-cp310-cp310-win_amd64.whl", hash = "sha256:cc45afb9c9b2dc0852d5c8b5321759cf825f82a31bfaf506b65bf4668c96f8b2"}, - {file = "websockets-14.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3bdc8c692c866ce5fefcaf07d2b55c91d6922ac397e031ef9b774e5b9ea42166"}, - {file = "websockets-14.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c93215fac5dadc63e51bcc6dceca72e72267c11def401d6668622b47675b097f"}, - {file = "websockets-14.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c9b6535c0e2cf8a6bf938064fb754aaceb1e6a4a51a80d884cd5db569886910"}, - {file = "websockets-14.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a52a6d7cf6938e04e9dceb949d35fbdf58ac14deea26e685ab6368e73744e4c"}, - {file = "websockets-14.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f05702e93203a6ff5226e21d9b40c037761b2cfb637187c9802c10f58e40473"}, - {file = "websockets-14.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22441c81a6748a53bfcb98951d58d1af0661ab47a536af08920d129b4d1c3473"}, - {file = "websockets-14.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd9b868d78b194790e6236d9cbc46d68aba4b75b22497eb4ab64fa640c3af56"}, - {file = "websockets-14.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1a5a20d5843886d34ff8c57424cc65a1deda4375729cbca4cb6b3353f3ce4142"}, - {file = "websockets-14.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:34277a29f5303d54ec6468fb525d99c99938607bc96b8d72d675dee2b9f5bf1d"}, - {file = "websockets-14.2-cp311-cp311-win32.whl", hash = "sha256:02687db35dbc7d25fd541a602b5f8e451a238ffa033030b172ff86a93cb5dc2a"}, - {file = "websockets-14.2-cp311-cp311-win_amd64.whl", hash = "sha256:862e9967b46c07d4dcd2532e9e8e3c2825e004ffbf91a5ef9dde519ee2effb0b"}, - {file = "websockets-14.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f20522e624d7ffbdbe259c6b6a65d73c895045f76a93719aa10cd93b3de100c"}, - {file = "websockets-14.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:647b573f7d3ada919fd60e64d533409a79dcf1ea21daeb4542d1d996519ca967"}, - {file = "websockets-14.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6af99a38e49f66be5a64b1e890208ad026cda49355661549c507152113049990"}, - {file = "websockets-14.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:091ab63dfc8cea748cc22c1db2814eadb77ccbf82829bac6b2fbe3401d548eda"}, - {file = "websockets-14.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b374e8953ad477d17e4851cdc66d83fdc2db88d9e73abf755c94510ebddceb95"}, - {file = "websockets-14.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a39d7eceeea35db85b85e1169011bb4321c32e673920ae9c1b6e0978590012a3"}, - {file = "websockets-14.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0a6f3efd47ffd0d12080594f434faf1cd2549b31e54870b8470b28cc1d3817d9"}, - {file = "websockets-14.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:065ce275e7c4ffb42cb738dd6b20726ac26ac9ad0a2a48e33ca632351a737267"}, - {file = "websockets-14.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e9d0e53530ba7b8b5e389c02282f9d2aa47581514bd6049d3a7cffe1385cf5fe"}, - {file = "websockets-14.2-cp312-cp312-win32.whl", hash = "sha256:20e6dd0984d7ca3037afcb4494e48c74ffb51e8013cac71cf607fffe11df7205"}, - {file = "websockets-14.2-cp312-cp312-win_amd64.whl", hash = "sha256:44bba1a956c2c9d268bdcdf234d5e5ff4c9b6dc3e300545cbe99af59dda9dcce"}, - {file = "websockets-14.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f1372e511c7409a542291bce92d6c83320e02c9cf392223272287ce55bc224e"}, - {file = "websockets-14.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4da98b72009836179bb596a92297b1a61bb5a830c0e483a7d0766d45070a08ad"}, - {file = "websockets-14.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8a86a269759026d2bde227652b87be79f8a734e582debf64c9d302faa1e9f03"}, - {file = "websockets-14.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86cf1aaeca909bf6815ea714d5c5736c8d6dd3a13770e885aafe062ecbd04f1f"}, - {file = "websockets-14.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b0f6c3ba3b1240f602ebb3971d45b02cc12bd1845466dd783496b3b05783a5"}, - {file = "websockets-14.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:669c3e101c246aa85bc8534e495952e2ca208bd87994650b90a23d745902db9a"}, - {file = "websockets-14.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eabdb28b972f3729348e632ab08f2a7b616c7e53d5414c12108c29972e655b20"}, - {file = "websockets-14.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2066dc4cbcc19f32c12a5a0e8cc1b7ac734e5b64ac0a325ff8353451c4b15ef2"}, - {file = "websockets-14.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ab95d357cd471df61873dadf66dd05dd4709cae001dd6342edafc8dc6382f307"}, - {file = "websockets-14.2-cp313-cp313-win32.whl", hash = "sha256:a9e72fb63e5f3feacdcf5b4ff53199ec8c18d66e325c34ee4c551ca748623bbc"}, - {file = "websockets-14.2-cp313-cp313-win_amd64.whl", hash = "sha256:b439ea828c4ba99bb3176dc8d9b933392a2413c0f6b149fdcba48393f573377f"}, - {file = "websockets-14.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7cd5706caec1686c5d233bc76243ff64b1c0dc445339bd538f30547e787c11fe"}, - {file = "websockets-14.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ec607328ce95a2f12b595f7ae4c5d71bf502212bddcea528290b35c286932b12"}, - {file = "websockets-14.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da85651270c6bfb630136423037dd4975199e5d4114cae6d3066641adcc9d1c7"}, - {file = "websockets-14.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3ecadc7ce90accf39903815697917643f5b7cfb73c96702318a096c00aa71f5"}, - {file = "websockets-14.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1979bee04af6a78608024bad6dfcc0cc930ce819f9e10342a29a05b5320355d0"}, - {file = "websockets-14.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dddacad58e2614a24938a50b85969d56f88e620e3f897b7d80ac0d8a5800258"}, - {file = "websockets-14.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:89a71173caaf75fa71a09a5f614f450ba3ec84ad9fca47cb2422a860676716f0"}, - {file = "websockets-14.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6af6a4b26eea4fc06c6818a6b962a952441e0e39548b44773502761ded8cc1d4"}, - {file = "websockets-14.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:80c8efa38957f20bba0117b48737993643204645e9ec45512579132508477cfc"}, - {file = "websockets-14.2-cp39-cp39-win32.whl", hash = "sha256:2e20c5f517e2163d76e2729104abc42639c41cf91f7b1839295be43302713661"}, - {file = "websockets-14.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4c8cef610e8d7c70dea92e62b6814a8cd24fbd01d7103cc89308d2bfe1659ef"}, - {file = "websockets-14.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d7d9cafbccba46e768be8a8ad4635fa3eae1ffac4c6e7cb4eb276ba41297ed29"}, - {file = "websockets-14.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c76193c1c044bd1e9b3316dcc34b174bbf9664598791e6fb606d8d29000e070c"}, - {file = "websockets-14.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd475a974d5352390baf865309fe37dec6831aafc3014ffac1eea99e84e83fc2"}, - {file = "websockets-14.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c6c0097a41968b2e2b54ed3424739aab0b762ca92af2379f152c1aef0187e1c"}, - {file = "websockets-14.2-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d7ff794c8b36bc402f2e07c0b2ceb4a2424147ed4785ff03e2a7af03711d60a"}, - {file = "websockets-14.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dec254fcabc7bd488dab64846f588fc5b6fe0d78f641180030f8ea27b76d72c3"}, - {file = "websockets-14.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:bbe03eb853e17fd5b15448328b4ec7fb2407d45fb0245036d06a3af251f8e48f"}, - {file = "websockets-14.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a3c4aa3428b904d5404a0ed85f3644d37e2cb25996b7f096d77caeb0e96a3b42"}, - {file = "websockets-14.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:577a4cebf1ceaf0b65ffc42c54856214165fb8ceeba3935852fc33f6b0c55e7f"}, - {file = "websockets-14.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad1c1d02357b7665e700eca43a31d52814ad9ad9b89b58118bdabc365454b574"}, - {file = "websockets-14.2-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f390024a47d904613577df83ba700bd189eedc09c57af0a904e5c39624621270"}, - {file = "websockets-14.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3c1426c021c38cf92b453cdf371228d3430acd775edee6bac5a4d577efc72365"}, - {file = "websockets-14.2-py3-none-any.whl", hash = "sha256:7a6ceec4ea84469f15cf15807a747e9efe57e369c384fa86e022b3bea679b79b"}, - {file = "websockets-14.2.tar.gz", hash = "sha256:5059ed9c54945efb321f097084b4c7e52c246f2c869815876a69d1efc4ad6eb5"}, + {file = "websockets-15.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5e6ee18a53dd5743e6155b8ff7e8e477c25b29b440f87f65be8165275c87fef0"}, + {file = "websockets-15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ee06405ea2e67366a661ed313e14cf2a86e84142a3462852eb96348f7219cee3"}, + {file = "websockets-15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8711682a629bbcaf492f5e0af72d378e976ea1d127a2d47584fa1c2c080b436b"}, + {file = "websockets-15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94c4a9b01eede952442c088d415861b0cf2053cbd696b863f6d5022d4e4e2453"}, + {file = "websockets-15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:45535fead66e873f411c1d3cf0d3e175e66f4dd83c4f59d707d5b3e4c56541c4"}, + {file = "websockets-15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e389efe46ccb25a1f93d08c7a74e8123a2517f7b7458f043bd7529d1a63ffeb"}, + {file = "websockets-15.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:67a04754d121ea5ca39ddedc3f77071651fb5b0bc6b973c71c515415b44ed9c5"}, + {file = "websockets-15.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:bd66b4865c8b853b8cca7379afb692fc7f52cf898786537dfb5e5e2d64f0a47f"}, + {file = "websockets-15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a4cc73a6ae0a6751b76e69cece9d0311f054da9b22df6a12f2c53111735657c8"}, + {file = "websockets-15.0-cp310-cp310-win32.whl", hash = "sha256:89da58e4005e153b03fe8b8794330e3f6a9774ee9e1c3bd5bc52eb098c3b0c4f"}, + {file = "websockets-15.0-cp310-cp310-win_amd64.whl", hash = "sha256:4ff380aabd7a74a42a760ee76c68826a8f417ceb6ea415bd574a035a111fd133"}, + {file = "websockets-15.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:dd24c4d256558429aeeb8d6c24ebad4e982ac52c50bc3670ae8646c181263965"}, + {file = "websockets-15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f83eca8cbfd168e424dfa3b3b5c955d6c281e8fc09feb9d870886ff8d03683c7"}, + {file = "websockets-15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4095a1f2093002c2208becf6f9a178b336b7572512ee0a1179731acb7788e8ad"}, + {file = "websockets-15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb915101dfbf318486364ce85662bb7b020840f68138014972c08331458d41f3"}, + {file = "websockets-15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:45d464622314973d78f364689d5dbb9144e559f93dca11b11af3f2480b5034e1"}, + {file = "websockets-15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace960769d60037ca9625b4c578a6f28a14301bd2a1ff13bb00e824ac9f73e55"}, + {file = "websockets-15.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c7cd4b1015d2f60dfe539ee6c95bc968d5d5fad92ab01bb5501a77393da4f596"}, + {file = "websockets-15.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4f7290295794b5dec470867c7baa4a14182b9732603fd0caf2a5bf1dc3ccabf3"}, + {file = "websockets-15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3abd670ca7ce230d5a624fd3d55e055215d8d9b723adee0a348352f5d8d12ff4"}, + {file = "websockets-15.0-cp311-cp311-win32.whl", hash = "sha256:110a847085246ab8d4d119632145224d6b49e406c64f1bbeed45c6f05097b680"}, + {file = "websockets-15.0-cp311-cp311-win_amd64.whl", hash = "sha256:8d7bbbe2cd6ed80aceef2a14e9f1c1b61683194c216472ed5ff33b700e784e37"}, + {file = "websockets-15.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:cccc18077acd34c8072578394ec79563664b1c205f7a86a62e94fafc7b59001f"}, + {file = "websockets-15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d4c22992e24f12de340ca5f824121a5b3e1a37ad4360b4e1aaf15e9d1c42582d"}, + {file = "websockets-15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1206432cc6c644f6fc03374b264c5ff805d980311563202ed7fef91a38906276"}, + {file = "websockets-15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d3cc75ef3e17490042c47e0523aee1bcc4eacd2482796107fd59dd1100a44bc"}, + {file = "websockets-15.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b89504227a5311610e4be16071465885a0a3d6b0e82e305ef46d9b064ce5fb72"}, + {file = "websockets-15.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56e3efe356416bc67a8e093607315951d76910f03d2b3ad49c4ade9207bf710d"}, + {file = "websockets-15.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0f2205cdb444a42a7919690238fb5979a05439b9dbb73dd47c863d39640d85ab"}, + {file = "websockets-15.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:aea01f40995fa0945c020228ab919b8dfc93fc8a9f2d3d705ab5b793f32d9e99"}, + {file = "websockets-15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9f8e33747b1332db11cf7fcf4a9512bef9748cb5eb4d3f7fbc8c30d75dc6ffc"}, + {file = "websockets-15.0-cp312-cp312-win32.whl", hash = "sha256:32e02a2d83f4954aa8c17e03fe8ec6962432c39aca4be7e8ee346b05a3476904"}, + {file = "websockets-15.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffc02b159b65c05f2ed9ec176b715b66918a674bd4daed48a9a7a590dd4be1aa"}, + {file = "websockets-15.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d2244d8ab24374bed366f9ff206e2619345f9cd7fe79aad5225f53faac28b6b1"}, + {file = "websockets-15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3a302241fbe825a3e4fe07666a2ab513edfdc6d43ce24b79691b45115273b5e7"}, + {file = "websockets-15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:10552fed076757a70ba2c18edcbc601c7637b30cdfe8c24b65171e824c7d6081"}, + {file = "websockets-15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c53f97032b87a406044a1c33d1e9290cc38b117a8062e8a8b285175d7e2f99c9"}, + {file = "websockets-15.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1caf951110ca757b8ad9c4974f5cac7b8413004d2f29707e4d03a65d54cedf2b"}, + {file = "websockets-15.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bf1ab71f9f23b0a1d52ec1682a3907e0c208c12fef9c3e99d2b80166b17905f"}, + {file = "websockets-15.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bfcd3acc1a81f106abac6afd42327d2cf1e77ec905ae11dc1d9142a006a496b6"}, + {file = "websockets-15.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c8c5c8e1bac05ef3c23722e591ef4f688f528235e2480f157a9cfe0a19081375"}, + {file = "websockets-15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:86bfb52a9cfbcc09aba2b71388b0a20ea5c52b6517c0b2e316222435a8cdab72"}, + {file = "websockets-15.0-cp313-cp313-win32.whl", hash = "sha256:26ba70fed190708551c19a360f9d7eca8e8c0f615d19a574292b7229e0ae324c"}, + {file = "websockets-15.0-cp313-cp313-win_amd64.whl", hash = "sha256:ae721bcc8e69846af00b7a77a220614d9b2ec57d25017a6bbde3a99473e41ce8"}, + {file = "websockets-15.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c348abc5924caa02a62896300e32ea80a81521f91d6db2e853e6b1994017c9f6"}, + {file = "websockets-15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5294fcb410ed0a45d5d1cdedc4e51a60aab5b2b3193999028ea94afc2f554b05"}, + {file = "websockets-15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c24ba103ecf45861e2e1f933d40b2d93f5d52d8228870c3e7bf1299cd1cb8ff1"}, + {file = "websockets-15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc8821a03bcfb36e4e4705316f6b66af28450357af8a575dc8f4b09bf02a3dee"}, + {file = "websockets-15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc5ae23ada6515f31604f700009e2df90b091b67d463a8401c1d8a37f76c1d7"}, + {file = "websockets-15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ac67b542505186b3bbdaffbc303292e1ee9c8729e5d5df243c1f20f4bb9057e"}, + {file = "websockets-15.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c86dc2068f1c5ca2065aca34f257bbf4f78caf566eb230f692ad347da191f0a1"}, + {file = "websockets-15.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:30cff3ef329682b6182c01c568f551481774c476722020b8f7d0daacbed07a17"}, + {file = "websockets-15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:98dcf978d4c6048965d1762abd534c9d53bae981a035bfe486690ba11f49bbbb"}, + {file = "websockets-15.0-cp39-cp39-win32.whl", hash = "sha256:37d66646f929ae7c22c79bc73ec4074d6db45e6384500ee3e0d476daf55482a9"}, + {file = "websockets-15.0-cp39-cp39-win_amd64.whl", hash = "sha256:24d5333a9b2343330f0f4eb88546e2c32a7f5c280f8dd7d3cc079beb0901781b"}, + {file = "websockets-15.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b499caef4bca9cbd0bd23cd3386f5113ee7378094a3cb613a2fa543260fe9506"}, + {file = "websockets-15.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:17f2854c6bd9ee008c4b270f7010fe2da6c16eac5724a175e75010aacd905b31"}, + {file = "websockets-15.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89f72524033abbfde880ad338fd3c2c16e31ae232323ebdfbc745cbb1b3dcc03"}, + {file = "websockets-15.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1657a9eecb29d7838e3b415458cc494e6d1b194f7ac73a34aa55c6fb6c72d1f3"}, + {file = "websockets-15.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e413352a921f5ad5d66f9e2869b977e88d5103fc528b6deb8423028a2befd842"}, + {file = "websockets-15.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8561c48b0090993e3b2a54db480cab1d23eb2c5735067213bb90f402806339f5"}, + {file = "websockets-15.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:190bc6ef8690cd88232a038d1b15714c258f79653abad62f7048249b09438af3"}, + {file = "websockets-15.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:327adab7671f3726b0ba69be9e865bba23b37a605b585e65895c428f6e47e766"}, + {file = "websockets-15.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bd8ef197c87afe0a9009f7a28b5dc613bfc585d329f80b7af404e766aa9e8c7"}, + {file = "websockets-15.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:789c43bf4a10cd067c24c321238e800b8b2716c863ddb2294d2fed886fa5a689"}, + {file = "websockets-15.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7394c0b7d460569c9285fa089a429f58465db930012566c03046f9e3ab0ed181"}, + {file = "websockets-15.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ea4f210422b912ebe58ef0ad33088bc8e5c5ff9655a8822500690abc3b1232d"}, + {file = "websockets-15.0-py3-none-any.whl", hash = "sha256:51ffd53c53c4442415b613497a34ba0aa7b99ac07f1e4a62db5dcd640ae6c3c3"}, + {file = "websockets-15.0.tar.gz", hash = "sha256:ca36151289a15b39d8d683fd8b7abbe26fc50be311066c5f8dcf3cb8cee107ab"}, ] [[package]] @@ -3126,4 +3126,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "db7a5a114c4c2503f6c3251ac229dcb7edda47f99dde22d7e802c6f45d69690b" +content-hash = "e6b6919176db6201af1b03257ce4579d117a9768d6415545ef8d0dce602760a3" diff --git a/pyproject.toml b/pyproject.toml index 1b4f203a5f..fa3549baec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ pycryptodome = "^3.17" idna = "^3.4" ansible = ">=7.3,<9.0" tabulate = "0.8.10" -websockets = ">=11.0.2,<15.0.0" +websockets = ">=11.0.2,<16.0.0" pyjwt = "^2.7.0" beautifulsoup4 = "^4.12.2" lxml = ">=4.9.2,<6.0.0" From 4be278b87c712d7b83085fbc7e7acfcbef06c6a3 Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Mon, 17 Feb 2025 13:47:35 -0500 Subject: [PATCH 74/79] bump badsecrets 0.9 --- bbot/modules/badsecrets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/modules/badsecrets.py b/bbot/modules/badsecrets.py index dd5784ca29..5fff3b9e96 100644 --- a/bbot/modules/badsecrets.py +++ b/bbot/modules/badsecrets.py @@ -17,7 +17,7 @@ class badsecrets(BaseModule): options_desc = { "custom_secrets": "Include custom secrets loaded from a local file", } - deps_pip = ["badsecrets~=0.6.21"] + deps_pip = ["badsecrets~=0.9.28"] async def setup(self): self.custom_secrets = None From 609fa50dfa9bed6b70489948524e1bc40c7be99b Mon Sep 17 00:00:00 2001 From: liquidsec <paul.mueller08@gmail.com> Date: Mon, 17 Feb 2025 15:31:31 -0500 Subject: [PATCH 75/79] version --- bbot/modules/badsecrets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbot/modules/badsecrets.py b/bbot/modules/badsecrets.py index 5fff3b9e96..c7ec6e9761 100644 --- a/bbot/modules/badsecrets.py +++ b/bbot/modules/badsecrets.py @@ -17,7 +17,7 @@ class badsecrets(BaseModule): options_desc = { "custom_secrets": "Include custom secrets loaded from a local file", } - deps_pip = ["badsecrets~=0.9.28"] + deps_pip = ["badsecrets~=0.9.29"] async def setup(self): self.custom_secrets = None From 40ea06d12e86c38cc407a455b95281f6a304ab69 Mon Sep 17 00:00:00 2001 From: TheTechromancer <20261699+TheTechromancer@users.noreply.github.com> Date: Wed, 19 Feb 2025 02:44:52 +0000 Subject: [PATCH 76/79] [create-pull-request] automated change --- docs/modules/nuclei.md | 2 +- docs/scanning/configuration.md | 8 +++++--- docs/scanning/presets_list.md | 30 ++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/docs/modules/nuclei.md b/docs/modules/nuclei.md index 03bec7e2e8..e9cb66c0d9 100644 --- a/docs/modules/nuclei.md +++ b/docs/modules/nuclei.md @@ -51,7 +51,7 @@ The Nuclei module has many configuration options: | modules.nuclei.silent | bool | Don't display nuclei's banner or status messages | False | | modules.nuclei.tags | str | execute a subset of templates that contain the provided tags | | | modules.nuclei.templates | str | template or template directory paths to include in the scan | | -| modules.nuclei.version | str | nuclei version | 3.3.8 | +| modules.nuclei.version | str | nuclei version | 3.3.9 | <!-- END BBOT MODULE OPTIONS NUCLEI --> Most of these you probably will **NOT** want to change. In particular, we advise against changing the version of Nuclei, as it's possible the latest version won't work right with BBOT. diff --git a/docs/scanning/configuration.md b/docs/scanning/configuration.md index 2417d628ae..a58cba809c 100644 --- a/docs/scanning/configuration.md +++ b/docs/scanning/configuration.md @@ -322,23 +322,25 @@ Many modules accept their own configuration options. These options have the abil | modules.dnsbrute_mutations.max_mutations | int | Maximum number of target-specific mutations to try per subdomain | 100 | | modules.dnscommonsrv.max_depth | int | The maximum subdomain depth to brute-force SRV records | 2 | | modules.ffuf.extensions | str | Optionally include a list of extensions to extend the keyword with (comma separated) | | +| modules.ffuf.ignore_case | bool | Only put lowercase words into the wordlist | False | | modules.ffuf.lines | int | take only the first N lines from the wordlist when finding directories | 5000 | | modules.ffuf.max_depth | int | the maximum directory depth to attempt to solve | 0 | | modules.ffuf.wordlist | str | Specify wordlist to use when finding directories | https://raw.githubusercontent.com/danielmiessler/SecLists/master/Discovery/Web-Content/raft-small-directories.txt | | modules.ffuf_shortnames.extensions | str | Optionally include a list of extensions to extend the keyword with (comma separated) | | | modules.ffuf_shortnames.find_common_prefixes | bool | Attempt to automatically detect common prefixes and make additional ffuf runs against them | False | | modules.ffuf_shortnames.find_delimiters | bool | Attempt to detect common delimiters and make additional ffuf runs against them | True | +| modules.ffuf_shortnames.find_subwords | bool | Attempt to detect subwords and make additional ffuf runs against them | False | | modules.ffuf_shortnames.ignore_redirects | bool | Explicitly ignore redirects (301,302) | True | | modules.ffuf_shortnames.max_depth | int | the maximum directory depth to attempt to solve | 1 | | modules.ffuf_shortnames.max_predictions | int | The maximum number of predictions to generate per shortname prefix | 250 | | modules.ffuf_shortnames.version | str | ffuf version | 2.0.0 | -| modules.ffuf_shortnames.wordlist | str | Specify wordlist to use when finding directories | | | modules.ffuf_shortnames.wordlist_extensions | str | Specify wordlist to use when making extension lists | | | modules.filedownload.base_64_encoded_file | str | Stream the bytes of a file and encode them in base 64 for event data. | false | | modules.filedownload.extensions | list | File extensions to download | ['bak', 'bash', 'bashrc', 'cfg', 'conf', 'crt', 'csv', 'db', 'dll', 'doc', 'docx', 'exe', 'ica', 'indd', 'ini', 'jar', 'json', 'key', 'log', 'markdown', 'md', 'msi', 'odg', 'odp', 'ods', 'odt', 'pdf', 'pem', 'pps', 'ppsx', 'ppt', 'pptx', 'ps1', 'pub', 'raw', 'rdp', 'rsa', 'sh', 'sql', 'sqlite', 'swp', 'sxw', 'tar.gz', 'tgz', 'tar', 'txt', 'vbs', 'war', 'wpd', 'xls', 'xlsx', 'xml', 'yaml', 'yml', 'zip', 'lzma', 'rar', '7z', 'xz', 'bz2'] | | modules.filedownload.max_filesize | str | Cancel download if filesize is greater than this size | 10MB | | modules.fingerprintx.skip_common_web | bool | Skip common web ports such as 80, 443, 8080, 8443, etc. | True | | modules.fingerprintx.version | str | fingerprintx version | 1.1.4 | +| modules.generic_ssrf.skip_dns_interaction | bool | Do not report DNS interactions (only HTTP interaction) | False | | modules.gitlab.api_key | str | Gitlab access token | | | modules.gowitness.idle_timeout | int | Skip the current gowitness batch if it stalls for longer than this many seconds | 1800 | | modules.gowitness.output_path | str | Where to save screenshots | | @@ -370,7 +372,7 @@ Many modules accept their own configuration options. These options have the abil | modules.nuclei.silent | bool | Don't display nuclei's banner or status messages | False | | modules.nuclei.tags | str | execute a subset of templates that contain the provided tags | | | modules.nuclei.templates | str | template or template directory paths to include in the scan | | -| modules.nuclei.version | str | nuclei version | 3.3.8 | +| modules.nuclei.version | str | nuclei version | 3.3.9 | | modules.oauth.try_all | bool | Check for OAUTH/IODC on every subdomain and URL. | False | | modules.paramminer_cookies.recycle_words | bool | Attempt to use words found during the scan on all other endpoints | False | | modules.paramminer_cookies.skip_boring_words | bool | Remove commonly uninteresting words from the wordlist | True | @@ -481,7 +483,7 @@ Many modules accept their own configuration options. These options have the abil | modules.trufflehog.config | str | File path or URL to YAML trufflehog config | | | modules.trufflehog.deleted_forks | bool | Scan for deleted github forks. WARNING: This is SLOW. For a smaller repository, this process can take 20 minutes. For a larger repository, it could take hours. | False | | modules.trufflehog.only_verified | bool | Only report credentials that have been verified | True | -| modules.trufflehog.version | str | trufflehog version | 3.88.2 | +| modules.trufflehog.version | str | trufflehog version | 3.88.9 | | modules.urlscan.urls | bool | Emit URLs in addition to DNS_NAMEs | False | | modules.virustotal.api_key | str | VirusTotal API Key | | | modules.wayback.garbage_threshold | int | Dedupe similar urls if they are in a group of this size or higher (lower values == less garbage data) | 10 | diff --git a/docs/scanning/presets_list.md b/docs/scanning/presets_list.md index 4f8df91f09..4f7544fc76 100644 --- a/docs/scanning/presets_list.md +++ b/docs/scanning/presets_list.md @@ -237,6 +237,9 @@ Comprehensive scan for all IIS/.NET specific modules and module settings modules: ffuf: extensions: asp,aspx,ashx,asmx,ascx + extensions_ignore_case: True + ffuf_shortnames: + find_subwords: True telerik: exploit_RAU_crypto: True include_subdirs: True # Run against every directory, not the default first received URL per-host @@ -271,6 +274,9 @@ Comprehensive scan for all IIS/.NET specific modules and module settings modules: ffuf: extensions: asp,aspx,ashx,asmx,ascx + extensions_ignore_case: True + ffuf_shortnames: + find_subwords: True telerik: exploit_RAU_crypto: True include_subdirs: True # Run against every directory, not the default first received URL per-host @@ -841,6 +847,29 @@ Enumerate subdomains via APIs, brute-force Modules: [52]("`anubisdb`, `asn`, `azure_realm`, `azure_tenant`, `baddns_direct`, `baddns_zone`, `bevigil`, `binaryedge`, `bufferoverrun`, `builtwith`, `c99`, `censys`, `certspotter`, `chaos`, `crt`, `digitorus`, `dnsbimi`, `dnsbrute_mutations`, `dnsbrute`, `dnscaa`, `dnscommonsrv`, `dnsdumpster`, `dnstlsrpt`, `fullhunt`, `github_codesearch`, `github_org`, `hackertarget`, `httpx`, `hunterio`, `internetdb`, `ipneighbor`, `leakix`, `myssl`, `oauth`, `otx`, `passivetotal`, `postman_download`, `postman`, `rapiddns`, `securitytrails`, `securitytxt`, `shodan_dns`, `sitedossier`, `social`, `sslcert`, `subdomaincenter`, `subdomainradar`, `trickest`, `urlscan`, `virustotal`, `wayback`, `zoomeye`") +## **tech-detect** + +Detect technologies via Wappalyzer, Nuclei, and FingerprintX + +??? note "`tech-detect.yml`" + ```yaml title="~/.bbot/presets/tech-detect.yml" + description: Detect technologies via Wappalyzer, Nuclei, and FingerprintX + + modules: + - nuclei + - wappalyzer + - fingerprintx + + config: + modules: + nuclei: + tags: tech + ``` + + + +Modules: [4]("`fingerprintx`, `httpx`, `nuclei`, `wappalyzer`") + ## **web-basic** Quick web scan @@ -941,6 +970,7 @@ Here is a the same data, but in a table: | spider | | Recursive web spider | 1 | httpx | | spider-intense | | Recursive web spider with more aggressive settings | 1 | httpx | | subdomain-enum | | Enumerate subdomains via APIs, brute-force | 52 | anubisdb, asn, azure_realm, azure_tenant, baddns_direct, baddns_zone, bevigil, binaryedge, bufferoverrun, builtwith, c99, censys, certspotter, chaos, crt, digitorus, dnsbimi, dnsbrute, dnsbrute_mutations, dnscaa, dnscommonsrv, dnsdumpster, dnstlsrpt, fullhunt, github_codesearch, github_org, hackertarget, httpx, hunterio, internetdb, ipneighbor, leakix, myssl, oauth, otx, passivetotal, postman, postman_download, rapiddns, securitytrails, securitytxt, shodan_dns, sitedossier, social, sslcert, subdomaincenter, subdomainradar, trickest, urlscan, virustotal, wayback, zoomeye | +| tech-detect | | Detect technologies via Wappalyzer, Nuclei, and FingerprintX | 4 | fingerprintx, httpx, nuclei, wappalyzer | | web-basic | | Quick web scan | 18 | azure_realm, baddns, badsecrets, bucket_amazon, bucket_azure, bucket_firebase, bucket_google, ffuf_shortnames, filedownload, git, httpx, iis_shortnames, ntlm, oauth, robots, securitytxt, sslcert, wappalyzer | | web-screenshots | | Take screenshots of webpages | 3 | gowitness, httpx, social | | web-thorough | | Aggressive web scan | 29 | ajaxpro, azure_realm, baddns, badsecrets, bucket_amazon, bucket_azure, bucket_digitalocean, bucket_firebase, bucket_google, bypass403, dastardly, dotnetnuke, ffuf_shortnames, filedownload, generic_ssrf, git, host_header, httpx, hunt, iis_shortnames, ntlm, oauth, robots, securitytxt, smuggler, sslcert, telerik, url_manipulation, wappalyzer | From 1903dc9fe5e5bb570ca99fbfb736d64910e96920 Mon Sep 17 00:00:00 2001 From: github-actions <github-actions@github.com> Date: Wed, 19 Feb 2025 10:15:32 -0500 Subject: [PATCH 77/79] request error handling, remove git dependency, add some branch names --- bbot/core/helpers/web/client.py | 1 + bbot/modules/gitdumper.py | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/bbot/core/helpers/web/client.py b/bbot/core/helpers/web/client.py index 28788e04d9..737a2f9dcb 100644 --- a/bbot/core/helpers/web/client.py +++ b/bbot/core/helpers/web/client.py @@ -83,6 +83,7 @@ def build_request(self, *args, **kwargs): # TODO: re-enable this if self._target.in_scope(str(request.url)): for hk, hv in self._web_config.get("http_headers", {}).items(): + hv = str(hv) # don't clobber headers if hk not in request.headers: request.headers[hk] = hv diff --git a/bbot/modules/gitdumper.py b/bbot/modules/gitdumper.py index 76f089b9c8..ae977b2583 100644 --- a/bbot/modules/gitdumper.py +++ b/bbot/modules/gitdumper.py @@ -24,8 +24,6 @@ class gitdumper(BaseModule): "max_semanic_version": "Maximum version number to fuzz for (default < v10.10.10)", } - deps_apt = ["git"] - scope_distance_modifier = 2 async def setup(self): @@ -57,19 +55,28 @@ async def setup(self): ] self.info("Compiling fuzz list with common branch names") branch_names = [ + "bugfix", "daily", - "stable", "dev", - "feature", + "develop", + "development", "feat", + "feature", "fix", "hotfix", + "integration", "issue", "main", "master", "ng", + "prod", + "production", + "qa", "quickfix", "release", + "stable", + "stage", + "staging", "test", "testing", "wip", @@ -130,7 +137,7 @@ async def handle_event(self, event): async def directory_listing_enabled(self, repo_url): response = await self.helpers.request(repo_url) - if "<title>Index of" in response.text: + if "<title>Index of" in getattr(response, "text", ""): self.info(f"Directory listing enabled at {repo_url}") return response return None @@ -146,7 +153,7 @@ async def recursive_dir_list(self, dir_listing): if href.endswith("/"): folder_url = self.helpers.urljoin(str(dir_listing.url), href) response = await self.helpers.request(folder_url) - if response.status_code == 200: + if getattr(response, "status_code", 0) == 200: file_list.extend(await self.recursive_dir_list(response)) else: file_url = self.helpers.urljoin(str(dir_listing.url), href) @@ -240,7 +247,7 @@ async def sanitize_config(self, folder): if config_file.exists(): with config_file.open("r", encoding="utf-8", errors="ignore") as file: content = file.read() - sanitized = await self.helpers.re.sub(self.unsafe_regex, "# \g<0>", content) + sanitized = await self.helpers.re.sub(self.unsafe_regex, r"# \g<0>", content) with config_file.open("w", encoding="utf-8") as file: file.write(sanitized) From 22da6076ca272e0b030db731d327ac987f2880a6 Mon Sep 17 00:00:00 2001 From: github-actions <github-actions@github.com> Date: Thu, 20 Feb 2025 13:32:39 -0500 Subject: [PATCH 78/79] log failed deps --- bbot/cli.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bbot/cli.py b/bbot/cli.py index 761fcbd5ff..7f1e91c72e 100755 --- a/bbot/cli.py +++ b/bbot/cli.py @@ -161,8 +161,11 @@ async def _main(): all_modules = list(preset.module_loader.preloaded()) scan.helpers.depsinstaller.force_deps = True succeeded, failed = await scan.helpers.depsinstaller.install(*all_modules) - log.info("Finished installing module dependencies") - return False if failed else True + if failed: + log.hugewarning(f"Failed to install dependencies for the following modules: {', '.join(failed)}") + return False + log.hugesuccess(f"Successfully installed dependencies for the following modules: {', '.join(succeeded)}") + return True scan_name = str(scan.name) From c12aa159675db7ae67280bab4043b4139c9096ae Mon Sep 17 00:00:00 2001 From: github-actions <github-actions@github.com> Date: Fri, 21 Feb 2025 10:41:22 -0500 Subject: [PATCH 79/79] install ansible collection community.general --- bbot/core/helpers/depsinstaller/installer.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/bbot/core/helpers/depsinstaller/installer.py b/bbot/core/helpers/depsinstaller/installer.py index aa7528ca64..5bea5f508a 100644 --- a/bbot/core/helpers/depsinstaller/installer.py +++ b/bbot/core/helpers/depsinstaller/installer.py @@ -96,7 +96,7 @@ def __init__(self, parent_helper): self.ensure_root_lock = Lock() async def install(self, *modules): - self.install_core_deps() + await self.install_core_deps() succeeded = [] failed = [] try: @@ -386,13 +386,14 @@ def ensure_root(self, message=""): else: log.warning("Incorrect password") - def install_core_deps(self): + async def install_core_deps(self): to_install = set() to_install_friendly = set() playbook = [] self._install_sudo_askpass() # ensure tldextract data is cached self.parent_helper.tldextract("evilcorp.co.uk") + # install any missing commands for command, package_name_or_playbook in self.CORE_DEPS.items(): if not self.parent_helper.which(command): to_install_friendly.add(command) @@ -400,6 +401,19 @@ def install_core_deps(self): to_install.add(package_name_or_playbook) else: playbook.extend(package_name_or_playbook) + # install ansible community.general collection + if not self.setup_status.get("ansible:community.general", False): + log.info("Installing Ansible Community General Collection") + try: + command = ["ansible-galaxy", "collection", "install", "community.general"] + await self.parent_helper.run(command, check=True) + self.setup_status["ansible:community.general"] = True + log.info("Successfully installed Ansible Community General Collection") + except CalledProcessError as err: + log.warning( + f"Failed to install Ansible Community.General Collection (return code {err.returncode}): {err.stderr}" + ) + # construct ansible playbook if to_install: playbook.append( { @@ -408,6 +422,7 @@ def install_core_deps(self): "become": True, } ) + # run playbook if playbook: log.info(f"Installing core BBOT dependencies: {','.join(sorted(to_install_friendly))}") self.ensure_root()