diff --git a/agent/agent.py b/agent/agent.py index 1b0947e49d7..fac90960cba 100644 --- a/agent/agent.py +++ b/agent/agent.py @@ -4,7 +4,6 @@ import argparse import base64 -import cgi import enum import http.server import ipaddress @@ -23,9 +22,12 @@ import tempfile import time import traceback -from io import StringIO +from email.parser import BytesParser +from email.policy import default as email_policy +from io import BytesIO, StringIO from threading import Lock from typing import Iterable +from urllib.parse import parse_qs from zipfile import ZipFile try: @@ -40,10 +42,10 @@ # The analysis process interacts with low-level Windows libraries that need a # x86 Python to be running. # (see https://github.com/kevoreilly/CAPEv2/issues/1680) -if sys.maxsize > 2**32 and sys.platform == "win32": - sys.exit("You should install python3 x86! not x64") +#if sys.maxsize > 2**32 and sys.platform == "win32": +# sys.exit("You should install python3 x86! not x64") -AGENT_VERSION = "0.20" +AGENT_VERSION = "0.21" AGENT_FEATURES = [ "execpy", "execute", @@ -110,6 +112,37 @@ def _missing_(cls, value): class MiniHTTPRequestHandler(http.server.SimpleHTTPRequestHandler): server_version = "CAPE Agent" + def _parse_form_and_files(self): + content_length = int(self.headers.get("Content-Length", "0") or 0) + content_type = self.headers.get("Content-Type", "") + media_type = content_type.split(";", 1)[0].strip().lower() + body = self.rfile.read(content_length) if content_length > 0 else b"" + + form = {} + files = {} + + if media_type == "multipart/form-data": + message = BytesParser(policy=email_policy).parsebytes( + b"Content-Type: " + content_type.encode("utf-8") + b"\r\n\r\n" + body + ) + for part in message.iter_parts(): + name = part.get_param("name", header="content-disposition") + if not name: + continue + filename = part.get_filename() + payload = part.get_payload(decode=True) or b"" + if filename: + files[name] = BytesIO(payload) + else: + charset = part.get_content_charset("utf-8") + form[name] = payload.decode(charset, errors="replace") + elif media_type == "application/x-www-form-urlencoded": + # Match cgi.FieldStorage default behavior: ignore blank form values. + parsed = parse_qs(body.decode("utf-8", errors="replace"), keep_blank_values=False) + form = {k: v[-1] if v else "" for k, v in parsed.items()} + + return form, files + def do_GET(self): request.client_ip, request.client_port = self.client_address request.form = {} @@ -119,47 +152,17 @@ def do_GET(self): self.httpd.handle(self) def do_POST(self): - environ = { - "REQUEST_METHOD": "POST", - "CONTENT_TYPE": self.headers.get("Content-Type"), - } - - form = cgi.FieldStorage(fp=self.rfile, headers=self.headers, environ=environ) - request.client_ip, request.client_port = self.client_address - request.form = {} - request.files = {} + request.form, request.files = self._parse_form_and_files() request.method = "POST" - if form.list: - for key in form.keys(): - value = form[key] - if value.filename: - request.files[key] = value.file - else: - request.form[key] = value.value self.httpd.handle(self) def do_DELETE(self): - environ = { - "REQUEST_METHOD": "DELETE", - "CONTENT_TYPE": self.headers.get("Content-Type"), - } - - form = cgi.FieldStorage(fp=self.rfile, headers=self.headers, environ=environ) - request.client_ip, request.client_port = self.client_address - request.form = {} - request.files = {} + request.form, request.files = self._parse_form_and_files() request.method = "DELETE" - if form.list: - for key in form.keys(): - value = form[key] - if value.filename: - request.files[key] = value.file - else: - request.form[key] = value.value self.httpd.handle(self) diff --git a/agent/test_python_architecture.py b/agent/test_python_architecture.py index 9548defe628..210817bfd5e 100644 --- a/agent/test_python_architecture.py +++ b/agent/test_python_architecture.py @@ -5,17 +5,6 @@ import pytest -def test_32_bit(monkeypatch): - with monkeypatch.context() as m: - # Unload "agent" module if previously imported. - sys.modules.pop("agent", None) - m.setattr(sys, "maxsize", 2**64) - m.setattr(sys, "platform", "win32") - with pytest.raises(SystemExit): - # Should raise an exception. - import agent # noqa: F401 - - def test_python_version(monkeypatch): with monkeypatch.context() as m: # Unload "agent" module if previously imported. diff --git a/analyzer/windows/analyzer.py b/analyzer/windows/analyzer.py index e8c1e094fe2..dd7a895757a 100644 --- a/analyzer/windows/analyzer.py +++ b/analyzer/windows/analyzer.py @@ -17,7 +17,7 @@ import timeit import traceback from contextlib import suppress -from ctypes import byref, c_buffer, c_int, create_string_buffer, sizeof, wintypes +from ctypes import byref, c_buffer, c_int, c_void_p, create_string_buffer, sizeof, wintypes from pathlib import Path from shutil import copy from threading import Lock, Thread @@ -50,6 +50,35 @@ SHELL32, USER32, ) + +KERNEL32.OpenProcess.restype = c_void_p +KERNEL32.OpenProcess.argtypes = [wintypes.DWORD, wintypes.BOOL, wintypes.DWORD] +KERNEL32.CreateMutexA.restype = c_void_p +KERNEL32.OpenEventA.restype = c_void_p +ADVAPI32.OpenSCManagerA.restype = c_void_p +ADVAPI32.OpenSCManagerA.argtypes = [wintypes.LPCSTR, wintypes.LPCSTR, wintypes.DWORD] +ADVAPI32.OpenServiceW.restype = c_void_p +ADVAPI32.OpenServiceW.argtypes = [c_void_p, wintypes.LPCWSTR, wintypes.DWORD] +ADVAPI32.QueryServiceStatusEx.argtypes = [c_void_p, c_int, c_void_p, wintypes.DWORD, c_void_p] +ADVAPI32.QueryServiceStatusEx.restype = wintypes.BOOL +ADVAPI32.CloseServiceHandle.argtypes = [c_void_p] +ADVAPI32.CloseServiceHandle.restype = wintypes.BOOL +USER32.GetShellWindow.restype = c_void_p +USER32.GetWindowThreadProcessId.argtypes = [c_void_p, c_void_p] +USER32.GetWindowThreadProcessId.restype = wintypes.DWORD +KERNEL32.OpenThread.restype = c_void_p +KERNEL32.CreateToolhelp32Snapshot.restype = c_void_p +KERNEL32.CreateFileW.restype = c_void_p +KERNEL32.CreateEventW.restype = c_void_p +KERNEL32.OpenEventW.restype = c_void_p +KERNEL32.GetCurrentProcess.restype = c_void_p +KERNEL32.CloseHandle.argtypes = [c_void_p] +KERNEL32.CloseHandle.restype = wintypes.BOOL +PSAPI.EnumProcesses.argtypes = [c_void_p, wintypes.DWORD, c_void_p] +PSAPI.EnumProcesses.restype = wintypes.BOOL +PSAPI.GetProcessImageFileNameA.argtypes = [c_void_p, c_void_p, wintypes.DWORD] +PSAPI.GetProcessImageFileNameA.restype = wintypes.DWORD + from lib.common.exceptions import CuckooError, CuckooPackageError from lib.common.hashing import hash_file from lib.common.results import upload_to_host @@ -119,7 +148,7 @@ def pids_from_image_names(suffixlist): log.debug("psapi.EnumProcesses failed") return retpids - suffixlist = tuple([x.lower() for x in suffixlist]) + suffixlist = tuple(x.lower() for x in suffixlist) num_processes = int(num_bytes.value / sizeof(wintypes.DWORD)) pids = lpid_process_ptr[:num_processes] @@ -474,51 +503,36 @@ def run(self): self.target = self.package.move_curdir(self.target) log.debug("New location of moved file: %s", self.target) - # Set the DLL to that specified by package - if self.package.options.get("dll") is not None: - MONITOR_DLL = self.package.options["dll"] - log.info("Analyzer: DLL set to %s from package %s", MONITOR_DLL, self.package_name) - else: - log.info("Analyzer: Package %s does not specify a DLL option", self.package_name) - - # Set the DLL_64 to that specified by package - if self.package.options.get("dll_64") is not None: - MONITOR_DLL_64 = self.package.options["dll_64"] - log.info("Analyzer: DLL_64 set to %s from package %s", MONITOR_DLL_64, self.package_name) - else: - log.info("Analyzer: Package %s does not specify a DLL_64 option", self.package_name) - - # Set the loader to that specified by package - if self.package.options.get("loader") is not None: - LOADER32 = self.package.options["loader"] - log.info("Analyzer: Loader set to %s from package %s", LOADER32, self.package_name) - else: - log.info("Analyzer: Package %s does not specify a loader option", self.package_name) - - # Set the loader_64 to that specified by package - if self.package.options.get("loader_64") is not None: - LOADER64 = self.package.options["loader_64"] - log.info("Analyzer: Loader_64 set to %s from package %s", LOADER64, self.package_name) - else: - log.info("Analyzer: Package %s does not specify a loader_64 option", self.package_name) + # Set the DLL/loader to that specified by package + for key in ("dll", "dll_64", "loader", "loader_64"): + if (value := self.package.options.get(key)) is not None: + log.info("Analyzer: %s set to %s from package %s", key, value, self.package_name) + if key == "dll": + MONITOR_DLL = value + elif key == "dll_64": + MONITOR_DLL_64 = value + elif key == "loader": + LOADER32 = value + elif key == "loader_64": + LOADER64 = value + else: + log.info("Analyzer: Package %s does not specify a %s option", self.package_name, key) # randomize monitor DLL and loader executable names - if MONITOR_DLL is not None: - copy(os.path.join("dll", MONITOR_DLL), CAPEMON32_NAME) - else: - copy("dll\\capemon.dll", CAPEMON32_NAME) - if MONITOR_DLL_64 is not None: - copy(os.path.join("dll", MONITOR_DLL_64), CAPEMON64_NAME) - else: - copy("dll\\capemon_x64.dll", CAPEMON64_NAME) - if LOADER32 is not None: - copy(os.path.join("bin", LOADER32), LOADER32_NAME) - else: - copy("bin\\loader.exe", LOADER32_NAME) - if LOADER64 is not None: - copy(os.path.join("bin", LOADER64), LOADER64_NAME) - else: - copy("bin\\loader_x64.exe", LOADER64_NAME) + for source_name, dest_name, default_name, source_dir in [ + (MONITOR_DLL, CAPEMON32_NAME, "capemon.dll", "dll"), + (MONITOR_DLL_64, CAPEMON64_NAME, "capemon_x64.dll", "dll"), + (LOADER32, LOADER32_NAME, "loader.exe", "bin"), + (LOADER64, LOADER64_NAME, "loader_x64.exe", "bin"), + ]: + if source_name is not None: + if os.path.basename(source_name) != source_name: + log.warning("Path traversal attempt detected in source_name: '%s'", source_name) + return + source_path = os.path.join(source_dir, source_name) + else: + source_path = os.path.join(source_dir, default_name) + copy(source_path, dest_name) si = subprocess.STARTUPINFO() # STARTF_USESHOWWINDOW @@ -899,6 +913,8 @@ class Files: "vmtoolsd.exe", "vmsrvc.exe", "python.exe", + "pythonw.exe", + "python3.exe", "perl.exe", ] @@ -978,7 +994,7 @@ def dump_file(self, filepath, metadata="", pids="", ppids="", category="files"): log.exception(e) def delete_file(self, filepath, pid=None): - """A file is about to removed and thus should be dumped right away.""" + """A file is about to be removed and thus should be dumped right away.""" self.add_pid(filepath, pid) self.dump_file(filepath) @@ -1026,7 +1042,7 @@ def add_pids(self, pids): self.add_pid(pids) def has_pid(self, pid, notrack=True): - """Return whether or not this process identifier being tracked.""" + """Return whether this process identifier being tracked.""" pid = int(pid) if pid in self.pids: return True @@ -1385,8 +1401,7 @@ def _inject_process(self, process_id, thread_id, mode): # Add the new process ID to the list of monitored processes. self.analyzer.process_list.add_pid(process_id) - # We're done operating on the processes list, - # release the lock + # We're done operating on the processes list, release the lock self.analyzer.process_lock.release() proc.inject(interest=filepath, nosleepskip=True) @@ -1397,7 +1412,6 @@ def _handle_process(self, data): """Request for injection into a process.""" # Parse the process identifier. # PROCESS:1:1824,2856 - process_id = thread_id = None # We parse the process ID. pid_s, tid_s = data.split(b",", 1) process_id = int(pid_s) @@ -1541,6 +1555,7 @@ def _handle_file_move(self, data): if b"::" not in data: log.warning("Received FILE_MOVE command from monitor with an incorrect argument") return + pid, paths = data.split(b",", 1) old_filepath, new_filepath = paths.split(b"::", 1) self.analyzer.files.move_file(old_filepath.decode(), new_filepath.decode(), pid.decode()) diff --git a/analyzer/windows/lib/api/process.py b/analyzer/windows/lib/api/process.py index 4463e9472d3..1a0a541379a 100644 --- a/analyzer/windows/lib/api/process.py +++ b/analyzer/windows/lib/api/process.py @@ -13,76 +13,138 @@ import urllib.error import urllib.parse import urllib.request -from ctypes import byref, c_buffer, c_int, c_ulong, create_string_buffer, sizeof, ArgumentError +from ctypes import ( + ArgumentError, + Array, + POINTER, + byref, + c_buffer, + c_char, + c_int, + c_ulong, + c_void_p, + cast, + create_string_buffer, + create_unicode_buffer, + sizeof, + string_at, + windll, +) +from ctypes.wintypes import BOOL, DWORD, HANDLE, LPCWSTR, LPVOID, LPWSTR from pathlib import Path from shutil import copy - +from typing import Tuple + +from lib.common.constants import ( + CAPEMON32_NAME, + CAPEMON64_NAME, + LOADER32_NAME, + LOADER64_NAME, + LOGSERVER_PREFIX, + PATHS, + PIPE, + SHUTDOWN_MUTEX, + SIDELOADER32_NAME, + SIDELOADER64_NAME, + TERMINATE_EVENT, + TTD32_NAME, + TTD64_NAME, +) +from lib.common.constants import OPT_CURDIR, OPT_EXECUTIONDIR from lib.common.defines import ( + ADVAPI32, CREATE_NEW_CONSOLE, CREATE_SUSPENDED, + ERROR_INSUFFICIENT_BUFFER, EVENT_MODIFY_STATE, + EXTENDED_STARTUPINFO_PRESENT, GENERIC_READ, GENERIC_WRITE, + KERNEL32, + LUID, MAX_PATH, + NTDLL, OPEN_EXISTING, + PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, PROCESS_ALL_ACCESS, + PROCESS_BASIC_INFORMATION, + PROCESS_CREATE_PROCESS, PROCESS_INFORMATION, PROCESS_QUERY_LIMITED_INFORMATION, PROCESSENTRY32, + PSAPI, + SIZE_T, STARTUPINFO, + STARTUPINFOEXW, STILL_ACTIVE, SYSTEM_INFO, + TOKEN_PRIVILEGES, TH32CS_SNAPPROCESS, THREAD_ALL_ACCESS, - ULONG_PTR, + UNICODE_STRING, ) - -if sys.platform == "win32": - from ctypes import windll - from lib.common.constants import ( - CAPEMON32_NAME, - CAPEMON64_NAME, - LOADER32_NAME, - LOADER64_NAME, - LOGSERVER_PREFIX, - PATHS, - PIPE, - SHUTDOWN_MUTEX, - TERMINATE_EVENT, - TTD32_NAME, - TTD64_NAME, - SIDELOADER32_NAME, - SIDELOADER64_NAME, - ) - from lib.common.defines import ( - KERNEL32, - NTDLL, - PSAPI, - ) - from lib.core.log import LogServer - -from lib.common.constants import OPT_CURDIR, OPT_EXECUTIONDIR from lib.common.errors import get_error_string from lib.common.rand import random_string from lib.common.results import upload_to_host from lib.core.compound import create_custom_folders from lib.core.config import Config +from lib.core.log import LogServer # CSIDL constants CSIDL_WINDOWS = 0x0024 CSIDL_SYSTEM = 0x0025 CSIDL_SYSTEMX86 = 0x0029 CSIDL_PROGRAM_FILES = 0x0026 -CSIDL_PROGRAM_FILESX86 = 0x002a +CSIDL_PROGRAM_FILESX86 = 0x002A IOCTL_PID = 0x222008 IOCTL_CUCKOO_PATH = 0x22200C -PATH_KERNEL_DRIVER = "\\\\.\\DriverSSDT" +PATH_KERNEL_DRIVER = "\\\\.\\DriverSSDT" LOGSERVER_POOL = {} log = logging.getLogger(__name__) +# Define function return types +KERNEL32.CloseHandle.argtypes = [HANDLE] +KERNEL32.CloseHandle.restype = BOOL +KERNEL32.CreateFileW.restype = HANDLE +KERNEL32.CreateProcessW.argtypes = [ + LPCWSTR, + LPWSTR, + LPVOID, + LPVOID, + BOOL, + DWORD, + LPVOID, + LPCWSTR, + LPVOID, + POINTER(PROCESS_INFORMATION), +] +KERNEL32.CreateProcessW.restype = BOOL +KERNEL32.DeleteProcThreadAttributeList.argtypes = [LPVOID] +KERNEL32.DeleteProcThreadAttributeList.restype = None +KERNEL32.GetCurrentProcess.argtypes = [] +KERNEL32.GetCurrentProcess.restype = HANDLE +KERNEL32.GetLastError.restype = DWORD +KERNEL32.InitializeProcThreadAttributeList.argtypes = [LPVOID, DWORD, DWORD, POINTER(SIZE_T)] +KERNEL32.InitializeProcThreadAttributeList.restype = BOOL +KERNEL32.OpenProcess.argtypes = [DWORD, BOOL, DWORD] +KERNEL32.OpenProcess.restype = HANDLE +KERNEL32.OpenThread.restype = HANDLE +KERNEL32.OpenThread.argtypes = [DWORD, BOOL, DWORD] +KERNEL32.UpdateProcThreadAttribute.argtypes = [LPVOID, DWORD, SIZE_T, LPVOID, SIZE_T, LPVOID, LPVOID] +KERNEL32.UpdateProcThreadAttribute.restype = BOOL + +ADVAPI32.AdjustTokenPrivileges.argtypes = [HANDLE, BOOL, POINTER(TOKEN_PRIVILEGES), DWORD, LPVOID, POINTER(DWORD)] +ADVAPI32.AdjustTokenPrivileges.restype = BOOL +ADVAPI32.OpenProcessToken.argtypes = [HANDLE, DWORD, POINTER(HANDLE)] +ADVAPI32.OpenProcessToken.restype = BOOL +ADVAPI32.LookupPrivilegeValueW.argtypes = [LPCWSTR, LPCWSTR, POINTER(LUID)] +ADVAPI32.LookupPrivilegeValueW.restype = BOOL + +NTDLL.NtQueryInformationProcess.restype = c_int +NTDLL.NtQueryInformationProcess.argtypes = [c_void_p, c_int, c_void_p, c_ulong, POINTER(c_ulong)] def is_os_64bit(): return platform.machine().endswith("64") @@ -118,6 +180,7 @@ def nt_path_to_dos_path_ansi(nt_path: str) -> str: return converted.decode("utf-8", errors="ignore") return nt_path + def NT_SUCCESS(val): return val >= 0 @@ -142,12 +205,13 @@ def __init__(self, options=None, config=None, pid=0, h_process=0, thread_id=0, h self.config = config self.options = options self.pid = pid - self.h_process = h_process + self.h_process = HANDLE(h_process) self.thread_id = thread_id - self.h_thread = h_thread + self.h_thread = HANDLE(h_thread) self.suspended = suspended self.system_info = SYSTEM_INFO() self.critical = False + self.path = None def __del__(self): """Close open handles.""" @@ -234,19 +298,15 @@ def get_filepath(self): if not self.h_process: self.open() - pbi = create_string_buffer(530) - size = c_int() - - # Set return value to signed 32bit integer. - NTDLL.NtQueryInformationProcess.restype = c_int + pbi = create_string_buffer(4096) + size = c_ulong() ret = NTDLL.NtQueryInformationProcess(self.h_process, 27, byref(pbi), sizeof(pbi), byref(size)) - - if NT_SUCCESS(ret) and size.value > 8: + if NT_SUCCESS(ret) and size.value >= sizeof(UNICODE_STRING): try: - fbuf = pbi.raw[8:] - fbuf = fbuf[: fbuf.find(b"\0\0") + 1] - return fbuf.decode("utf16", errors="ignore") + us = UNICODE_STRING.from_buffer_copy(pbi.raw[: sizeof(UNICODE_STRING)]) + if us.Buffer and us.Length: + return string_at(us.Buffer, us.Length).decode("utf-16le", errors="ignore") except Exception as e: log.info(e) @@ -254,9 +314,9 @@ def get_filepath(self): def get_folder_path(self, csidl): """Use SHGetFolderPathW to get the system folder path for a given CSIDL.""" - buf = create_string_buffer(MAX_PATH) - windll.shell32.SHGetFolderPathA(None, csidl, None, 0, buf) - return buf.value.decode('utf-8', errors='ignore') + buf = create_unicode_buffer(MAX_PATH) + windll.shell32.SHGetFolderPathW(None, csidl, None, 0, buf) + return buf.value def get_image_name(self): """Get the image name; returns an empty string on error.""" @@ -295,16 +355,13 @@ def get_parent_pid(self): if not self.h_process: self.open() - pbi = (ULONG_PTR * 6)() + pbi = PROCESS_BASIC_INFORMATION() size = c_ulong() - # Set return value to signed 32bit integer. - NTDLL.NtQueryInformationProcess.restype = c_int - ret = NTDLL.NtQueryInformationProcess(self.h_process, 0, byref(pbi), sizeof(pbi), byref(size)) if NT_SUCCESS(ret) and size.value == sizeof(pbi): - return pbi[5] + return pbi.InheritedFromUniqueProcessId return None @@ -370,7 +427,7 @@ def kernel_analyze(self): sys_file = os.path.join(Path.cwd(), "dll", "zer0m0n.sys") exe_file = os.path.join(Path.cwd(), "dll", "logs_dispatcher.exe") if not os.path.isfile(sys_file) or not os.path.isfile(exe_file): - log.warning("no valid zer0m0n files to be used for %s, injection aborted", self) + log.warning("No valid zer0m0n files to be used for process with pid %d, injection aborted", self.pid) return False exe_name = service_name = driver_name = random_string(6) @@ -470,7 +527,8 @@ def kernel_analyze(self): hFile = KERNEL32.CreateFileW(PATH_KERNEL_DRIVER, GENERIC_READ | GENERIC_WRITE, 0, None, OPEN_EXISTING, 0, None) if os_is_64bit: KERNEL32.Wow64RevertWow64FsRedirection(wow64) - if hFile: + + if hFile and hFile != HANDLE(-1).value: p = Process(pid=os.getpid()) ppid = p.get_parent_pid() pid_vboxservice = 0 @@ -489,7 +547,9 @@ def kernel_analyze(self): elif proc_info.sz_exeFile == "VBoxTray.exe": pid_vboxtray = proc_info.th32ProcessID log.info("VBoxTray.exe found!") + flag = KERNEL32.Process32Next(snapshot, byref(proc_info)) + bytes_returned = c_ulong(0) msg = f"{self.pid}_{ppid}_{os.getpid()}_{pi.dwProcessId}_{pid_vboxservice}_{pid_vboxtray}\0" KERNEL32.DeviceIoControl(hFile, IOCTL_PID, msg, len(msg), None, 0, byref(bytes_returned), None) @@ -500,6 +560,76 @@ def kernel_analyze(self): return True + def build_parent_attribute_list(self) -> Tuple[LPVOID, Array[c_char], HANDLE]: + cb_attribute_list_size = SIZE_T(0) + ok = KERNEL32.InitializeProcThreadAttributeList(None, 1, 0, byref(cb_attribute_list_size)) + last_error = KERNEL32.GetLastError() + if ok or last_error != ERROR_INSUFFICIENT_BUFFER or cb_attribute_list_size.value == 0: + log.error( + "InitializeProcThreadAttributeList(size probe) unexpected result: ok=%s last_error=%d size=%d", + ok, last_error, cb_attribute_list_size.value + ) + + attr_buf = create_string_buffer(cb_attribute_list_size.value) + attr_list = cast(attr_buf, LPVOID) + + if not KERNEL32.InitializeProcThreadAttributeList(attr_list, 1, 0, byref(cb_attribute_list_size)): + log.error("InitializeProcThreadAttributeList(init)") + + log.info("Successfully called InitializeProcThreadAttributeList") + hwnd = windll.user32.GetShellWindow() + explorer_pid = DWORD() + windll.user32.GetWindowThreadProcessId(hwnd, byref(explorer_pid)) + log.info("Explorer PID: %s", explorer_pid.value) + + raw_parent = KERNEL32.OpenProcess(PROCESS_CREATE_PROCESS, False, explorer_pid) + if not raw_parent: + KERNEL32.DeleteProcThreadAttributeList(attr_list) + log.error("OpenProcess") + + h_parent = HANDLE(raw_parent) + if not KERNEL32.UpdateProcThreadAttribute( + attr_list, + 0, + PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, + byref(h_parent), + sizeof(HANDLE), + None, + None, + ): + KERNEL32.CloseHandle(h_parent) + KERNEL32.DeleteProcThreadAttributeList(attr_list) + log.error("UpdateProcThreadAttribute") + + log.info("build_parent_attribute_list returning") + return attr_list, attr_buf, h_parent + + def log_process_tree(self, process_name): + if not process_name: + return + + cmd = [ + "powershell.exe", + "-NoProfile", + "-Command", + f"Get-CimInstance Win32_Process -Filter \"Name='{process_name}'\" | " + "ForEach-Object { " + "$parent = Get-CimInstance Win32_Process -Filter \"ProcessId=$($_.ParentProcessId)\"; " + "[PSCustomObject]@{ " + "ProcessId = $_.ProcessId; " + "Name = $_.Name; " + "ParentProcessId = $_.ParentProcessId; " + "ParentName = $parent.Name " + "} " + "} | Format-Table -AutoSize", + ] + try: + output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, text=True) + if output.strip(): + log.info("%s process info:\n%s", process_name, output.strip()) + except subprocess.CalledProcessError as e: + log.error("Failed to collect %s process info: %s", process_name, e.output) + def execute(self, path, args=None, suspended=False, kernel_analysis=False): """Execute sample process. @param path: sample path. @@ -511,47 +641,51 @@ def execute(self, path, args=None, suspended=False, kernel_analysis=False): log.error('Unable to access file at path "%s", execution aborted', path) return False - startup_info = STARTUPINFO() - startup_info.cb = sizeof(startup_info) + startup_info = STARTUPINFOEXW() + startup_info.StartupInfo.cb = sizeof(STARTUPINFOEXW) + attr_list, attr_buf, h_parent = self.build_parent_attribute_list() + startup_info.lpAttributeList = attr_list # STARTF_USESHOWWINDOW - startup_info.dwFlags = 1 + startup_info.StartupInfo.dwFlags = 1 # SW_SHOWNORMAL - startup_info.wShowWindow = 1 + startup_info.StartupInfo.wShowWindow = 1 process_info = PROCESS_INFORMATION() arguments = f'"{path}" ' if args: arguments += args - creation_flags = CREATE_NEW_CONSOLE + self.path = path + + creation_flags = CREATE_NEW_CONSOLE | EXTENDED_STARTUPINFO_PRESENT if suspended: self.suspended = True creation_flags += CREATE_SUSPENDED # Use the custom execution directory if provided, otherwise launch in the same location # where the sample resides (default %TEMP%) - if OPT_EXECUTIONDIR in self.options.keys(): - execution_directory = self.options[OPT_EXECUTIONDIR] - elif OPT_CURDIR in self.options.keys(): - execution_directory = self.options[OPT_CURDIR] - else: - execution_directory = os.getenv("TEMP") + execution_directory = self.options.get(OPT_EXECUTIONDIR) or self.options.get(OPT_CURDIR) or os.getenv("TEMP") # Try to create the custom directories so that the execution path is deemed valid create_custom_folders(execution_directory) created = KERNEL32.CreateProcessW( - path, arguments, None, None, None, creation_flags, None, execution_directory, byref(startup_info), byref(process_info) + path, arguments, None, None, False, creation_flags, None, execution_directory, byref(startup_info), byref(process_info) ) + KERNEL32.CloseHandle(h_parent) + KERNEL32.DeleteProcThreadAttributeList(attr_list) + if created: self.pid = process_info.dwProcessId self.h_process = process_info.hProcess self.thread_id = process_info.dwThreadId self.h_thread = process_info.hThread log.info('Successfully executed process from path "%s" with arguments "%s" with pid %d', path, args or "", self.pid) + # self.log_process_tree(os.path.basename(path)) if kernel_analysis: return self.kernel_analyze() + return True else: log.error( @@ -567,7 +701,7 @@ def resume(self): @return: operation status. """ if not self.suspended: - log.warning("%s was not suspended at creation", self) + log.warning("The process with pid %d was not suspended at creation", self.pid) return False if not self.h_thread: @@ -577,10 +711,10 @@ def resume(self): if KERNEL32.ResumeThread(self.h_thread) != -1: self.suspended = False - log.info("Successfully resumed %s", self) + log.info("Successfully resumed process with pid %d", self.pid) return True else: - log.error("Failed to resume %s", self) + log.error("Failed to resume process with pid %d", self.pid) return False def ttd_stop(self): @@ -604,6 +738,10 @@ def ttd_stop(self): text=True, timeout=1, ) + if result.stdout: + log.info(" ".join(result.stdout.split())) + if result.stderr: + log.error(" ".join(result.stderr.split())) except subprocess.TimeoutExpired as e: if e.stdout: log.info(" ".join(e.stdout.split())) @@ -612,11 +750,6 @@ def ttd_stop(self): except Exception as e: log.error("Exception attempting TTD stop for %s process with pid %d: %s", bit_str, self.pid, e) - if result.stdout: - log.info(" ".join(result.stdout.split())) - if result.stderr: - log.error(" ".join(result.stderr.split())) - log.info("Stopped TTD for %s process with pid %d", bit_str, self.pid) return True @@ -631,20 +764,20 @@ def set_terminate_event(self): if self.terminate_event_handle: # make sure process is aware of the termination KERNEL32.SetEvent(self.terminate_event_handle) - log.info("Terminate event set for %s", self) + log.info("Terminate event set for process %d", self.pid) KERNEL32.CloseHandle(self.terminate_event_handle) else: - log.error("Failed to open terminate event for %s", self) + log.error("Failed to open terminate event for pid %d", self.pid) return # recreate event for monitor 'reply' self.terminate_event_handle = KERNEL32.CreateEventW(0, False, False, event_name) if not self.terminate_event_handle: - log.error("Failed to create terminate-reply event for %s", self) + log.error("Failed to create terminate-reply event for process %d", self.pid) return KERNEL32.WaitForSingleObject(self.terminate_event_handle, 5000) - log.info("Termination confirmed for %s", self) + log.info("Termination confirmed for process %d", self.pid) KERNEL32.CloseHandle(self.terminate_event_handle) try: @@ -662,10 +795,10 @@ def terminate(self): self.open() if KERNEL32.TerminateProcess(self.h_process, 1): - log.info("Successfully terminated %s", self) + log.info("Successfully terminated process with pid %d", self.pid) return True else: - log.error("Failed to terminate %s", self) + log.error("Failed to terminate process with pid %d", self.pid) return False def is_64bit(self): @@ -684,7 +817,7 @@ def is_64bit(self): def write_monitor_config(self, interest=None, nosleepskip=False): config_path = os.path.join(Path.cwd(), "dll", f"{self.pid}.ini") - log.info("Monitor config for %s: %s", self, config_path) + log.info("Monitor config for process %s: %s", self.pid, config_path) # start the logserver for this monitored process logserver_path = f"{LOGSERVER_PREFIX}{self.pid}" @@ -752,7 +885,7 @@ def inject(self, interest=None, nosleepskip=False): thread_id = self.thread_id or 0 if not self.is_alive(): - log.warning("the %s is not alive, injection aborted", self) + log.warning("The process with pid %d is not alive, injection aborted", self.pid) return False if self.is_64bit(): @@ -760,22 +893,24 @@ def inject(self, interest=None, nosleepskip=False): bin_name = LOADER64_NAME dll = CAPEMON64_NAME bit_str = "64-bit" + side_dll = SIDELOADER64_NAME else: ttd_name = TTD32_NAME bin_name = LOADER32_NAME dll = CAPEMON32_NAME bit_str = "32-bit" + side_dll = SIDELOADER32_NAME bin_name = os.path.join(Path.cwd(), bin_name) dll = os.path.join(Path.cwd(), dll) if not os.path.exists(bin_name): - log.warning("invalid loader path %s for injecting DLL in %s, injection aborted", bin_name, self) + log.warning("Invalid loader path %s for injecting DLL in process with pid %d, injection aborted", bin_name, self.pid) log.error("Please ensure the %s loader is in analyzer/windows/bin in order to analyze %s binaries", bit_str, bit_str) return False if not os.path.exists(dll): - log.warning("invalid path %s for monitor DLL to be injected in %s, injection aborted", dll, self) + log.warning("Invalid path %s for monitor DLL to be injected in process with pid %d, injection aborted", dll, self.pid) return False try: @@ -793,6 +928,22 @@ def inject(self, interest=None, nosleepskip=False): self.deploy_version_proxy(path) return True + if self.detect_dll_sideloading(path): + try: + copy(dll, os.path.join(path, "capemon.dll")) + copy(side_dll, os.path.join(path, "version.dll")) + copy(os.path.join(Path.cwd(), "dll", f"{self.pid}.ini"), os.path.join(path, "config.ini")) + except OSError as e: + log.error("Failed to copy DLL: %s", e) + return False + log.info( + "%s DLL to sideload is %s, sideloader %s", + bit_str, + os.path.join(path, "capemon.dll"), + os.path.join(path, "version.dll"), + ) + return True + log.info("%s DLL to inject is %s, loader %s", bit_str, dll, bin_name) try: @@ -801,7 +952,7 @@ def inject(self, interest=None, nosleepskip=False): if ret.returncode == 1: log.info("Injected into %s %s", bit_str, self) elif ret.returncode != 0: - log.error("Unable to inject into %s %s, error: %d", bit_str, self, ret.returncode) + log.error("Unable to inject into %s process with pid %d, error: %d", bit_str, self.pid, ret.returncode) except Exception as e: log.error("Error running process: %s", e) return False @@ -810,7 +961,7 @@ def inject(self, interest=None, nosleepskip=False): return True try: - ret = subprocess.run( + result = subprocess.run( [ os.path.join(Path.cwd(), ttd_name), "-accepteula", @@ -824,6 +975,10 @@ def inject(self, interest=None, nosleepskip=False): text=True, timeout=1, ) + if result.stdout: + log.info(" ".join(result.stdout.split())) + if result.stderr: + log.error(" ".join(result.stderr.split())) except subprocess.TimeoutExpired as e: if e.stdout: log.info(" ".join(e.stdout.split())) @@ -849,7 +1004,7 @@ def upload_memdump(self): log.exception(e) log.error(os.path.join("memory", f"{self.pid}.dmp")) log.error(file_path) - log.info("Memory dump of %s uploaded", self) + log.info("Memory dump of process %d uploaded", self.pid) return True @@ -861,11 +1016,7 @@ def __str__(self): def has_msimg32(self, directory_path: str) -> bool: """Check if msimg32.dll exists in directory""" try: - return any( - f.name.lower() == "msimg32.dll" - for f in Path(directory_path).glob("*") - if f.is_file() - ) + return any(f.name.lower() == "msimg32.dll" for f in Path(directory_path).glob("*") if f.is_file()) except (OSError, PermissionError): return False diff --git a/analyzer/windows/lib/common/defines.py b/analyzer/windows/lib/common/defines.py index 39b11411aec..3422c608dcf 100644 --- a/analyzer/windows/lib/common/defines.py +++ b/analyzer/windows/lib/common/defines.py @@ -2,37 +2,37 @@ # This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org # See the file 'docs/LICENSE' for copying permission. -import sys from ctypes import ( POINTER, + WINFUNCTYPE, Structure, Union, - c_bool, c_char, c_double, c_int, + c_size_t, + c_ssize_t, c_ubyte, c_uint, c_ulonglong, c_ushort, c_void_p, c_wchar_p, + c_long, + windll, + wintypes, ) -if sys.platform == "win32": - from ctypes import WINFUNCTYPE, windll - - NTDLL = windll.ntdll - KERNEL32 = windll.kernel32 - ADVAPI32 = windll.advapi32 - USER32 = windll.user32 - SHELL32 = windll.shell32 - PDH = windll.pdh - PSAPI = windll.psapi - EnumWindowsProc = WINFUNCTYPE(c_bool, POINTER(c_int), POINTER(c_int)) - EnumChildProc = WINFUNCTYPE(c_bool, POINTER(c_int), POINTER(c_int)) +NTDLL = windll.ntdll +KERNEL32 = windll.kernel32 +ADVAPI32 = windll.advapi32 +USER32 = windll.user32 +SHELL32 = windll.shell32 +PDH = windll.pdh +PSAPI = windll.psapi BYTE = c_ubyte +BOOL = c_int USHORT = c_ushort WORD = c_ushort DWORD = c_uint @@ -44,14 +44,19 @@ LPTSTR = POINTER(c_char) PWSTR = c_wchar_p HANDLE = c_void_p +HWND = HANDLE PVOID = c_void_p LPVOID = c_void_p -UINT_PTR = c_void_p -ULONG_PTR = c_void_p -SIZE_T = c_void_p +LONG_PTR = c_ssize_t +UINT_PTR = c_size_t +ULONG_PTR = c_size_t +SIZE_T = c_size_t +LPARAM = LONG_PTR HMODULE = c_void_p PWCHAR = c_wchar_p DOUBLE = c_double +EnumWindowsProc = WINFUNCTYPE(BOOL, HWND, LPARAM) +EnumChildProc = WINFUNCTYPE(BOOL, HWND, LPARAM) DEBUG_PROCESS = 0x00000001 CREATE_NEW_CONSOLE = 0x00000010 @@ -96,7 +101,7 @@ PIPE_TYPE_BYTE = 0x00000000 PIPE_READMODE_BYTE = 0x00000000 FILE_FLAG_WRITE_THROUGH = 0x80000000 -INVALID_HANDLE_VALUE = 0xFFFFFFFF +INVALID_HANDLE_VALUE = -1 ERROR_BROKEN_PIPE = 0x0000006D ERROR_MORE_DATA = 0x000000EA ERROR_PIPE_CONNECTED = 0x00000217 @@ -120,8 +125,6 @@ GENERIC_EXECUTE = 0x20000000 GENERIC_ALL = 0x10000000 -OPEN_EXISTING = 0x00000003 - TH32CS_SNAPPROCESS = 0x02 GMEM_MOVEABLE = 0x0002 @@ -153,6 +156,14 @@ # Process cannot access the file because it is being used by another process. ERROR_SHARING_VIOLATION = 0x00000020 +PROCESS_CREATE_PROCESS = 0x0080 +TOKEN_ADJUST_PRIVILEGES = 0x0020 +TOKEN_QUERY = 0x0008 +PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000 +ERROR_INSUFFICIENT_BUFFER = 122 +ERROR_NOT_ALL_ASSIGNED = 1300 +EXTENDED_STARTUPINFO_PRESENT = 0x00080000 + class STARTUPINFO(Structure): _fields_ = [ @@ -195,7 +206,7 @@ class PROCESSENTRY32(Structure): ("th32ModuleID", DWORD), ("cntThreads", DWORD), ("th32ParentProcessID", DWORD), - ("pcPriClassBase", DWORD), + ("pcPriClassBase", LONG), ("dwFlags", DWORD), ("sz_exeFile", c_char * 260), ] @@ -218,7 +229,7 @@ class LUID_AND_ATTRIBUTES(Structure): class TOKEN_PRIVILEGES(Structure): _fields_ = [ ("PrivilegeCount", DWORD), - ("Privileges", LUID_AND_ATTRIBUTES), + ("Privileges", LUID_AND_ATTRIBUTES * 1), ] @@ -264,7 +275,6 @@ class SYSTEM_INFO(Structure): class UNICODE_STRING(Structure): - _pack_ = 1 _fields_ = [ ("Length", USHORT), ("MaximumLength", USHORT), @@ -273,7 +283,6 @@ class UNICODE_STRING(Structure): class SECURITY_DESCRIPTOR(Structure): - _pack_ = 1 _fields_ = [ ("Revision", BYTE), ("Sbz1", BYTE), @@ -286,16 +295,14 @@ class SECURITY_DESCRIPTOR(Structure): class SECURITY_ATTRIBUTES(Structure): - _pack_ = 1 _fields_ = [ ("nLength", DWORD), ("lpSecurityDescriptor", PVOID), - ("bInheritHandle", BYTE), + ("bInheritHandle", BOOL), ] class SYSTEMTIME(Structure): - _pack_ = 1 _fields_ = [ ("wYear", WORD), ("wMonth", WORD), @@ -327,3 +334,44 @@ class PDH_FMT_COUNTERVALUE(Structure): ("CStatus", DWORD), ("doubleValue", DOUBLE), ] + + +class PROCESS_BASIC_INFORMATION(Structure): + _fields_ = [ + ("ExitStatus", c_long), + ("PebBaseAddress", c_void_p), + ("AffinityMask", ULONG_PTR), + ("BasePriority", c_long), + ("UniqueProcessId", ULONG_PTR), + ("InheritedFromUniqueProcessId", ULONG_PTR), + ] + + +class STARTUPINFOW(Structure): + _fields_ = [ + ("cb", wintypes.DWORD), + ("lpReserved", wintypes.LPWSTR), + ("lpDesktop", wintypes.LPWSTR), + ("lpTitle", wintypes.LPWSTR), + ("dwX", wintypes.DWORD), + ("dwY", wintypes.DWORD), + ("dwXSize", wintypes.DWORD), + ("dwYSize", wintypes.DWORD), + ("dwXCountChars", wintypes.DWORD), + ("dwYCountChars", wintypes.DWORD), + ("dwFillAttribute", wintypes.DWORD), + ("dwFlags", wintypes.DWORD), + ("wShowWindow", wintypes.WORD), + ("cbReserved2", wintypes.WORD), + ("lpReserved2", POINTER(c_ubyte)), + ("hStdInput", wintypes.HANDLE), + ("hStdOutput", wintypes.HANDLE), + ("hStdError", wintypes.HANDLE), + ] + + +class STARTUPINFOEXW(Structure): + _fields_ = [ + ("StartupInfo", STARTUPINFOW), + ("lpAttributeList", LPVOID), + ] diff --git a/analyzer/windows/lib/core/log.py b/analyzer/windows/lib/core/log.py index d9a5e653fa7..7381df0c68d 100644 --- a/analyzer/windows/lib/core/log.py +++ b/analyzer/windows/lib/core/log.py @@ -5,7 +5,7 @@ import logging import socket import traceback -from ctypes import addressof, byref, c_int, create_string_buffer, sizeof +from ctypes import addressof, byref, c_int, c_void_p, create_string_buffer, sizeof from threading import Thread from lib.common.defines import ( @@ -27,6 +27,10 @@ BUFSIZE = 512 LOGBUFSIZE = 16384 +INVALID_HANDLE_VALUE_PTR = c_void_p(-1).value + +# Ensure WinAPI returns proper handle-sized values on 64-bit. +KERNEL32.CreateNamedPipeW.restype = c_void_p class LogServerThread(Thread): @@ -123,7 +127,7 @@ def __init__(self, result_ip, result_port, logserver_path): byref(sa), ) - if h_pipe == INVALID_HANDLE_VALUE: + if h_pipe in (None, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE_PTR): log.warning("Unable to create log server pipe") return False diff --git a/analyzer/windows/lib/core/pipe.py b/analyzer/windows/lib/core/pipe.py index c5f399ae3e2..5193b486190 100644 --- a/analyzer/windows/lib/core/pipe.py +++ b/analyzer/windows/lib/core/pipe.py @@ -7,7 +7,7 @@ import logging import socket import threading -from ctypes import addressof, byref, c_uint, create_string_buffer, sizeof +from ctypes import addressof, byref, c_uint, c_void_p, create_string_buffer, sizeof from lib.common.defines import ( ADVAPI32, @@ -34,6 +34,10 @@ BUFSIZE = 0x10000 open_handles = set() +INVALID_HANDLE_VALUE_PTR = c_void_p(-1).value + +# Ensure WinAPI returns/accepts proper handle-sized values on 64-bit. +KERNEL32.CreateNamedPipeW.restype = c_void_p class PipeForwarder(threading.Thread): @@ -205,7 +209,7 @@ def run(self): byref(sa), # None, ) - if pipe_handle == INVALID_HANDLE_VALUE: + if pipe_handle in (None, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE_PTR): log.warning("Error opening logging pipe server") continue diff --git a/analyzer/windows/lib/core/privileges.py b/analyzer/windows/lib/core/privileges.py index 0a240b364b1..823ea3ace7c 100644 --- a/analyzer/windows/lib/core/privileges.py +++ b/analyzer/windows/lib/core/privileges.py @@ -33,6 +33,9 @@ def grant_debug_privilege(pid=None): POINTER(TOKEN_PRIVILEGES), POINTER(wintypes.DWORD), ) + KERNEL32.GetCurrentProcess.restype = wintypes.HANDLE + KERNEL32.OpenProcess.argtypes = (wintypes.DWORD, wintypes.BOOL, wintypes.DWORD) + KERNEL32.OpenProcess.restype = wintypes.HANDLE if pid is None: h_process = KERNEL32.GetCurrentProcess() @@ -55,7 +58,7 @@ def grant_debug_privilege(pid=None): luid_attributes.Attributes = SE_PRIVILEGE_ENABLED token_privs = TOKEN_PRIVILEGES() token_privs.PrivilegeCount = 1 - token_privs.Privileges = luid_attributes + token_privs.Privileges[0] = luid_attributes if not ADVAPI32.AdjustTokenPrivileges(h_current_token, False, token_privs, 0, None, None): return False diff --git a/analyzer/windows/modules/auxiliary/disguise.py b/analyzer/windows/modules/auxiliary/disguise.py index 4d8c2d0db7f..a77590b8392 100644 --- a/analyzer/windows/modules/auxiliary/disguise.py +++ b/analyzer/windows/modules/auxiliary/disguise.py @@ -8,6 +8,7 @@ import os import re import subprocess +from ctypes import byref, sizeof from random import randint from uuid import uuid4 from winreg import ( @@ -27,7 +28,16 @@ ) from lib.common.abstracts import Auxiliary +from lib.common.defines import ( + CREATE_NEW_CONSOLE, + EXTENDED_STARTUPINFO_PRESENT, + KERNEL32, + PROCESS_INFORMATION, + STARTUPINFOEXW, +) from lib.common.rand import random_integer, random_string +from lib.core.config import Config +from lib.api.process import Process log = logging.getLogger(__name__) si = subprocess.STARTUPINFO() @@ -246,10 +256,98 @@ def add_persistent_route(self, gateway: str): self.run_as_system(["C:\\Windows\\System32\\ROUTE.exe", "-p", "add", "0.0.0.0", "mask", "0.0.0.0", gateway]) self.run_as_system(["C:\\Windows\\System32\\ROUTE.exe", "-p", "change", "0.0.0.0", "mask", "0.0.0.0", gateway]) + def launch_background_processes(self): + notepad_path = os.path.join(os.environ["SystemRoot"], "System32", "notepad.exe") + self._launch_background_process(notepad_path) + + def _launch_background_process(self, process_path): + try: + process = Process(options=self.options, config=self.config or Config(cfg="analysis.conf")) + startup_info = STARTUPINFOEXW() + startup_info.StartupInfo.cb = sizeof(STARTUPINFOEXW) + attr_list, _attr_buf, h_parent = process.build_parent_attribute_list() + startup_info.lpAttributeList = attr_list + startup_info.StartupInfo.dwFlags = 1 # STARTF_USESHOWWINDOW + startup_info.StartupInfo.wShowWindow = 0 # SW_HIDE + process_info = PROCESS_INFORMATION() + creation_flags = CREATE_NEW_CONSOLE | EXTENDED_STARTUPINFO_PRESENT + + created = KERNEL32.CreateProcessW( + process_path, + f'"{process_path}"', + None, + None, + False, + creation_flags, + None, + None, + byref(startup_info), + byref(process_info), + ) + + KERNEL32.CloseHandle(h_parent) + KERNEL32.DeleteProcThreadAttributeList(attr_list) + + if not created: + raise RuntimeError("CreateProcessW failed") + + pid = process_info.dwProcessId + if process_info.hThread: + KERNEL32.CloseHandle(process_info.hThread) + if process_info.hProcess: + KERNEL32.CloseHandle(process_info.hProcess) + log.info("Launched background process %s hidden (PID: %d)", os.path.basename(process_path), pid) + except Exception as e: + log.error("Failed to launch background process %s: %s", process_path, e) + + def log_notepad_process_tree(self): + cmd = [ + "powershell.exe", + "-NoProfile", + "-Command", + "Get-CimInstance Win32_Process -Filter \"Name='notepad.exe'\" | " + "ForEach-Object { " + "$parent = Get-CimInstance Win32_Process -Filter \"ProcessId=$($_.ParentProcessId)\"; " + "[PSCustomObject]@{ " + "ProcessId = $_.ProcessId; " + "Name = $_.Name; " + "ParentProcessId = $_.ParentProcessId; " + "ParentName = $parent.Name " + "} " + "} | Format-Table -AutoSize", + ] + try: + output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, startupinfo=si, text=True) + if output.strip(): + log.info("Notepad process info:\n%s", output.strip()) + except subprocess.CalledProcessError as e: + log.error("Failed to collect notepad process info: %s", e.output) + def start(self): + try: + total_processes = int(self.options.get("background_processes", 1)) + except (TypeError, ValueError): + total_processes = 1 + total_processes = max(0, min(total_processes, 10)) + + if total_processes > 0: + system32 = os.path.join(os.environ["SystemRoot"], "System32") + notepad_path = os.path.join(system32, "notepad.exe") + calc_path = os.path.join(system32, "calc.exe") + process_pool = [notepad_path, calc_path] + + # Always launch notepad first. + self._launch_background_process(notepad_path) + + for _ in range(total_processes - 1): + selected_process = process_pool[randint(0, len(process_pool) - 1)] + self._launch_background_process(selected_process) + # self.log_notepad_process_tree() + if self.config.windows_static_route: log.info("Config for route is: %s", str(self.config.windows_static_route)) self.add_persistent_route(self.config.windows_static_route_gateway) + self.change_productid() self.set_office_mrus() self.ramnit() diff --git a/analyzer/windows/modules/auxiliary/human.py b/analyzer/windows/modules/auxiliary/human.py index bd4b61d0d15..a05448ff0b2 100644 --- a/analyzer/windows/modules/auxiliary/human.py +++ b/analyzer/windows/modules/auxiliary/human.py @@ -11,7 +11,7 @@ import math import re import traceback -from ctypes import POINTER, WINFUNCTYPE, byref, c_bool, c_int, create_unicode_buffer, memmove, sizeof, wintypes +from ctypes import WINFUNCTYPE, byref, c_bool, c_size_t, create_unicode_buffer, memmove, sizeof, wintypes from datetime import datetime, timedelta from math import floor from threading import Thread @@ -22,7 +22,6 @@ BM_GETCHECK, BM_SETCHECK, BST_CHECKED, - CF_TEXT, GMEM_MOVEABLE, KERNEL32, USER32, @@ -33,8 +32,8 @@ log = logging.getLogger(__name__) -EnumWindowsProc = WINFUNCTYPE(c_bool, POINTER(c_int), POINTER(c_int)) -EnumChildProc = WINFUNCTYPE(c_bool, POINTER(c_int), POINTER(c_int)) +EnumWindowsProc = WINFUNCTYPE(c_bool, wintypes.HWND, wintypes.LPARAM) +EnumChildProc = WINFUNCTYPE(c_bool, wintypes.HWND, wintypes.LPARAM) CURSOR_POSITION_REGEX = r"\((\d+):(\d+)\)" WAIT_REGEX = r"WAIT(\d+)" @@ -58,6 +57,23 @@ GIVEN_INSTRUCTIONS = [] CLOSED_DOCUMENT_WINDOW = False DOCUMENT_WINDOW_CLICK_AROUND = False +CF_UNICODETEXT = 0x000D + +# Define clipboard-related WinAPI signatures explicitly to avoid pointer truncation. +KERNEL32.GlobalAlloc.argtypes = [wintypes.UINT, c_size_t] +KERNEL32.GlobalAlloc.restype = wintypes.HGLOBAL +KERNEL32.GlobalLock.argtypes = [wintypes.HGLOBAL] +KERNEL32.GlobalLock.restype = wintypes.LPVOID +KERNEL32.GlobalUnlock.argtypes = [wintypes.HGLOBAL] +KERNEL32.GlobalUnlock.restype = wintypes.BOOL +USER32.OpenClipboard.argtypes = [wintypes.HWND] +USER32.OpenClipboard.restype = wintypes.BOOL +USER32.EmptyClipboard.argtypes = [] +USER32.EmptyClipboard.restype = wintypes.BOOL +USER32.SetClipboardData.argtypes = [wintypes.UINT, wintypes.HANDLE] +USER32.SetClipboardData.restype = wintypes.HANDLE +USER32.CloseClipboard.argtypes = [] +USER32.CloseClipboard.restype = wintypes.BOOL CLICK_BUTTONS = ( # english @@ -520,15 +536,24 @@ def populate_clipboard(): clipstr = "".join(clipval) cliprawstr = create_unicode_buffer(clipstr) - USER32.OpenClipboard(None) - USER32.EmptyClipboard() - - buf = KERNEL32.GlobalAlloc(GMEM_MOVEABLE, sizeof(cliprawstr)) - lockbuf = KERNEL32.GlobalLock(buf) - memmove(lockbuf, cliprawstr, sizeof(cliprawstr)) - KERNEL32.GlobalUnlock(buf) - USER32.SetClipboardData(CF_TEXT, buf) - USER32.CloseClipboard() + if not USER32.OpenClipboard(None): + return + + try: + USER32.EmptyClipboard() + buf = KERNEL32.GlobalAlloc(GMEM_MOVEABLE, sizeof(cliprawstr)) + if not buf: + return + + lockbuf = KERNEL32.GlobalLock(buf) + if not lockbuf: + return + + memmove(lockbuf, cliprawstr, sizeof(cliprawstr)) + KERNEL32.GlobalUnlock(buf) + USER32.SetClipboardData(CF_UNICODETEXT, buf) + finally: + USER32.CloseClipboard() class Human(Auxiliary, Thread): diff --git a/analyzer/windows/modules/auxiliary/tlsdump.py b/analyzer/windows/modules/auxiliary/tlsdump.py index ab70d35a8a6..6a6c7033d47 100644 --- a/analyzer/windows/modules/auxiliary/tlsdump.py +++ b/analyzer/windows/modules/auxiliary/tlsdump.py @@ -3,7 +3,7 @@ # See the file 'docs/LICENSE' for copying permission. import logging -from ctypes import byref, sizeof +from ctypes import byref, c_void_p, sizeof from lib.api.process import Process from lib.common.abstracts import Auxiliary @@ -11,6 +11,10 @@ from lib.common.exceptions import CuckooError log = logging.getLogger(__name__) +INVALID_HANDLE_VALUE_PTR = c_void_p(-1).value + +# Ensure snapshot handle is not truncated on 64-bit. +KERNEL32.CreateToolhelp32Snapshot.restype = c_void_p class TLSDumpMasterSecrets(Auxiliary): @@ -29,6 +33,9 @@ def start(self): proc_info = PROCESSENTRY32() proc_info.dwSize = sizeof(PROCESSENTRY32) snapshot = KERNEL32.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) + if snapshot in (None, INVALID_HANDLE_VALUE_PTR): + log.warning("Failed to create process snapshot") + return flag = KERNEL32.Process32First(snapshot, byref(proc_info)) pid = 0 while flag: @@ -37,6 +44,7 @@ def start(self): log.info("lsass.exe found, pid %d", pid) flag = 0 flag = KERNEL32.Process32Next(snapshot, byref(proc_info)) + KERNEL32.CloseHandle(snapshot) if not pid: log.warning("Unable to find lsass.exe process") return diff --git a/conf/default/api.conf.default b/conf/default/api.conf.default index 26a273bc42a..6e2000543ac 100644 --- a/conf/default/api.conf.default +++ b/conf/default/api.conf.default @@ -449,6 +449,12 @@ rps = 1/s rpm = 4/m mcp = no +[yara_uploader] +enabled = no +auth_only = no +rps = 1/s +rpm = 4/m + # Allow to request stop of the analysis inside of the VM [user_stop] enabled = no diff --git a/poetry.lock b/poetry.lock index 42c432ebbbb..ae107f97ed5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.3.2 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 = "aiohappyeyeballs" @@ -153,7 +153,7 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.3.0)", "backports.zstd ; platform_python_implementation == \"CPython\" and python_version < \"3.14\"", "brotlicffi ; platform_python_implementation != \"CPython\""] +speedups = ["Brotli", "aiodns (>=3.3.0)", "backports.zstd", "brotlicffi"] [[package]] name = "aiosignal" @@ -235,7 +235,7 @@ typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\" and python_version < \"3.14\""] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] trio = ["trio (>=0.26.1)"] [[package]] @@ -263,7 +263,7 @@ description = "Timeout context manager for asyncio programs" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version == \"3.10\"" +markers = "python_version < \"3.11\"" files = [ {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, @@ -282,12 +282,12 @@ files = [ ] [package.extras] -benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] [[package]] name = "autobahn" @@ -308,13 +308,13 @@ setuptools = "*" txaio = ">=21.2.1" [package.extras] -all = ["PyGObject (>=3.40.0)", "argon2-cffi (>=20.1.0)", "attrs (>=20.3.0)", "base58 (>=2.1.0)", "bitarray (>=2.7.5)", "cbor2 (>=5.2.0)", "cffi (>=1.14.5)", "click (>=8.1.2)", "ecdsa (>=0.16.1)", "eth-abi (>=4.0.0)", "flatbuffers (>=22.12.6)", "hkdf (>=0.0.3)", "jinja2 (>=2.11.3)", "mnemonic (>=0.19)", "msgpack (>=1.0.2) ; platform_python_implementation == \"CPython\"", "passlib (>=1.7.4)", "py-ecc (>=5.1.0)", "py-eth-sig-utils (>=0.4.0)", "py-multihash (>=2.0.1)", "py-ubjson (>=0.16.1)", "pynacl (>=1.4.0)", "pyopenssl (>=20.0.1)", "python-snappy (>=0.6.0)", "pytrie (>=0.4.0)", "qrcode (>=7.3.1)", "rlp (>=2.0.1)", "service-identity (>=18.1.0)", "spake2 (>=0.8)", "twisted (>=20.3.0)", "twisted (>=24.3.0)", "u-msgpack-python (>=2.1) ; platform_python_implementation != \"CPython\"", "ujson (>=4.0.2) ; platform_python_implementation == \"CPython\"", "web3[ipfs] (>=6.0.0)", "xbr (>=21.2.1)", "yapf (==0.29.0)", "zlmdb (>=21.2.1)", "zope.interface (>=5.2.0)"] +all = ["PyGObject (>=3.40.0)", "argon2-cffi (>=20.1.0)", "attrs (>=20.3.0)", "base58 (>=2.1.0)", "bitarray (>=2.7.5)", "cbor2 (>=5.2.0)", "cffi (>=1.14.5)", "click (>=8.1.2)", "ecdsa (>=0.16.1)", "eth-abi (>=4.0.0)", "flatbuffers (>=22.12.6)", "hkdf (>=0.0.3)", "jinja2 (>=2.11.3)", "mnemonic (>=0.19)", "msgpack (>=1.0.2)", "passlib (>=1.7.4)", "py-ecc (>=5.1.0)", "py-eth-sig-utils (>=0.4.0)", "py-multihash (>=2.0.1)", "py-ubjson (>=0.16.1)", "pynacl (>=1.4.0)", "pyopenssl (>=20.0.1)", "python-snappy (>=0.6.0)", "pytrie (>=0.4.0)", "qrcode (>=7.3.1)", "rlp (>=2.0.1)", "service-identity (>=18.1.0)", "spake2 (>=0.8)", "twisted (>=20.3.0)", "twisted (>=24.3.0)", "u-msgpack-python (>=2.1)", "ujson (>=4.0.2)", "web3[ipfs] (>=6.0.0)", "xbr (>=21.2.1)", "yapf (==0.29.0)", "zlmdb (>=21.2.1)", "zope.interface (>=5.2.0)"] compress = ["python-snappy (>=0.6.0)"] -dev = ["backports.tempfile (>=1.0)", "build (>=1.2.1)", "bumpversion (>=0.5.3)", "codecov (>=2.0.15)", "flake8 (<5)", "humanize (>=0.5.1)", "mypy (>=0.610) ; python_version >= \"3.4\" and platform_python_implementation != \"PyPy\"", "passlib", "pep8-naming (>=0.3.3)", "pip (>=9.0.1)", "pyenchant (>=1.6.6)", "pyflakes (>=1.0.0)", "pyinstaller (>=4.2)", "pylint (>=1.9.2)", "pytest (>=3.4.2)", "pytest-aiohttp", "pytest-asyncio (>=0.14.0)", "pytest-runner (>=2.11.1)", "pyyaml (>=4.2b4)", "qualname", "sphinx (>=1.7.1)", "sphinx-autoapi (>=1.7.0)", "sphinx-rtd-theme (>=0.1.9)", "sphinxcontrib-images (>=0.9.1)", "tox (>=4.2.8)", "tox-gh-actions (>=2.2.0)", "twine (>=3.3.0)", "twisted (>=22.10.0)", "txaio (>=20.4.1)", "watchdog (>=0.8.3)", "wheel (>=0.36.2)", "yapf (==0.29.0)"] +dev = ["backports.tempfile (>=1.0)", "build (>=1.2.1)", "bumpversion (>=0.5.3)", "codecov (>=2.0.15)", "flake8 (<5)", "humanize (>=0.5.1)", "mypy (>=0.610)", "passlib", "pep8-naming (>=0.3.3)", "pip (>=9.0.1)", "pyenchant (>=1.6.6)", "pyflakes (>=1.0.0)", "pyinstaller (>=4.2)", "pylint (>=1.9.2)", "pytest (>=3.4.2)", "pytest-aiohttp", "pytest-asyncio (>=0.14.0)", "pytest-runner (>=2.11.1)", "pyyaml (>=4.2b4)", "qualname", "sphinx (>=1.7.1)", "sphinx-autoapi (>=1.7.0)", "sphinx-rtd-theme (>=0.1.9)", "sphinxcontrib-images (>=0.9.1)", "tox (>=4.2.8)", "tox-gh-actions (>=2.2.0)", "twine (>=3.3.0)", "twisted (>=22.10.0)", "txaio (>=20.4.1)", "watchdog (>=0.8.3)", "wheel (>=0.36.2)", "yapf (==0.29.0)"] encryption = ["pynacl (>=1.4.0)", "pyopenssl (>=20.0.1)", "pytrie (>=0.4.0)", "qrcode (>=7.3.1)", "service-identity (>=18.1.0)"] nvx = ["cffi (>=1.14.5)"] scram = ["argon2-cffi (>=20.1.0)", "cffi (>=1.14.5)", "passlib (>=1.7.4)"] -serialization = ["cbor2 (>=5.2.0)", "flatbuffers (>=22.12.6)", "msgpack (>=1.0.2) ; platform_python_implementation == \"CPython\"", "py-ubjson (>=0.16.1)", "u-msgpack-python (>=2.1) ; platform_python_implementation != \"CPython\"", "ujson (>=4.0.2) ; platform_python_implementation == \"CPython\""] +serialization = ["cbor2 (>=5.2.0)", "flatbuffers (>=22.12.6)", "msgpack (>=1.0.2)", "py-ubjson (>=0.16.1)", "u-msgpack-python (>=2.1)", "ujson (>=4.0.2)"] twisted = ["attrs (>=20.3.0)", "twisted (>=24.3.0)", "zope.interface (>=5.2.0)"] ui = ["PyGObject (>=3.40.0)"] xbr = ["base58 (>=2.1.0)", "bitarray (>=2.7.5)", "cbor2 (>=5.2.0)", "click (>=8.1.2)", "ecdsa (>=0.16.1)", "eth-abi (>=4.0.0)", "hkdf (>=0.0.3)", "jinja2 (>=2.11.3)", "mnemonic (>=0.19)", "py-ecc (>=5.1.0)", "py-eth-sig-utils (>=0.4.0)", "py-multihash (>=2.0.1)", "rlp (>=2.0.1)", "spake2 (>=0.8)", "twisted (>=20.3.0)", "web3[ipfs] (>=6.0.0)", "xbr (>=21.2.1)", "yapf (==0.29.0)", "zlmdb (>=21.2.1)"] @@ -397,39 +397,39 @@ lxml = ["lxml"] [[package]] name = "black" -version = "26.1.0" +version = "26.3.1" description = "The uncompromising code formatter." optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "black-26.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ca699710dece84e3ebf6e92ee15f5b8f72870ef984bf944a57a777a48357c168"}, - {file = "black-26.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5e8e75dabb6eb83d064b0db46392b25cabb6e784ea624219736e8985a6b3675d"}, - {file = "black-26.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb07665d9a907a1a645ee41a0df8a25ffac8ad9c26cdb557b7b88eeeeec934e0"}, - {file = "black-26.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:7ed300200918147c963c87700ccf9966dceaefbbb7277450a8d646fc5646bf24"}, - {file = "black-26.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:c5b7713daea9bf943f79f8c3b46f361cc5229e0e604dcef6a8bb6d1c37d9df89"}, - {file = "black-26.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3cee1487a9e4c640dc7467aaa543d6c0097c391dc8ac74eb313f2fbf9d7a7cb5"}, - {file = "black-26.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d62d14ca31c92adf561ebb2e5f2741bf8dea28aef6deb400d49cca011d186c68"}, - {file = "black-26.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb1dafbbaa3b1ee8b4550a84425aac8874e5f390200f5502cf3aee4a2acb2f14"}, - {file = "black-26.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:101540cb2a77c680f4f80e628ae98bd2bd8812fb9d72ade4f8995c5ff019e82c"}, - {file = "black-26.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:6f3977a16e347f1b115662be07daa93137259c711e526402aa444d7a88fdc9d4"}, - {file = "black-26.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6eeca41e70b5f5c84f2f913af857cf2ce17410847e1d54642e658e078da6544f"}, - {file = "black-26.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dd39eef053e58e60204f2cdf059e2442e2eb08f15989eefe259870f89614c8b6"}, - {file = "black-26.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9459ad0d6cd483eacad4c6566b0f8e42af5e8b583cee917d90ffaa3778420a0a"}, - {file = "black-26.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a19915ec61f3a8746e8b10adbac4a577c6ba9851fa4a9e9fbfbcf319887a5791"}, - {file = "black-26.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:643d27fb5facc167c0b1b59d0315f2674a6e950341aed0fc05cf307d22bf4954"}, - {file = "black-26.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ba1d768fbfb6930fc93b0ecc32a43d8861ded16f47a40f14afa9bb04ab93d304"}, - {file = "black-26.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b807c240b64609cb0e80d2200a35b23c7df82259f80bef1b2c96eb422b4aac9"}, - {file = "black-26.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1de0f7d01cc894066a1153b738145b194414cc6eeaad8ef4397ac9abacf40f6b"}, - {file = "black-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:91a68ae46bf07868963671e4d05611b179c2313301bd756a89ad4e3b3db2325b"}, - {file = "black-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:be5e2fe860b9bd9edbf676d5b60a9282994c03fbbd40fe8f5e75d194f96064ca"}, - {file = "black-26.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9dc8c71656a79ca49b8d3e2ce8103210c9481c57798b48deeb3a8bb02db5f115"}, - {file = "black-26.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b22b3810451abe359a964cc88121d57f7bce482b53a066de0f1584988ca36e79"}, - {file = "black-26.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:53c62883b3f999f14e5d30b5a79bd437236658ad45b2f853906c7cbe79de00af"}, - {file = "black-26.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:f016baaadc423dc960cdddf9acae679e71ee02c4c341f78f3179d7e4819c095f"}, - {file = "black-26.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:66912475200b67ef5a0ab665011964bf924745103f51977a78b4fb92a9fc1bf0"}, - {file = "black-26.1.0-py3-none-any.whl", hash = "sha256:1054e8e47ebd686e078c0bb0eaf31e6ce69c966058d122f2c0c950311f9f3ede"}, - {file = "black-26.1.0.tar.gz", hash = "sha256:d294ac3340eef9c9eb5d29288e96dc719ff269a88e27b396340459dd85da4c58"}, + {file = "black-26.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:86a8b5035fce64f5dcd1b794cf8ec4d31fe458cf6ce3986a30deb434df82a1d2"}, + {file = "black-26.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5602bdb96d52d2d0672f24f6ffe5218795736dd34807fd0fd55ccd6bf206168b"}, + {file = "black-26.3.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c54a4a82e291a1fee5137371ab488866b7c86a3305af4026bdd4dc78642e1ac"}, + {file = "black-26.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:6e131579c243c98f35bce64a7e08e87fb2d610544754675d4a0e73a070a5aa3a"}, + {file = "black-26.3.1-cp310-cp310-win_arm64.whl", hash = "sha256:5ed0ca58586c8d9a487352a96b15272b7fa55d139fc8496b519e78023a8dab0a"}, + {file = "black-26.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:28ef38aee69e4b12fda8dba75e21f9b4f979b490c8ac0baa7cb505369ac9e1ff"}, + {file = "black-26.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf9bf162ed91a26f1adba8efda0b573bc6924ec1408a52cc6f82cb73ec2b142c"}, + {file = "black-26.3.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:474c27574d6d7037c1bc875a81d9be0a9a4f9ee95e62800dab3cfaadbf75acd5"}, + {file = "black-26.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e9d0d86df21f2e1677cc4bd090cd0e446278bcbbe49bf3659c308c3e402843e"}, + {file = "black-26.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:9a5e9f45e5d5e1c5b5c29b3bd4265dcc90e8b92cf4534520896ed77f791f4da5"}, + {file = "black-26.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e6f89631eb88a7302d416594a32faeee9fb8fb848290da9d0a5f2903519fc1"}, + {file = "black-26.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cd2012d35b47d589cb8a16faf8a32ef7a336f56356babd9fcf70939ad1897f"}, + {file = "black-26.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f76ff19ec5297dd8e66eb64deda23631e642c9393ab592826fd4bdc97a4bce7"}, + {file = "black-26.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:ddb113db38838eb9f043623ba274cfaf7d51d5b0c22ecb30afe58b1bb8322983"}, + {file = "black-26.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:dfdd51fc3e64ea4f35873d1b3fb25326773d55d2329ff8449139ebaad7357efb"}, + {file = "black-26.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:855822d90f884905362f602880ed8b5df1b7e3ee7d0db2502d4388a954cc8c54"}, + {file = "black-26.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8a33d657f3276328ce00e4d37fe70361e1ec7614da5d7b6e78de5426cb56332f"}, + {file = "black-26.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f1cd08e99d2f9317292a311dfe578fd2a24b15dbce97792f9c4d752275c1fa56"}, + {file = "black-26.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:c7e72339f841b5a237ff14f7d3880ddd0fc7f98a1199e8c4327f9a4f478c1839"}, + {file = "black-26.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:afc622538b430aa4c8c853f7f63bc582b3b8030fd8c80b70fb5fa5b834e575c2"}, + {file = "black-26.3.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2d6bfaf7fd0993b420bed691f20f9492d53ce9a2bcccea4b797d34e947318a78"}, + {file = "black-26.3.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f89f2ab047c76a9c03f78d0d66ca519e389519902fa27e7a91117ef7611c0568"}, + {file = "black-26.3.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b07fc0dab849d24a80a29cfab8d8a19187d1c4685d8a5e6385a5ce323c1f015f"}, + {file = "black-26.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:0126ae5b7c09957da2bdbd91a9ba1207453feada9e9fe51992848658c6c8e01c"}, + {file = "black-26.3.1-cp314-cp314-win_arm64.whl", hash = "sha256:92c0ec1f2cc149551a2b7b47efc32c866406b6891b0ee4625e95967c8f4acfb1"}, + {file = "black-26.3.1-py3-none-any.whl", hash = "sha256:2bd5aa94fc267d38bb21a70d7410a89f1a1d318841855f698746f8e7f51acd1b"}, + {file = "black-26.3.1.tar.gz", hash = "sha256:2c50f5063a9641c7eed7795014ba37b0f5fa227f3d408b968936e24bc0566b07"}, ] [package.dependencies] @@ -438,7 +438,7 @@ mypy-extensions = ">=0.4.3" packaging = ">=22.0" pathspec = ">=1.0.0" platformdirs = ">=2" -pytokens = ">=0.3.0" +pytokens = ">=0.4.0,<0.5.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} @@ -446,7 +446,7 @@ typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} colorama = ["colorama (>=0.4.3)"] d = ["aiohttp (>=3.10)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] +uvloop = ["uvloop (>=0.15.2)", "winloop (>=0.5.0)"] [[package]] name = "bs4" @@ -829,111 +829,125 @@ files = [ [[package]] name = "coverage" -version = "7.13.2" +version = "7.14.0" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "coverage-7.13.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f4af3b01763909f477ea17c962e2cca8f39b350a4e46e3a30838b2c12e31b81b"}, - {file = "coverage-7.13.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:36393bd2841fa0b59498f75466ee9bdec4f770d3254f031f23e8fd8e140ffdd2"}, - {file = "coverage-7.13.2-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9cc7573518b7e2186bd229b1a0fe24a807273798832c27032c4510f47ffdb896"}, - {file = "coverage-7.13.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ca9566769b69a5e216a4e176d54b9df88f29d750c5b78dbb899e379b4e14b30c"}, - {file = "coverage-7.13.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c9bdea644e94fd66d75a6f7e9a97bb822371e1fe7eadae2cacd50fcbc28e4dc"}, - {file = "coverage-7.13.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5bd447332ec4f45838c1ad42268ce21ca87c40deb86eabd59888859b66be22a5"}, - {file = "coverage-7.13.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7c79ad5c28a16a1277e1187cf83ea8dafdcc689a784228a7d390f19776db7c31"}, - {file = "coverage-7.13.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:76e06ccacd1fb6ada5d076ed98a8c6f66e2e6acd3df02819e2ee29fd637b76ad"}, - {file = "coverage-7.13.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:49d49e9a5e9f4dc3d3dac95278a020afa6d6bdd41f63608a76fa05a719d5b66f"}, - {file = "coverage-7.13.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ed2bce0e7bfa53f7b0b01c722da289ef6ad4c18ebd52b1f93704c21f116360c8"}, - {file = "coverage-7.13.2-cp310-cp310-win32.whl", hash = "sha256:1574983178b35b9af4db4a9f7328a18a14a0a0ce76ffaa1c1bacb4cc82089a7c"}, - {file = "coverage-7.13.2-cp310-cp310-win_amd64.whl", hash = "sha256:a360a8baeb038928ceb996f5623a4cd508728f8f13e08d4e96ce161702f3dd99"}, - {file = "coverage-7.13.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:060ebf6f2c51aff5ba38e1f43a2095e087389b1c69d559fde6049a4b0001320e"}, - {file = "coverage-7.13.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1ea8ca9db5e7469cd364552985e15911548ea5b69c48a17291f0cac70484b2e"}, - {file = "coverage-7.13.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b780090d15fd58f07cf2011943e25a5f0c1c894384b13a216b6c86c8a8a7c508"}, - {file = "coverage-7.13.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:88a800258d83acb803c38175b4495d293656d5fac48659c953c18e5f539a274b"}, - {file = "coverage-7.13.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6326e18e9a553e674d948536a04a80d850a5eeefe2aae2e6d7cf05d54046c01b"}, - {file = "coverage-7.13.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:59562de3f797979e1ff07c587e2ac36ba60ca59d16c211eceaa579c266c5022f"}, - {file = "coverage-7.13.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:27ba1ed6f66b0e2d61bfa78874dffd4f8c3a12f8e2b5410e515ab345ba7bc9c3"}, - {file = "coverage-7.13.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8be48da4d47cc68754ce643ea50b3234557cbefe47c2f120495e7bd0a2756f2b"}, - {file = "coverage-7.13.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2a47a4223d3361b91176aedd9d4e05844ca67d7188456227b6bf5e436630c9a1"}, - {file = "coverage-7.13.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c6f141b468740197d6bd38f2b26ade124363228cc3f9858bd9924ab059e00059"}, - {file = "coverage-7.13.2-cp311-cp311-win32.whl", hash = "sha256:89567798404af067604246e01a49ef907d112edf2b75ef814b1364d5ce267031"}, - {file = "coverage-7.13.2-cp311-cp311-win_amd64.whl", hash = "sha256:21dd57941804ae2ac7e921771a5e21bbf9aabec317a041d164853ad0a96ce31e"}, - {file = "coverage-7.13.2-cp311-cp311-win_arm64.whl", hash = "sha256:10758e0586c134a0bafa28f2d37dd2cdb5e4a90de25c0fc0c77dabbad46eca28"}, - {file = "coverage-7.13.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f106b2af193f965d0d3234f3f83fc35278c7fb935dfbde56ae2da3dd2c03b84d"}, - {file = "coverage-7.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78f45d21dc4d5d6bd29323f0320089ef7eae16e4bef712dff79d184fa7330af3"}, - {file = "coverage-7.13.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fae91dfecd816444c74531a9c3d6ded17a504767e97aa674d44f638107265b99"}, - {file = "coverage-7.13.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:264657171406c114787b441484de620e03d8f7202f113d62fcd3d9688baa3e6f"}, - {file = "coverage-7.13.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae47d8dcd3ded0155afbb59c62bd8ab07ea0fd4902e1c40567439e6db9dcaf2f"}, - {file = "coverage-7.13.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8a0b33e9fd838220b007ce8f299114d406c1e8edb21336af4c97a26ecfd185aa"}, - {file = "coverage-7.13.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b3becbea7f3ce9a2d4d430f223ec15888e4deb31395840a79e916368d6004cce"}, - {file = "coverage-7.13.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f819c727a6e6eeb8711e4ce63d78c620f69630a2e9d53bc95ca5379f57b6ba94"}, - {file = "coverage-7.13.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:4f7b71757a3ab19f7ba286e04c181004c1d61be921795ee8ba6970fd0ec91da5"}, - {file = "coverage-7.13.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b7fc50d2afd2e6b4f6f2f403b70103d280a8e0cb35320cbbe6debcda02a1030b"}, - {file = "coverage-7.13.2-cp312-cp312-win32.whl", hash = "sha256:292250282cf9bcf206b543d7608bda17ca6fc151f4cbae949fc7e115112fbd41"}, - {file = "coverage-7.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:eeea10169fac01549a7921d27a3e517194ae254b542102267bef7a93ed38c40e"}, - {file = "coverage-7.13.2-cp312-cp312-win_arm64.whl", hash = "sha256:2a5b567f0b635b592c917f96b9a9cb3dbd4c320d03f4bf94e9084e494f2e8894"}, - {file = "coverage-7.13.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ed75de7d1217cf3b99365d110975f83af0528c849ef5180a12fd91b5064df9d6"}, - {file = "coverage-7.13.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97e596de8fa9bada4d88fde64a3f4d37f1b6131e4faa32bad7808abc79887ddc"}, - {file = "coverage-7.13.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:68c86173562ed4413345410c9480a8d64864ac5e54a5cda236748031e094229f"}, - {file = "coverage-7.13.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7be4d613638d678b2b3773b8f687537b284d7074695a43fe2fbbfc0e31ceaed1"}, - {file = "coverage-7.13.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d7f63ce526a96acd0e16c4af8b50b64334239550402fb1607ce6a584a6d62ce9"}, - {file = "coverage-7.13.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:406821f37f864f968e29ac14c3fccae0fec9fdeba48327f0341decf4daf92d7c"}, - {file = "coverage-7.13.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ee68e5a4e3e5443623406b905db447dceddffee0dceb39f4e0cd9ec2a35004b5"}, - {file = "coverage-7.13.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2ee0e58cca0c17dd9c6c1cdde02bb705c7b3fbfa5f3b0b5afeda20d4ebff8ef4"}, - {file = "coverage-7.13.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6e5bbb5018bf76a56aabdb64246b5288d5ae1b7d0dd4d0534fe86df2c2992d1c"}, - {file = "coverage-7.13.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a55516c68ef3e08e134e818d5e308ffa6b1337cc8b092b69b24287bf07d38e31"}, - {file = "coverage-7.13.2-cp313-cp313-win32.whl", hash = "sha256:5b20211c47a8abf4abc3319d8ce2464864fa9f30c5fcaf958a3eed92f4f1fef8"}, - {file = "coverage-7.13.2-cp313-cp313-win_amd64.whl", hash = "sha256:14f500232e521201cf031549fb1ebdfc0a40f401cf519157f76c397e586c3beb"}, - {file = "coverage-7.13.2-cp313-cp313-win_arm64.whl", hash = "sha256:9779310cb5a9778a60c899f075a8514c89fa6d10131445c2207fc893e0b14557"}, - {file = "coverage-7.13.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:e64fa5a1e41ce5df6b547cbc3d3699381c9e2c2c369c67837e716ed0f549d48e"}, - {file = "coverage-7.13.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b01899e82a04085b6561eb233fd688474f57455e8ad35cd82286463ba06332b7"}, - {file = "coverage-7.13.2-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:838943bea48be0e2768b0cf7819544cdedc1bbb2f28427eabb6eb8c9eb2285d3"}, - {file = "coverage-7.13.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:93d1d25ec2b27e90bcfef7012992d1f5121b51161b8bffcda756a816cf13c2c3"}, - {file = "coverage-7.13.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93b57142f9621b0d12349c43fc7741fe578e4bc914c1e5a54142856cfc0bf421"}, - {file = "coverage-7.13.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f06799ae1bdfff7ccb8665d75f8291c69110ba9585253de254688aa8a1ccc6c5"}, - {file = "coverage-7.13.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7f9405ab4f81d490811b1d91c7a20361135a2df4c170e7f0b747a794da5b7f23"}, - {file = "coverage-7.13.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f9ab1d5b86f8fbc97a5b3cd6280a3fd85fef3b028689d8a2c00918f0d82c728c"}, - {file = "coverage-7.13.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:f674f59712d67e841525b99e5e2b595250e39b529c3bda14764e4f625a3fa01f"}, - {file = "coverage-7.13.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c6cadac7b8ace1ba9144feb1ae3cb787a6065ba6d23ffc59a934b16406c26573"}, - {file = "coverage-7.13.2-cp313-cp313t-win32.whl", hash = "sha256:14ae4146465f8e6e6253eba0cccd57423e598a4cb925958b240c805300918343"}, - {file = "coverage-7.13.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9074896edd705a05769e3de0eac0a8388484b503b68863dd06d5e473f874fd47"}, - {file = "coverage-7.13.2-cp313-cp313t-win_arm64.whl", hash = "sha256:69e526e14f3f854eda573d3cf40cffd29a1a91c684743d904c33dbdcd0e0f3e7"}, - {file = "coverage-7.13.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:387a825f43d680e7310e6f325b2167dd093bc8ffd933b83e9aa0983cf6e0a2ef"}, - {file = "coverage-7.13.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f0d7fea9d8e5d778cd5a9e8fc38308ad688f02040e883cdc13311ef2748cb40f"}, - {file = "coverage-7.13.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e080afb413be106c95c4ee96b4fffdc9e2fa56a8bbf90b5c0918e5c4449412f5"}, - {file = "coverage-7.13.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a7fc042ba3c7ce25b8a9f097eb0f32a5ce1ccdb639d9eec114e26def98e1f8a4"}, - {file = "coverage-7.13.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d0ba505e021557f7f8173ee8cd6b926373d8653e5ff7581ae2efce1b11ef4c27"}, - {file = "coverage-7.13.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7de326f80e3451bd5cc7239ab46c73ddb658fe0b7649476bc7413572d36cd548"}, - {file = "coverage-7.13.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:abaea04f1e7e34841d4a7b343904a3f59481f62f9df39e2cd399d69a187a9660"}, - {file = "coverage-7.13.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9f93959ee0c604bccd8e0697be21de0887b1f73efcc3aa73a3ec0fd13feace92"}, - {file = "coverage-7.13.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:13fe81ead04e34e105bf1b3c9f9cdf32ce31736ee5d90a8d2de02b9d3e1bcb82"}, - {file = "coverage-7.13.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d6d16b0f71120e365741bca2cb473ca6fe38930bc5431c5e850ba949f708f892"}, - {file = "coverage-7.13.2-cp314-cp314-win32.whl", hash = "sha256:9b2f4714bb7d99ba3790ee095b3b4ac94767e1347fe424278a0b10acb3ff04fe"}, - {file = "coverage-7.13.2-cp314-cp314-win_amd64.whl", hash = "sha256:e4121a90823a063d717a96e0a0529c727fb31ea889369a0ee3ec00ed99bf6859"}, - {file = "coverage-7.13.2-cp314-cp314-win_arm64.whl", hash = "sha256:6873f0271b4a15a33e7590f338d823f6f66f91ed147a03938d7ce26efd04eee6"}, - {file = "coverage-7.13.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:f61d349f5b7cd95c34017f1927ee379bfbe9884300d74e07cf630ccf7a610c1b"}, - {file = "coverage-7.13.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a43d34ce714f4ca674c0d90beb760eb05aad906f2c47580ccee9da8fe8bfb417"}, - {file = "coverage-7.13.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bff1b04cb9d4900ce5c56c4942f047dc7efe57e2608cb7c3c8936e9970ccdbee"}, - {file = "coverage-7.13.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6ae99e4560963ad8e163e819e5d77d413d331fd00566c1e0856aa252303552c1"}, - {file = "coverage-7.13.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e79a8c7d461820257d9aa43716c4efc55366d7b292e46b5b37165be1d377405d"}, - {file = "coverage-7.13.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:060ee84f6a769d40c492711911a76811b4befb6fba50abb450371abb720f5bd6"}, - {file = "coverage-7.13.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bca209d001fd03ea2d978f8a4985093240a355c93078aee3f799852c23f561a"}, - {file = "coverage-7.13.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:6b8092aa38d72f091db61ef83cb66076f18f02da3e1a75039a4f218629600e04"}, - {file = "coverage-7.13.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4a3158dc2dcce5200d91ec28cd315c999eebff355437d2765840555d765a6e5f"}, - {file = "coverage-7.13.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3973f353b2d70bd9796cc12f532a05945232ccae966456c8ed7034cb96bbfd6f"}, - {file = "coverage-7.13.2-cp314-cp314t-win32.whl", hash = "sha256:79f6506a678a59d4ded048dc72f1859ebede8ec2b9a2d509ebe161f01c2879d3"}, - {file = "coverage-7.13.2-cp314-cp314t-win_amd64.whl", hash = "sha256:196bfeabdccc5a020a57d5a368c681e3a6ceb0447d153aeccc1ab4d70a5032ba"}, - {file = "coverage-7.13.2-cp314-cp314t-win_arm64.whl", hash = "sha256:69269ab58783e090bfbf5b916ab3d188126e22d6070bbfc93098fdd474ef937c"}, - {file = "coverage-7.13.2-py3-none-any.whl", hash = "sha256:40ce1ea1e25125556d8e76bd0b61500839a07944cc287ac21d5626f3e620cad5"}, - {file = "coverage-7.13.2.tar.gz", hash = "sha256:044c6951ec37146b72a50cc81ef02217d27d4c3640efd2640311393cbbf143d3"}, + {file = "coverage-7.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:84c32d90bf4537f0e7b4dec9aaa9a938fb8205136b9d2ecf4d7629d5262dc075"}, + {file = "coverage-7.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7c843572c605ab51cfdb5c6b5f2586e2a8467c0d28eca4bdef4ec70c5fecbd82"}, + {file = "coverage-7.14.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0c451757d3fa2603354fdc789b5e58a0e327a117c370a40e3476ba4eabab228c"}, + {file = "coverage-7.14.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3fd43f0616e765ab78d069cf8358def7363957a45cee446d65c502dcfeea7893"}, + {file = "coverage-7.14.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:731e535b1498b27d13594a0527a79b0510867b0ad891532be41cb883f2128e20"}, + {file = "coverage-7.14.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c7492f2d493b976941c7ca050f273cbda2f43c381124f7586a3e3c16d1804fec"}, + {file = "coverage-7.14.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dc38367eaa2abb1b766ac333142bce7655335a73537f5c8b75aaa89c2b987757"}, + {file = "coverage-7.14.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0a951308cde22cf77f953955a754d04dccb57fe3bb8e345d685778ed9fc1632a"}, + {file = "coverage-7.14.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fab3877e4ebb06bd9d4d4d00ee53309ee5478e66873c66a382272e3ee33eb7ea"}, + {file = "coverage-7.14.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:b812eb847b19876ebf33fb6c4f11819af05ab6050b0bfa1bc53412ae81779adb"}, + {file = "coverage-7.14.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d9c8ef6ed820c433de075657d72dda1f89a2984955e58b8a75feb3f184250218"}, + {file = "coverage-7.14.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d128b1bba9361fbaaf6a19e179e6cfd6a9103ce0c0555876f72780acc93efd85"}, + {file = "coverage-7.14.0-cp310-cp310-win32.whl", hash = "sha256:65f267ca1370726ec2c1aa38bbe4df9a71a740f22878d2d4bf59d71a4cd8d323"}, + {file = "coverage-7.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:b34ece8065914f938ed7f2c5872bb865336977a52919149846eac3744327267a"}, + {file = "coverage-7.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a78e2a9d9c5e3b8d4ab9b9d28c985ea66fced0a7d7c2aec1f216e03a2011480"}, + {file = "coverage-7.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1816c505187592dcd1c5a5f226601a549f70365fbd00930ac88b0c225b76bb4"}, + {file = "coverage-7.14.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d8e1762f0e9cbc26ec315471e7b47855218e833cd5a032d706fbf43845d878c7"}, + {file = "coverage-7.14.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9336e23e8bb3a3925398261385e2a1533957d3e760e91070dcb0e98bfa514eed"}, + {file = "coverage-7.14.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd1169b2230f9cbe9c638ba38022ed7a2b1e641cc07f7cea0365e4be2a74980"}, + {file = "coverage-7.14.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d1bb3543b58fea74d2cd1abc4054cc927e4724687cb4560cd2ed88d2c7d820c0"}, + {file = "coverage-7.14.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a93bac2cb577ef60074999ed56d8a1535894398e2ed920d4185c3ec0c8864742"}, + {file = "coverage-7.14.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5904abf7e18cddc463219b17552229650c6b79e061d31a1059283051169cf7d5"}, + {file = "coverage-7.14.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:741f57cddc9004a8c81b084660215f33a6b597dbe62c31386b983ee26310e327"}, + {file = "coverage-7.14.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:664123feb0929d7affc135717dbd70d61d98688a08ab1e5ba464739620c6252d"}, + {file = "coverage-7.14.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:c83d2399a51bbec8429266905d33616f04bc5726b1138c35844d5fcd896b2e20"}, + {file = "coverage-7.14.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb2e855b87321259a037429288ae85216d191c74de3e79bf57cd2bc0761992c"}, + {file = "coverage-7.14.0-cp311-cp311-win32.whl", hash = "sha256:731dc15b385ac52289743d476245b61e1a2927e803bef655b52bc3b2a75a21f3"}, + {file = "coverage-7.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:bfb0ed8ec5d25e93face268115d7964db9df8b9aae8edcde9ec6b16c726a7cc1"}, + {file = "coverage-7.14.0-cp311-cp311-win_arm64.whl", hash = "sha256:7ebb1c6df9f78046a1b1e0a89674cd4bf73b7c648914eebcf976a57fd99a5627"}, + {file = "coverage-7.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7ffd19fc8aed057fd686a17a4935eef5f9859d69208f96310e893e64b9b6ccf5"}, + {file = "coverage-7.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:829994cfe1aeb773ca27bf246d4badc1e764893e3bfb98fff820fcecd1ca4662"}, + {file = "coverage-7.14.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b4f07cf7edcb7ec39431a5074d7ea83b29a9f71fcfc494f0f40af4e65180420f"}, + {file = "coverage-7.14.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ca3d9cf2c32b521bd9518385608787fa86f38daf993695307531822c3430ed67"}, + {file = "coverage-7.14.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92af52828e7f29d827346b0294e5a0853fa206db77db0395b282918d41e28db9"}, + {file = "coverage-7.14.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7b2bb6c9d7e769360d0f20a0f219603fd64f0c8f97de17ab25853261602be0fb"}, + {file = "coverage-7.14.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1c9ed6ef99f88fb8c14aa8e2bf8eb0fe55fa2edfea68f8675d78741df1a5ac0e"}, + {file = "coverage-7.14.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8231ade007f37959fbf58acc677f26b922c02eda6f0428ea307da0fd39681bf3"}, + {file = "coverage-7.14.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d8b013632cc1ce1d09dbe4f32667b4d320ec2f54fc326ebeffcd0b0bcc2bb6c4"}, + {file = "coverage-7.14.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1733198802d71ec4c524f322e2867ee05c62e9e75df86bdca545407a221827d1"}, + {file = "coverage-7.14.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:72a305291fa8ee01332f1aaf38b348ca34097f6aa0b0ef627eef2837e57bbba5"}, + {file = "coverage-7.14.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fcaba850dd317c65423a9d63d88f9573c53b00354d6dd95724576cc98a131595"}, + {file = "coverage-7.14.0-cp312-cp312-win32.whl", hash = "sha256:5ac83957a80d0701310e96d8bec68cdcf4f90a7674b7d13f15a344315b41ab27"}, + {file = "coverage-7.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:70390b0da32cb90b501953716302906e8bcce087cb283e70d8c97729f22e92b2"}, + {file = "coverage-7.14.0-cp312-cp312-win_arm64.whl", hash = "sha256:91b993743d959b8be85b4abf9d5478216a69329c321efe5be0433c1a841d691d"}, + {file = "coverage-7.14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f2bbb8254370eb4c628ff3d6fa8a7f74ddc40565394d4f7ab791d1fe568e37ef"}, + {file = "coverage-7.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:23b81107f46d3f21d0cbce30664fcec0f5d9f585638a67081750f99738f6bf66"}, + {file = "coverage-7.14.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:22a7e06a5f11a757cdfe79018e9095f9f69ae283c5cd8123774c788deec8717b"}, + {file = "coverage-7.14.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9d1aa57a1dc8e05bdc42e81c5d671d849577aeedf279f4c449d6d286f9ed88ca"}, + {file = "coverage-7.14.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90c1a51bcfddf645b3bb7ec333d9e94393a8e94f55642380fa8a9a5a9e636cb7"}, + {file = "coverage-7.14.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a841fae2fadcae4f438d43b6ccc4aac2ad609f47cdb6cfdce60cbb3fe5ca7bc2"}, + {file = "coverage-7.14.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c79d2319cabef1fe8e86df73371126931550804738f78ad7d31e3aad85a67367"}, + {file = "coverage-7.14.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1b23b0c6f0b1db6ad769b7050c8b641c0bf215ded26c1816955b17b7f26edfa9"}, + {file = "coverage-7.14.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:55d3089079ce181a4566b1065ab28d2575eb76d8ac8f81f4fcda2bf037fee087"}, + {file = "coverage-7.14.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:49c005cba1e2f9677fb2845dcdf9a2e72a52a17d63e8231aaaae35d9f50215ef"}, + {file = "coverage-7.14.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:9117377b823daa28aa8635fbb08cda1cd6be3d7143257345459559aeef852d52"}, + {file = "coverage-7.14.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7b79d646cf46d5cf9a9f40281d4441df5849e445726e369006d2b117710b33fe"}, + {file = "coverage-7.14.0-cp313-cp313-win32.whl", hash = "sha256:fb609b3658479e33f9516d46f1a89dbb9b6c261366e3a11844a96ec487533dae"}, + {file = "coverage-7.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:0773d8329cf32b6fd222e4b52622c61fe8d503eb966cfc8d3c3c10c96266d50e"}, + {file = "coverage-7.14.0-cp313-cp313-win_arm64.whl", hash = "sha256:b4e26a0f1b696faf283bffe5b8569e44e336c582439df5d53281ab89ee0cba96"}, + {file = "coverage-7.14.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:953f521ca9445300397e65fda3dca58b2dbd68fee983777420b57ac3c77e9f90"}, + {file = "coverage-7.14.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:98af83fd65ae24b1fdd03aaead967a9f523bcd2f1aab2d4f3ffda65bb568a6f1"}, + {file = "coverage-7.14.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:668b92e6958c4db7cf92e81caac328dfbbdbb215db2850ad28f0cbe1eea0bfbd"}, + {file = "coverage-7.14.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9fbd898551762dea00d3fef2b1c4f99afd2c6a3ff952ea07d60a9bd5ed4f34bc"}, + {file = "coverage-7.14.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:68af363c07ecd8d4b7d4043d85cb376d7d227eceb54e5323ee45da73dbd3e426"}, + {file = "coverage-7.14.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6e57054a583da8ac55edf24117ea4c9133032cfc4cf72aa2d48c1e5d4b52f899"}, + {file = "coverage-7.14.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cc3499459bbcdd51a65b64c35ab7ed2764eaf3cba826e0df3f1d7fe2e102b70b"}, + {file = "coverage-7.14.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:45899ec2138a4346ed34d601dedf5076fb74edf2d1dd9dc76a78e82397edee90"}, + {file = "coverage-7.14.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8767486808c436f05b23ab98eb963fb29185e32a9357a166971685cb3459900f"}, + {file = "coverage-7.14.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:a3b5ddfd6aa7ddad53ee3edb231e88a2151507a43229b7d71b953916deca127d"}, + {file = "coverage-7.14.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:63df0fe568e698e1045792399f8ab6da3a6c2dce3182813fb92afa2641087b47"}, + {file = "coverage-7.14.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:827d6397dbd95144939b18f89edf31f63e1f99633e8d5f32f22ba8bdda567477"}, + {file = "coverage-7.14.0-cp313-cp313t-win32.whl", hash = "sha256:7bf43e000d24012599b879791cff41589af90674722421ef11b11a5431920bab"}, + {file = "coverage-7.14.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3f5549365af25d770e06b1f8f5682d9a5637d06eb494db91c6fa75d3950cc917"}, + {file = "coverage-7.14.0-cp313-cp313t-win_arm64.whl", hash = "sha256:6d160217ec6fe890f16ad3a9531761589443749e448f91986c972714fad361c8"}, + {file = "coverage-7.14.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9aed9fa983514ca032790f3fe0d1c0e42ca7e16b42432af1706b50a9a46bef5d"}, + {file = "coverage-7.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ba3b8390db29296dbbf49e91b6fe08f990743a90c8f447ba4c2ffc29670dfa63"}, + {file = "coverage-7.14.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3a5d8e876dfa2f102e970b183863d6dedd023d3c0eeca1fe7a9787bc5f28b212"}, + {file = "coverage-7.14.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5ebb8f4614a3787d567e610bbfdf96a4798dd69a1afb1bd8ad228d4111fe6ff3"}, + {file = "coverage-7.14.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b9bf47223dd8db3d4c4b2e443b02bace480d428f0822c3f991600448a176c97"}, + {file = "coverage-7.14.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3485a836550b303d006d57cc06e3d5afaabc642c77050b7c985a97b13e3776b8"}, + {file = "coverage-7.14.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3e7e88110bae996d199d1693ca8ec3fd52441d426401ae963437598667b4c5eb"}, + {file = "coverage-7.14.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:15228a6800ce7bdf1b74800595e56db7138cecb338fdbf044806e10dcf182dfe"}, + {file = "coverage-7.14.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9d26ac7f5398bafc5b57421ad994e8a4749e8a7a0e62d05ec7d53014d5963bfa"}, + {file = "coverage-7.14.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2fb73254ff43c911c967a899e1359bc5049b4b115d6e8fbdde4937d0a2246cd5"}, + {file = "coverage-7.14.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:454a380af72c6adada298ed270d38c7a391288198dbfb8467f786f588751a90c"}, + {file = "coverage-7.14.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:65c86fb646d2bd2972e96bd1a8b45817ed907cee68655d6295fe7ec031d04cca"}, + {file = "coverage-7.14.0-cp314-cp314-win32.whl", hash = "sha256:6a6516b02a6101398e19a3f44820f69bab2590697f7def4331f668b14adaf828"}, + {file = "coverage-7.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:45e0f79d8351fa76e256716df91eab12890d32678b9590df7ae1042e4bd4cf5d"}, + {file = "coverage-7.14.0-cp314-cp314-win_arm64.whl", hash = "sha256:4b899594a8b2d81e5cc064a0d7f9cac2081fed91049456cae7676787e41549c9"}, + {file = "coverage-7.14.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:f580f8c80acd94ac72e863efe2cab791d8c38d153e0b463b92dfa000d5c84cd1"}, + {file = "coverage-7.14.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a2bd259c442cd43c49b30fbafc51776eb19ea396faf159d26a83e6a0a5f13b0c"}, + {file = "coverage-7.14.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a706b908dfa85538863504c624b237a3cc34232bf403c057414ebfdb3b4d9f84"}, + {file = "coverage-7.14.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7333cd944ee4393b9b3d3c1b598c936d4fc8d70573a4c7dacfec5590dd50e436"}, + {file = "coverage-7.14.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f162bc9a15b82d947b02651b0c7e1609d6f7a8735ca330cfadec8481dd97d5a"}, + {file = "coverage-7.14.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:362cb78e01a5dc82009d88004cf60f2e6b6d6fcbfdec05b05af73b0abf40118f"}, + {file = "coverage-7.14.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:acebd068fca5512c3a6fde9c045f901613478781a73f0e82b307b214daef23fb"}, + {file = "coverage-7.14.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:29fe3da551dface75deb2ccbf87b6b66e2e7ef38f6d89050b428be94afff3490"}, + {file = "coverage-7.14.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b4cc4fce8672fffcb09b0eafc167b396b3ba53c4a7230f54b7aaffbf6c835fa9"}, + {file = "coverage-7.14.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5d4a51aad8ba8bdcd2b8bd8f03d4aca19693fa2327a3470e4718a25b03481020"}, + {file = "coverage-7.14.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:9f323af3e1e4f68b60b7b247e37b8515563a61375518fa59de1af48ba28a3db6"}, + {file = "coverage-7.14.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1a0abc7342ea9711c469dd8b821c6c311e6bc6aac1442e5fbd6b27fae0a8f3db"}, + {file = "coverage-7.14.0-cp314-cp314t-win32.whl", hash = "sha256:a9f864ef57b7172e2db87a096642dd51e179e085ab6b2c371c29e885f65c8fb2"}, + {file = "coverage-7.14.0-cp314-cp314t-win_amd64.whl", hash = "sha256:29943e552fdc08e082eb51400fb2f58e118a83b5542bd06531214e084399b644"}, + {file = "coverage-7.14.0-cp314-cp314t-win_arm64.whl", hash = "sha256:742a73ea621953b012f2c4c2219b512180dd84489acf5b1596b0aafc55b9100b"}, + {file = "coverage-7.14.0-py3-none-any.whl", hash = "sha256:8de5b61163aee3d05c8a2beab6f47913df7981dad1baf82c414d99158c286ab1"}, + {file = "coverage-7.14.0.tar.gz", hash = "sha256:057a6af2f160a85384cde4ab36f0d2777bae1057bae255f95413cdd382aa5c74"}, ] [package.dependencies] tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] -toml = ["tomli ; python_full_version <= \"3.11.0a6\""] +toml = ["tomli"] [[package]] name = "crispy-bootstrap4" @@ -1011,10 +1025,10 @@ files = [ cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} [package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0) ; python_version >= \"3.8\""] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0)"] docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] -nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2) ; python_version >= \"3.8\""] -pep8test = ["check-sdist ; python_version >= \"3.8\"", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] +nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"] +pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] test = ["certifi (>=2024)", "cryptography-vectors (==44.0.1)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] @@ -1404,7 +1418,7 @@ description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" groups = ["main", "dev"] -markers = "python_version == \"3.10\"" +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"}, @@ -1746,18 +1760,18 @@ files = [ [package.dependencies] cffi = {version = ">=1.12.2", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""} greenlet = [ - {version = ">=3.0rc3", markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\""}, {version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""}, + {version = ">=3.0rc3", markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\""}, ] "zope.event" = "*" "zope.interface" = "*" [package.extras] -dnspython = ["dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\""] +dnspython = ["dnspython (>=1.16.0,<2.0)", "idna"] docs = ["furo", "repoze.sphinx.autointerface", "sphinx", "sphinxcontrib-programoutput", "zope.schema"] -monitor = ["psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\""] -recommended = ["cffi (>=1.12.2) ; platform_python_implementation == \"CPython\"", "dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\"", "psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\""] -test = ["cffi (>=1.12.2) ; platform_python_implementation == \"CPython\"", "coverage (>=5.0) ; sys_platform != \"win32\"", "dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\"", "objgraph", "psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\"", "requests"] +monitor = ["psutil (>=5.7.0)"] +recommended = ["cffi (>=1.12.2)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)"] +test = ["cffi (>=1.12.2)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idna", "objgraph", "psutil (>=5.7.0)", "requests"] [[package]] name = "google-api-core" @@ -1776,25 +1790,25 @@ files = [ google-auth = ">=2.14.1,<3.0.0" googleapis-common-protos = ">=1.56.2,<2.0.0" grpcio = [ - {version = ">=1.49.1,<2.0.0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, - {version = ">=1.33.2,<2.0.0", optional = true, markers = "extra == \"grpc\""}, + {version = ">=1.33.2,<2.0.0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, + {version = ">=1.49.1,<2.0.0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\" and python_version < \"3.14\""}, {version = ">=1.75.1,<2.0.0", optional = true, markers = "python_version >= \"3.14\" and extra == \"grpc\""}, ] grpcio-status = [ - {version = ">=1.49.1,<2.0.0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, - {version = ">=1.33.2,<2.0.0", optional = true, markers = "extra == \"grpc\""}, + {version = ">=1.33.2,<2.0.0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, + {version = ">=1.49.1,<2.0.0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\" and python_version < \"3.14\""}, {version = ">=1.75.1,<2.0.0", optional = true, markers = "python_version >= \"3.14\" and extra == \"grpc\""}, ] proto-plus = [ + {version = ">=1.22.3,<2.0.0", markers = "python_version < \"3.13\""}, {version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""}, - {version = ">=1.22.3,<2.0.0"}, ] protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" requests = ">=2.18.0,<3.0.0" [package.extras] async-rest = ["google-auth[aiohttp] (>=2.35.0,<3.0.0)"] -grpc = ["grpcio (>=1.33.2,<2.0.0)", "grpcio (>=1.49.1,<2.0.0) ; python_version >= \"3.11\"", "grpcio (>=1.75.1,<2.0.0) ; python_version >= \"3.14\"", "grpcio-status (>=1.33.2,<2.0.0)", "grpcio-status (>=1.49.1,<2.0.0) ; python_version >= \"3.11\"", "grpcio-status (>=1.75.1,<2.0.0) ; python_version >= \"3.14\""] +grpc = ["grpcio (>=1.33.2,<2.0.0)", "grpcio (>=1.49.1,<2.0.0)", "grpcio (>=1.75.1,<2.0.0)", "grpcio-status (>=1.33.2,<2.0.0)", "grpcio-status (>=1.49.1,<2.0.0)", "grpcio-status (>=1.75.1,<2.0.0)"] grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.0)"] grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.0)"] @@ -1845,7 +1859,7 @@ google-api-core = ">=1.31.6,<2.0.dev0 || >2.3.0,<3.0.0" google-auth = ">=1.25.0,<3.0.0" [package.extras] -grpc = ["grpcio (>=1.38.0,<2.0.0) ; python_version < \"3.14\"", "grpcio (>=1.75.1,<2.0.0) ; python_version >= \"3.14\"", "grpcio-status (>=1.38.0,<2.0.0)"] +grpc = ["grpcio (>=1.38.0,<2.0.0)", "grpcio (>=1.75.1,<2.0.0)", "grpcio-status (>=1.38.0,<2.0.0)"] [[package]] name = "google-cloud-pubsub" @@ -1872,9 +1886,9 @@ grpcio-status = ">=1.33.2" opentelemetry-api = {version = ">=1.27.0", markers = "python_version >= \"3.8\""} opentelemetry-sdk = {version = ">=1.27.0", markers = "python_version >= \"3.8\""} proto-plus = [ + {version = ">=1.22.0,<2.0.0", markers = "python_version < \"3.11\""}, {version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""}, {version = ">=1.22.2,<2.0.0", markers = "python_version >= \"3.11\" and python_version < \"3.13\""}, - {version = ">=1.22.0,<2.0.0", markers = "python_version < \"3.11\""}, ] protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" @@ -1903,7 +1917,7 @@ google-resumable-media = ">=2.7.2,<3.0.0" requests = ">=2.22.0,<3.0.0" [package.extras] -grpc = ["google-api-core[grpc] (>=2.27.0,<3.0.0)", "grpc-google-iam-v1 (>=0.14.0,<1.0.0)", "grpcio (>=1.33.2,<2.0.0) ; python_version < \"3.14\"", "grpcio (>=1.75.1,<2.0.0) ; python_version >= \"3.14\"", "grpcio-status (>=1.76.0,<2.0.0)", "proto-plus (>=1.22.3,<2.0.0) ; python_version < \"3.13\"", "proto-plus (>=1.25.0,<2.0.0) ; python_version >= \"3.13\"", "protobuf (>=3.20.2,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<7.0.0)"] +grpc = ["google-api-core[grpc] (>=2.27.0,<3.0.0)", "grpc-google-iam-v1 (>=0.14.0,<1.0.0)", "grpcio (>=1.33.2,<2.0.0)", "grpcio (>=1.75.1,<2.0.0)", "grpcio-status (>=1.76.0,<2.0.0)", "proto-plus (>=1.22.3,<2.0.0)", "proto-plus (>=1.25.0,<2.0.0)", "protobuf (>=3.20.2,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<7.0.0)"] protobuf = ["protobuf (>=3.20.2,<7.0.0)"] tracing = ["opentelemetry-api (>=1.1.0,<2.0.0)"] @@ -2365,7 +2379,7 @@ httpcore = "==1.*" idna = "*" [package.extras] -brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] +brotli = ["brotli", "brotlicffi"] cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] @@ -2505,14 +2519,14 @@ files = [ [[package]] name = "identify" -version = "2.6.16" +version = "2.6.19" description = "File identification library for Python" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "identify-2.6.16-py2.py3-none-any.whl", hash = "sha256:391ee4d77741d994189522896270b787aed8670389bfd60f326d677d64a6dfb0"}, - {file = "identify-2.6.16.tar.gz", hash = "sha256:846857203b5511bbe94d5a352a48ef2359532bc8f6727b5544077a0dcfb24980"}, + {file = "identify-2.6.19-py2.py3-none-any.whl", hash = "sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a"}, + {file = "identify-2.6.19.tar.gz", hash = "sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842"}, ] [package.extras] @@ -2550,13 +2564,13 @@ files = [ zipp = ">=3.20" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=3.4)"] perf = ["ipython"] test = ["flufl.flake8", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] -type = ["mypy (<1.19) ; platform_python_implementation == \"PyPy\"", "pytest-mypy (>=1.0.1)"] +type = ["mypy (<1.19)", "pytest-mypy (>=1.0.1)"] [[package]] name = "incremental" @@ -2621,19 +2635,18 @@ sortedcontainers = ">=2.0,<3.0" [[package]] name = "isort" -version = "7.0.0" +version = "8.0.1" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.10.0" groups = ["dev"] files = [ - {file = "isort-7.0.0-py3-none-any.whl", hash = "sha256:1bcabac8bc3c36c7fb7b98a76c8abb18e0f841a3ba81decac7691008592499c1"}, - {file = "isort-7.0.0.tar.gz", hash = "sha256:5513527951aadb3ac4292a41a16cbc50dd1642432f5e8c20057d414bdafb4187"}, + {file = "isort-8.0.1-py3-none-any.whl", hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75"}, + {file = "isort-8.0.1.tar.gz", hash = "sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d"}, ] [package.extras] colors = ["colorama"] -plugins = ["setuptools"] [[package]] name = "jinja2" @@ -3207,10 +3220,10 @@ files = [ ] [package.extras] -dev = ["attrs", "coverage", "eval-type-backport ; python_version < \"3.10\"", "furo", "ipython", "msgpack", "mypy", "pre-commit", "pyright", "pytest", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "tomli ; python_version < \"3.11\"", "tomli_w"] +dev = ["attrs", "coverage", "eval-type-backport", "furo", "ipython", "msgpack", "mypy", "pre-commit", "pyright", "pytest", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "tomli", "tomli_w"] doc = ["furo", "ipython", "sphinx", "sphinx-copybutton", "sphinx-design"] -test = ["attrs", "eval-type-backport ; python_version < \"3.10\"", "msgpack", "pytest", "pyyaml", "tomli ; python_version < \"3.11\"", "tomli_w"] -toml = ["tomli ; python_version < \"3.11\"", "tomli_w"] +test = ["attrs", "eval-type-backport", "msgpack", "pytest", "pyyaml", "tomli", "tomli_w"] +toml = ["tomli", "tomli_w"] yaml = ["pyyaml"] [[package]] @@ -3534,7 +3547,7 @@ files = [ [package.dependencies] colorclass = "*" easygui = "*" -msoffcrypto-tool = {version = "*", markers = "platform_python_implementation != \"PyPy\" or python_version >= \"3\" and platform_system != \"Windows\" and platform_system != \"Darwin\""} +msoffcrypto-tool = {version = "*", markers = "platform_python_implementation != \"PyPy\" or python_version >= \"3\" and (platform_system != \"Windows\" and platform_system != \"Darwin\")"} olefile = ">=0.46" pcodedmp = ">=1.2.5" pyparsing = ">=2.1.0,<4" @@ -3713,27 +3726,26 @@ cryptography = ">=3.3" pynacl = ">=1.5" [package.extras] -all = ["gssapi (>=1.4.1) ; platform_system != \"Windows\"", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8) ; platform_system == \"Windows\""] -gssapi = ["gssapi (>=1.4.1) ; platform_system != \"Windows\"", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8) ; platform_system == \"Windows\""] +all = ["gssapi (>=1.4.1)", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] +gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] invoke = ["invoke (>=2.0)"] [[package]] name = "pathspec" -version = "1.0.4" +version = "1.1.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723"}, - {file = "pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645"}, + {file = "pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189"}, + {file = "pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a"}, ] [package.extras] hyperscan = ["hyperscan (>=0.7)"] optional = ["typing-extensions (>=4)"] re2 = ["google-re2 (>=1.1)"] -tests = ["pytest (>=9)", "typing-extensions (>=4.15)"] [[package]] name = "pcodedmp" @@ -3884,7 +3896,7 @@ docs = ["furo", "olefile", "sphinx (>=8.1)", "sphinx-copybutton", "sphinx-inline fpx = ["olefile"] mic = ["olefile"] tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout", "trove-classifiers (>=2024.10.12)"] -typing = ["typing-extensions ; python_version < \"3.10\""] +typing = ["typing-extensions"] xmp = ["defusedxml"] [[package]] @@ -3932,6 +3944,22 @@ files = [ dev = ["pre-commit", "tox"] testing = ["coverage", "pytest", "pytest-benchmark"] +[[package]] +name = "plyara" +version = "2.2.8" +description = "Parse YARA rules" +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "extra == \"yara\"" +files = [ + {file = "plyara-2.2.8-py3-none-any.whl", hash = "sha256:8b2a3b46c0963976010a69d3ddd280a77dcd6cf1097408ff573b5af5df04d06d"}, + {file = "plyara-2.2.8.tar.gz", hash = "sha256:ce6a5be6bdc172f78bb10d2e220409c76bd46b3da9ff43e53bbe84d1eede9700"}, +] + +[package.extras] +tests = ["coverage", "pycodestyle", "pydocstyle", "pyflakes"] + [[package]] name = "postgrest" version = "1.1.1" @@ -3952,14 +3980,14 @@ strenum = {version = ">=0.4.9,<0.5.0", markers = "python_version < \"3.11\""} [[package]] name = "pre-commit" -version = "4.5.1" +version = "4.6.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77"}, - {file = "pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61"}, + {file = "pre_commit-4.6.0-py2.py3-none-any.whl", hash = "sha256:e2cf246f7299edcabcf15f9b0571fdce06058527f0a06535068a86d38089f29b"}, + {file = "pre_commit-4.6.0.tar.gz", hash = "sha256:718d2208cef53fdc38206e40524a6d4d9576d103eb16f0fec11c875e7716e9d9"}, ] [package.dependencies] @@ -4425,7 +4453,7 @@ typing-inspection = ">=0.4.0" [package.extras] email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] +timezone = ["tzdata"] [[package]] name = "pydantic-core" @@ -4729,9 +4757,9 @@ dnspython = ">=1.16.0,<3.0.0" [package.extras] aws = ["pymongo-auth-aws (>=1.1.0,<2.0.0)"] docs = ["furo (==2024.8.6)", "readthedocs-sphinx-search (>=0.3,<1.0)", "sphinx (>=5.3,<9)", "sphinx-autobuild (>=2020.9.1)", "sphinx-rtd-theme (>=2,<4)", "sphinxcontrib-shellcheck (>=1,<2)"] -encryption = ["certifi ; os_name == \"nt\" or sys_platform == \"darwin\"", "pymongo-auth-aws (>=1.1.0,<2.0.0)", "pymongocrypt (>=1.12.0,<2.0.0)"] -gssapi = ["pykerberos ; os_name != \"nt\"", "winkerberos (>=0.5.0) ; os_name == \"nt\""] -ocsp = ["certifi ; os_name == \"nt\" or sys_platform == \"darwin\"", "cryptography (>=2.5)", "pyopenssl (>=17.2.0)", "requests (<3.0.0)", "service-identity (>=18.1.0)"] +encryption = ["certifi", "pymongo-auth-aws (>=1.1.0,<2.0.0)", "pymongocrypt (>=1.12.0,<2.0.0)"] +gssapi = ["pykerberos", "winkerberos (>=0.5.0)"] +ocsp = ["certifi", "cryptography (>=2.5)", "pyopenssl (>=17.2.0)", "requests (<3.0.0)", "service-identity (>=18.1.0)"] snappy = ["python-snappy"] test = ["pytest (>=8.2)", "pytest-asyncio (>=0.24.0)"] zstd = ["zstandard"] @@ -5183,54 +5211,54 @@ files = [ [[package]] name = "pytokens" -version = "0.4.0" +version = "0.4.1" description = "A Fast, spec compliant Python 3.14+ tokenizer that runs on older Pythons." optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "pytokens-0.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:af0c3166aea367a9e755a283171befb92dd3043858b94ae9b3b7efbe9def26a3"}, - {file = "pytokens-0.4.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:daae524ed14ca459932cbf51d74325bea643701ba8a8b0cc2d10f7cd4b3e2b63"}, - {file = "pytokens-0.4.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e95cb158c44d642ed62f555bf8136bbe780dbd64d2fb0b9169e11ffb944664c3"}, - {file = "pytokens-0.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:df58d44630eaf25f587540e94bdf1fc50b4e6d5f212c786de0fb024bfcb8753a"}, - {file = "pytokens-0.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55efcc36f9a2e0e930cfba0ce7f83445306b02f8326745585ed5551864eba73a"}, - {file = "pytokens-0.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:92eb3ef88f27c22dc9dbab966ace4d61f6826e02ba04dac8e2d65ea31df56c8e"}, - {file = "pytokens-0.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f4b77858a680635ee9904306f54b0ee4781effb89e211ba0a773d76539537165"}, - {file = "pytokens-0.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25cacc20c2ad90acb56f3739d87905473c54ca1fa5967ffcd675463fe965865e"}, - {file = "pytokens-0.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:628fab535ebc9079e4db35cd63cb401901c7ce8720a9834f9ad44b9eb4e0f1d4"}, - {file = "pytokens-0.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:4d0f568d7e82b7e96be56d03b5081de40e43c904eb6492bf09aaca47cd55f35b"}, - {file = "pytokens-0.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cd8da894e5a29ba6b6da8be06a4f7589d7220c099b5e363cb0643234b9b38c2a"}, - {file = "pytokens-0.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:237ba7cfb677dbd3b01b09860810aceb448871150566b93cd24501d5734a04b1"}, - {file = "pytokens-0.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01d1a61e36812e4e971cfe2c0e4c1f2d66d8311031dac8bf168af8a249fa04dd"}, - {file = "pytokens-0.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e47e2ef3ec6ee86909e520d79f965f9b23389fda47460303cf715d510a6fe544"}, - {file = "pytokens-0.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:3d36954aba4557fd5a418a03cf595ecbb1cdcce119f91a49b19ef09d691a22ae"}, - {file = "pytokens-0.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73eff3bdd8ad08da679867992782568db0529b887bed4c85694f84cdf35eafc6"}, - {file = "pytokens-0.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d97cc1f91b1a8e8ebccf31c367f28225699bea26592df27141deade771ed0afb"}, - {file = "pytokens-0.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c8952c537cb73a1a74369501a83b7f9d208c3cf92c41dd88a17814e68d48ce"}, - {file = "pytokens-0.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5dbf56f3c748aed9310b310d5b8b14e2c96d3ad682ad5a943f381bdbbdddf753"}, - {file = "pytokens-0.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:e131804513597f2dff2b18f9911d9b6276e21ef3699abeffc1c087c65a3d975e"}, - {file = "pytokens-0.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0d7374c917197106d3c4761374718bc55ea2e9ac0fb94171588ef5840ee1f016"}, - {file = "pytokens-0.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cd3fa1caf9e47a72ee134a29ca6b5bea84712724bba165d6628baa190c6ea5b"}, - {file = "pytokens-0.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c6986576b7b07fe9791854caa5347923005a80b079d45b63b0be70d50cce5f1"}, - {file = "pytokens-0.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9940f7c2e2f54fb1cb5fe17d0803c54da7a2bf62222704eb4217433664a186a7"}, - {file = "pytokens-0.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:54691cf8f299e7efabcc25adb4ce715d3cef1491e1c930eaf555182f898ef66a"}, - {file = "pytokens-0.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:94ff5db97a0d3cd7248a5b07ba2167bd3edc1db92f76c6db00137bbaf068ddf8"}, - {file = "pytokens-0.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d0dd6261cd9cc95fae1227b1b6ebee023a5fd4a4b6330b071c73a516f5f59b63"}, - {file = "pytokens-0.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cdca8159df407dbd669145af4171a0d967006e0be25f3b520896bc7068f02c4"}, - {file = "pytokens-0.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4b5770abeb2a24347380a1164a558f0ebe06e98aedbd54c45f7929527a5fb26e"}, - {file = "pytokens-0.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:74500d72c561dad14c037a9e86a657afd63e277dd5a3bb7570932ab7a3b12551"}, - {file = "pytokens-0.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e368e0749e4e9d86a6e08763310dc92bc69ad73d9b6db5243b30174c71a8a534"}, - {file = "pytokens-0.4.0-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:865cc65c75c8f2e9e0d8330338f649b12bfd9442561900ebaf58c596a72107d2"}, - {file = "pytokens-0.4.0-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dbb9338663b3538f31c4ca7afe4f38d9b9b3a16a8be18a273a5704a1bc7a2367"}, - {file = "pytokens-0.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:658f870523ac1a5f4733d7db61ce9af61a0c23b2aeea3d03d1800c93f760e15f"}, - {file = "pytokens-0.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:d69a2491190a74e4b6f87f3b9dfce7a6873de3f3bf330d20083d374380becac0"}, - {file = "pytokens-0.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8cd795191c4127fcb3d7b76d84006a07748c390226f47657869235092eedbc05"}, - {file = "pytokens-0.4.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef2bcbddb73ac18599a86c8c549d5145130f2cd9d83dc2b5482fd8322b7806cd"}, - {file = "pytokens-0.4.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:06ac081c1187389762b58823d90d6339e6880ce0df912f71fb9022d81d7fd429"}, - {file = "pytokens-0.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:278129d54573efdc79e75c6082e73ebd19858e22a2e848359f93629323186ca6"}, - {file = "pytokens-0.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:9380fb6d96fa5ab83ed606ebad27b6171930cc14a8a8d215f6adb187ba428690"}, - {file = "pytokens-0.4.0-py3-none-any.whl", hash = "sha256:0508d11b4de157ee12063901603be87fb0253e8f4cb9305eb168b1202ab92068"}, - {file = "pytokens-0.4.0.tar.gz", hash = "sha256:6b0b03e6ea7c9f9d47c5c61164b69ad30f4f0d70a5d9fe7eac4d19f24f77af2d"}, + {file = "pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5"}, + {file = "pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe"}, + {file = "pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c"}, + {file = "pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7"}, + {file = "pytokens-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2"}, + {file = "pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440"}, + {file = "pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc"}, + {file = "pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d"}, + {file = "pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16"}, + {file = "pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6"}, + {file = "pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083"}, + {file = "pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1"}, + {file = "pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1"}, + {file = "pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9"}, + {file = "pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68"}, + {file = "pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b"}, + {file = "pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f"}, + {file = "pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1"}, + {file = "pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4"}, + {file = "pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78"}, + {file = "pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321"}, + {file = "pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa"}, + {file = "pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d"}, + {file = "pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324"}, + {file = "pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9"}, + {file = "pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb"}, + {file = "pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3"}, + {file = "pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975"}, + {file = "pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a"}, + {file = "pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918"}, + {file = "pytokens-0.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:da5baeaf7116dced9c6bb76dc31ba04a2dc3695f3d9f74741d7910122b456edc"}, + {file = "pytokens-0.4.1-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11edda0942da80ff58c4408407616a310adecae1ddd22eef8c692fe266fa5009"}, + {file = "pytokens-0.4.1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0fc71786e629cef478cbf29d7ea1923299181d0699dbe7c3c0f4a583811d9fc1"}, + {file = "pytokens-0.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dcafc12c30dbaf1e2af0490978352e0c4041a7cde31f4f81435c2a5e8b9cabb6"}, + {file = "pytokens-0.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:42f144f3aafa5d92bad964d471a581651e28b24434d184871bd02e3a0d956037"}, + {file = "pytokens-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:34bcc734bd2f2d5fe3b34e7b3c0116bfb2397f2d9666139988e7a3eb5f7400e3"}, + {file = "pytokens-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:941d4343bf27b605e9213b26bfa1c4bf197c9c599a9627eb7305b0defcfe40c1"}, + {file = "pytokens-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3ad72b851e781478366288743198101e5eb34a414f1d5627cdd585ca3b25f1db"}, + {file = "pytokens-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:682fa37ff4d8e95f7df6fe6fe6a431e8ed8e788023c6bcc0f0880a12eab80ad1"}, + {file = "pytokens-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:30f51edd9bb7f85c748979384165601d028b84f7bd13fe14d3e065304093916a"}, + {file = "pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de"}, + {file = "pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a"}, ] [package.extras] @@ -5458,7 +5486,7 @@ typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""} [package.extras] dev = ["inline-snapshot (>=0.24)", "jsonschema (>=4)", "mypy (>=1.14.1)", "nodeenv (>=1.9.1)", "packaging (>=25)", "pre-commit (>=3.5)", "pytest (>=8.3.5)", "pytest-cov (>=5)", "rich-codex (>=1.2.11)", "ruff (>=0.12.4)", "typer (>=0.15)", "types-setuptools (>=75.8.0.20250110)"] -docs = ["markdown-include (>=0.8.1)", "mike (>=2.1.3)", "mkdocs-github-admonitions-plugin (>=0.1.1)", "mkdocs-glightbox (>=0.4)", "mkdocs-include-markdown-plugin (>=7.1.7) ; python_version >= \"3.9\"", "mkdocs-material-extensions (>=1.3.1)", "mkdocs-material[imaging] (>=9.5.18,<9.6.0)", "mkdocs-redirects (>=1.2.2)", "mkdocs-rss-plugin (>=1.15)", "mkdocs[docs] (>=1.6.1)", "mkdocstrings[python] (>=0.26.1)", "rich-codex (>=1.2.11)", "typer (>=0.15)"] +docs = ["markdown-include (>=0.8.1)", "mike (>=2.1.3)", "mkdocs-github-admonitions-plugin (>=0.1.1)", "mkdocs-glightbox (>=0.4)", "mkdocs-include-markdown-plugin (>=7.1.7)", "mkdocs-material-extensions (>=1.3.1)", "mkdocs-material[imaging] (>=9.5.18,<9.6.0)", "mkdocs-redirects (>=1.2.2)", "mkdocs-rss-plugin (>=1.15)", "mkdocs[docs] (>=1.6.1)", "mkdocstrings[python] (>=0.26.1)", "rich-codex (>=1.2.11)", "typer (>=0.15)"] [[package]] name = "rsa" @@ -5502,7 +5530,7 @@ description = "C version of reader, parser and emitter for ruamel.yaml derived f optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version < \"3.13\" and platform_python_implementation == \"CPython\"" +markers = "platform_python_implementation == \"CPython\" and python_version < \"3.13\"" files = [ {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5"}, {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969"}, @@ -5593,7 +5621,7 @@ files = [ ] [package.extras] -dev = ["Django (>=1.11)", "check-manifest", "colorama (<=0.4.1) ; python_version == \"3.4\"", "coverage", "flake8", "nose2", "readme-renderer (<25.0) ; python_version == \"3.4\"", "tox", "wheel", "zest.releaser[recommended]"] +dev = ["Django (>=1.11)", "check-manifest", "colorama (<=0.4.1)", "coverage", "flake8", "nose2", "readme-renderer (<25.0)", "tox", "wheel", "zest.releaser[recommended]"] doc = ["Sphinx", "sphinx-rtd-theme"] [[package]] @@ -5719,13 +5747,13 @@ files = [ ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] -core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"] +core = ["importlib_metadata (>=6)", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] [[package]] name = "sflock2" @@ -5749,9 +5777,9 @@ unicorn = {version = ">=2.0.0", optional = true, markers = "extra == \"shellcode yara-python = {version = ">=4.1.0", optional = true, markers = "extra == \"shellcode\""} [package.extras] -linux = ["python-magic (>=0.4.13) ; sys_platform == \"linux\""] +linux = ["python-magic (>=0.4.13)"] shellcode = ["unicorn (>=2.0.0)", "yara-python (>=4.1.0)"] -windows = ["python-magic-bin (>=0.4.14) ; sys_platform == \"win32\""] +windows = ["python-magic-bin (>=0.4.14)"] [[package]] name = "shellingham" @@ -5934,8 +5962,8 @@ intervals = ["intervals (>=0.7.1)"] password = ["passlib (>=1.6,<2.0)"] pendulum = ["pendulum (>=2.0.5)"] phone = ["phonenumbers (>=5.9.2)"] -test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo ; python_version < \"3.9\"", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] -test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo ; python_version < \"3.9\"", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] timezone = ["python-dateutil"] url = ["furl (>=0.4.1)"] @@ -6039,9 +6067,6 @@ files = [ {file = "stpyv8-13.1.201.22-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:6cb5e8751aee2487cc3b5f21eac6d459041a7180a779941b64db5736e27276ee"}, {file = "stpyv8-13.1.201.22-cp313-cp313-manylinux_2_31_x86_64.whl", hash = "sha256:834b9761bb7f49da8b887847c7647495a2cf6c45f69e2124ae0e3f024493bc15"}, {file = "stpyv8-13.1.201.22-cp313-cp313-win_amd64.whl", hash = "sha256:c8189b8c4d87579f353705441757f11e2f2260578b82000925dadf0ed59a47e3"}, - {file = "stpyv8-13.1.201.22-cp314-cp314-macosx_13_0_x86_64.whl", hash = "sha256:c4bf3048c96a6a1561861da0c74be842c79a71373d3bec0d53c4e8f6eaa7b6e8"}, - {file = "stpyv8-13.1.201.22-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:6fdbc3a8b1aa941064ec0976a5a85761f50e9090468ce275c22d0774293d2668"}, - {file = "stpyv8-13.1.201.22-cp314-cp314-manylinux_2_35_x86_64.whl", hash = "sha256:c0b258c7c5a79c5f19e636b93eece90d3cf9109af9a11c5394bdb807ed68e04a"}, {file = "stpyv8-13.1.201.22-cp39-cp39-macosx_13_0_x86_64.whl", hash = "sha256:05c3ecaaf2dd8dbe06bdb70f3192b7e6161337ee04e6830a57b58eb4be7c70bd"}, {file = "stpyv8-13.1.201.22-cp39-cp39-manylinux_2_31_x86_64.whl", hash = "sha256:bf51578ec84dba6519d75ca81a154a070910e638da0ec384f4bf6d535f9b5218"}, {file = "stpyv8-13.1.201.22-cp39-cp39-win_amd64.whl", hash = "sha256:d00a220268d63d68490682b571d082d5b197de1f19d6f478a88357c61da94f7a"}, @@ -6145,7 +6170,7 @@ description = "A lil' TOML parser" optional = false python-versions = ">=3.8" groups = ["main", "dev"] -markers = "python_version == \"3.10\"" +markers = "python_version < \"3.11\"" 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"}, @@ -6206,19 +6231,19 @@ typing-extensions = ">=4.2.0" zope-interface = ">=5" [package.extras] -all-non-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.2,<5.0)", "h2 (>=3.2,<5.0)", "httpx[http2] (>=0.27)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] +all-non-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.2,<5.0)", "h2 (>=3.2,<5.0)", "httpx[http2] (>=0.27)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] conch = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)"] dev = ["coverage (>=7.5,<8.0)", "cython-test-exception-raiser (>=1.0.2,<2)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "pydoctor (>=23.9.0,<23.10.0)", "pyflakes (>=2.2,<3.0)", "pyhamcrest (>=2)", "python-subunit (>=1.4,<2.0)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "twistedchecker (>=0.7,<1.0)"] dev-release = ["pydoctor (>=23.9.0,<23.10.0)", "pydoctor (>=23.9.0,<23.10.0)", "sphinx (>=6,<7)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "towncrier (>=23.6,<24.0)"] -gtk-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.2,<5.0)", "h2 (>=3.2,<5.0)", "httpx[http2] (>=0.27)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pygobject", "pygobject", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] +gtk-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.2,<5.0)", "h2 (>=3.2,<5.0)", "httpx[http2] (>=0.27)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pygobject", "pygobject", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] http2 = ["h2 (>=3.2,<5.0)", "priority (>=1.1.0,<2.0)"] -macos-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.2,<5.0)", "h2 (>=3.2,<5.0)", "httpx[http2] (>=0.27)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] -mypy = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "coverage (>=7.5,<8.0)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.2,<5.0)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "idna (>=2.4)", "mypy (==1.10.1)", "mypy-zope (==1.0.6)", "priority (>=1.1.0,<2.0)", "pydoctor (>=23.9.0,<23.10.0)", "pyflakes (>=2.2,<3.0)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "python-subunit (>=1.4,<2.0)", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "twistedchecker (>=0.7,<1.0)", "types-pyopenssl", "types-setuptools"] -osx-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.2,<5.0)", "h2 (>=3.2,<5.0)", "httpx[http2] (>=0.27)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] -serial = ["pyserial (>=3.0)", "pywin32 (!=226) ; platform_system == \"Windows\""] +macos-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.2,<5.0)", "h2 (>=3.2,<5.0)", "httpx[http2] (>=0.27)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] +mypy = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "coverage (>=7.5,<8.0)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.2,<5.0)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "idna (>=2.4)", "mypy (==1.10.1)", "mypy-zope (==1.0.6)", "priority (>=1.1.0,<2.0)", "pydoctor (>=23.9.0,<23.10.0)", "pyflakes (>=2.2,<3.0)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "python-subunit (>=1.4,<2.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "twistedchecker (>=0.7,<1.0)", "types-pyopenssl", "types-setuptools"] +osx-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.2,<5.0)", "h2 (>=3.2,<5.0)", "httpx[http2] (>=0.27)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"] +serial = ["pyserial (>=3.0)", "pywin32 (!=226)"] test = ["cython-test-exception-raiser (>=1.0.2,<2)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "pyhamcrest (>=2)"] tls = ["idna (>=2.4)", "pyopenssl (>=21.0.0)", "service-identity (>=18.1.0)"] -windows-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.2,<5.0)", "h2 (>=3.2,<5.0)", "httpx[http2] (>=0.27)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)", "twisted-iocpsupport (>=1.0.2)", "twisted-iocpsupport (>=1.0.2)"] +windows-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.2,<5.0)", "h2 (>=3.2,<5.0)", "httpx[http2] (>=0.27)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)", "twisted-iocpsupport (>=1.0.2)", "twisted-iocpsupport (>=1.0.2)"] [[package]] name = "txaio" @@ -6258,14 +6283,14 @@ shellingham = ">=1.3.0" [[package]] name = "types-requests" -version = "2.32.4.20260107" +version = "2.33.0.20260508" description = "Typing stubs for requests" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "types_requests-2.32.4.20260107-py3-none-any.whl", hash = "sha256:b703fe72f8ce5b31ef031264fe9395cac8f46a04661a79f7ed31a80fb308730d"}, - {file = "types_requests-2.32.4.20260107.tar.gz", hash = "sha256:018a11ac158f801bfa84857ddec1650750e393df8a004a8a9ae2a9bec6fcb24f"}, + {file = "types_requests-2.33.0.20260508-py3-none-any.whl", hash = "sha256:fa01459cca184229713df03709db46a905325906d27e042cd4fd7ea3d15d3400"}, + {file = "types_requests-2.33.0.20260508.tar.gz", hash = "sha256:81b2ae5f0d20967714a6aa5ef9284c05570d7cb06b7de8f2a77b918b63ddd411"}, ] [package.dependencies] @@ -6345,7 +6370,7 @@ files = [ ] [package.extras] -brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -6369,12 +6394,12 @@ h11 = ">=0.8" httptools = {version = ">=0.4.0", optional = true, markers = "extra == \"standard\""} python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} -uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} websockets = {version = ">=10.0", optional = true, markers = "extra == \"standard\""} [package.extras] -standard = ["colorama (>=0.4) ; sys_platform == \"win32\"", "httptools (>=0.4.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1) ; sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"", "watchfiles (>=0.13)", "websockets (>=10.0)"] +standard = ["colorama (>=0.4)", "httptools (>=0.4.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.0)"] [[package]] name = "uvloop" @@ -6383,7 +6408,7 @@ description = "Fast implementation of asyncio event loop on top of libuv" optional = false python-versions = ">=3.8.0" groups = ["main"] -markers = "platform_python_implementation != \"PyPy\" and sys_platform != \"win32\" and sys_platform != \"cygwin\"" +markers = "platform_python_implementation != \"PyPy\" and (sys_platform != \"win32\" and sys_platform != \"cygwin\")" files = [ {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f"}, {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d"}, @@ -6449,7 +6474,7 @@ typing-extensions = {version = ">=4.13.2", markers = "python_version < \"3.11\"" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [[package]] name = "viv-utils" @@ -6965,7 +6990,7 @@ files = [ ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] @@ -7050,8 +7075,9 @@ testing = ["coverage[toml]", "zope.event", "zope.testing"] gcp = ["google-cloud-pubsub", "google-cloud-storage"] maco = ["maco"] mcp = ["fastmcp", "httpx"] +yara = ["plyara"] [metadata] lock-version = "2.1" python-versions = ">=3.10, <4.0" -content-hash = "cb03a48d812fd739b002f938e9c8d755fa0a1e86c7d2432b6493e1157eea4a4f" +content-hash = "0c48f513927db4217f073b3e25b0aeb11eacd00d988a5504ab71399c57839f52" diff --git a/pyproject.toml b/pyproject.toml index 0ebe284f7e8..d313144fd6b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,195 +1,194 @@ -[project] -name = "CAPEv2" -version = "0.1.0" -description = "CAPE: Malware Configuration And Payload Extraction" -authors = [ - { name = "Kevin O'Reilly", email = "kev@capesandbox.com" }, - { name = "doomedraven", email = "doomedraven@capesandbox.com" }, -] -license = { text = "MIT" } -requires-python = ">=3.10, <4.0" -dependencies = [ - "alembic==1.9.4", - "gevent==24.2.1", - "greenlet==3.0.3", - "Pebble==5.1.0", - # "pymisp==2.4.144", - "cryptography>=44.0.1", - "requests[security,socks]==2.32.4", - # "pyOpenSSL==24.0.0", - "pefile", - "tldextract>=5.1.2", - "oletools==0.60.2", - "olefile==0.47", - # "mixbox==1.0.5", - "capstone==5.0.5", - "pycryptodomex>=3.20.0", - # "xmltodict==0.12.0", - "requests-file>=1.5.1", - "orjson>=3.9.15", - # "maec==4.1.0.17", - # "regex==2021.7.6", - "SFlock2[linux,shellcode]>=0.3.76", - # "volatility3==2.11.0", - # "XLMMacroDeobfuscator==0.2.7", - "pyzipper==0.3.6", - "flare-capa==9.3.1", - "Cython==3.0.11", - "Django>=4.2.18", - "SQLAlchemy==2.0.41", - "SQLAlchemy-Utils==0.41.1", - "Jinja2>=3.1.6", - "chardet==4.0.0", - "pygal==2.4.0", - "dpkt==1.9.6", - "dnspython==2.7.0", - "pytz==2021.1", - "maxminddb==2.6.3", - "Pillow>=8.2.0", - "python-whois==0.9.5", - "bs4==0.0.1", - "pydeep2==0.5.1", - "django-recaptcha==4.0.0", # https://pypi.org/project/django-recaptcha/ - "django-crispy-forms==2.3", - "crispy-bootstrap4==2024.10", - "django-settings-export==1.2.1", - "django-csp==3.8", - "django-extensions==3.2.3", - "django-ratelimit==4.1.0", - # "qrcode==7.2", - "python-tlsh==4.5.0", - "djangorestframework==3.15.2", - "yara-python==4.5.1", - "pymongo>=4.0.1", - # "ImageHash==4.3.1", - "LnkParse3==1.5.0", - "cachetools>=5.5.1", - "django-allauth==65.13.0", # https://django-allauth.readthedocs.io/en/latest/configuration.html - # "socks5man @ git+https://github.com/CAPESandbox/socks5man.git@7b335d027297b67abdf28f38cc7d5d42c9d810b5", - # "httpreplay @ git+https://github.com/CAPESandbox/httpreplay.git@0d5a5b3144ab15f93189b83ca8188afde43db134", - # "bingraph @ git+https://github.com/CAPESandbox/binGraph.git@552d1210ac6770f8b202d0d1fc4610cc14d878ec", - "psycopg2-binary>=2.9.10", - "ruff>=0.7.2", - "paramiko==3.5.0", - "psutil==6.1.1", - "peepdf-3==5.0.0", - "pyre2>=0.3.10", - "Werkzeug==3.1.5", - "packaging==24.2", - "setuptools==78.1.1", - # command line config manipulation - "crudini==0.9.5", - "python-dateutil==2.9.0.post0", - "python-msi>=0.0.0a3", - # guac-session - "pyguacamole>=0.11", - "uvicorn[standard]>=0.18.2", - "gunicorn>=23.0.0", - "channels[daphne]>=4.0.0", - "setproctitle==1.3.2", - "CAPE-parsers>=0.1.36", - "maco==1.1.8", -] - -[project.optional-dependencies] -maco = ["maco"] -gcp = ["google-cloud-storage", "google-cloud-pubsub"] -mcp = ["fastmcp", "httpx"] - -[dependency-groups] -dev = [ - "black>=24.3.0", - "isort>=5.10.1", - "mypy==1.14.1", - "pytest==7.2.2", - "pytest-pretty==1.1.0", - "pytest-cov==3.0.0", - "pytest-mock==3.7.0", - "pytest-django==4.5.2", - "pytest-asyncio==0.18.3", - "pytest-xdist==3.6.1", - "pytest-freezer==0.4.8", - "tenacity==8.1.0", - "types-requests>=2.32", - "httpretty>=1.1.4", - "func-timeout>=4.3.5", - "pre-commit>=2.19.0", -] - -[tool.poetry] -package-mode = false - -[tool.black] -line-length = 132 -include = "\\.py(_disabled)?$" - -[tool.isort] -profile = "black" -no_lines_before = ["FUTURE", "STDLIB"] -line_length = 132 -supported_extensions = ["py", "py_disabled"] - -[tool.flake8] -max-line-length = 132 -exclude = ".git,__pycache__,.cache,.venv" - -[tool.pytest.ini_options] -django_find_project = false -DJANGO_SETTINGS_MODULE = "web.settings" -pythonpath = [".", "web"] -testpaths = ["tests", "agent"] -norecursedirs = "tests/zip_compound" -asyncio_mode = "auto" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" - -[tool.ruff] -line-length = 132 -exclude = [ - "./analyzer/linux/dbus_next", -] - -[tool.ruff.lint] -select = [ - "F", # pyflakes - "E", # pycodestyle errors - "W", # pycodestyle warnings - # "I", # isort - # "N", # pep8-naming - "G", # flake8-logging-format -] - -ignore = [ - "E501", # ignore due to conflict with formatter - "N818", # exceptions don't need the Error suffix - "E741", # allow ambiguous variable names - "E402", - "W605", # ToDo to fix - Invalid escape sequence -] - -fixable = ["ALL"] - -[tool.ruff.lint.per-file-ignores] -"stubs/*" = [ - "N", # naming conventions don't matter in stubs - "F403", # star imports are okay in stubs - "F405", # star imports are okay in stubs -] - -[tool.ruff.format] -quote-style = "double" -indent-style = "space" -skip-magic-trailing-comma = false -line-ending = "auto" - -[tool.ruff.lint.isort] -known-first-party = ["libqtile", "test"] -default-section = "third-party" - -[tool.mypy] -warn_unused_configs = true -files = [ - "agent/**/*.py", -] +[project] +name = "CAPEv2" +version = "0.1.0" +description = "CAPE: Malware Configuration And Payload Extraction" +authors = [ + { name = "Kevin O'Reilly", email = "kev@capesandbox.com" }, + { name = "doomedraven", email = "doomedraven@capesandbox.com" }, +] +license = { text = "MIT" } +requires-python = ">=3.10, <4.0" +dependencies = [ + "alembic==1.9.4", + "gevent==24.2.1", + "greenlet==3.0.3", + "Pebble==5.1.0", + # "pymisp==2.4.144", + "cryptography>=44.0.1", + "requests[security,socks]==2.32.4", + # "pyOpenSSL==24.0.0", + "pefile", + "tldextract>=5.1.2", + "oletools==0.60.2", + "olefile==0.47", + # "mixbox==1.0.5", + "capstone==5.0.5", + "pycryptodomex>=3.20.0", + # "xmltodict==0.12.0", + "requests-file>=1.5.1", + "orjson>=3.9.15", + # "maec==4.1.0.17", + # "regex==2021.7.6", + "SFlock2[linux,shellcode]>=0.3.76", + # "volatility3==2.11.0", + # "XLMMacroDeobfuscator==0.2.7", + "pyzipper==0.3.6", + "flare-capa==9.3.1", + "Cython==3.0.11", + "Django>=4.2.18", + "SQLAlchemy==2.0.41", + "SQLAlchemy-Utils==0.41.1", + "Jinja2>=3.1.6", + "chardet==4.0.0", + "pygal==2.4.0", + "dpkt==1.9.6", + "dnspython==2.7.0", + "pytz==2021.1", + "maxminddb==2.6.3", + "Pillow>=8.2.0", + "python-whois==0.9.5", + "bs4==0.0.1", + "pydeep2==0.5.1", + "django-recaptcha==4.0.0", # https://pypi.org/project/django-recaptcha/ + "django-crispy-forms==2.3", + "crispy-bootstrap4==2024.10", + "django-settings-export==1.2.1", + "django-csp==3.8", + "django-extensions==3.2.3", + "django-ratelimit==4.1.0", + # "qrcode==7.2", + "python-tlsh==4.5.0", + "djangorestframework==3.15.2", + "yara-python==4.5.1", + "pymongo>=4.0.1", + # "ImageHash==4.3.1", + "LnkParse3==1.5.0", + "cachetools>=5.5.1", + "django-allauth==65.13.0", # https://django-allauth.readthedocs.io/en/latest/configuration.html + # "socks5man @ git+https://github.com/CAPESandbox/socks5man.git@7b335d027297b67abdf28f38cc7d5d42c9d810b5", + # "httpreplay @ git+https://github.com/CAPESandbox/httpreplay.git@0d5a5b3144ab15f93189b83ca8188afde43db134", + # "bingraph @ git+https://github.com/CAPESandbox/binGraph.git@552d1210ac6770f8b202d0d1fc4610cc14d878ec", + "psycopg2-binary>=2.9.10", + "ruff>=0.7.2", + "paramiko==3.5.0", + "psutil==6.1.1", + "peepdf-3==5.0.0", + "pyre2>=0.3.10", + "Werkzeug==3.1.5", + "packaging==24.2", + "setuptools==78.1.1", + # command line config manipulation + "crudini==0.9.5", + "python-dateutil==2.9.0.post0", + "python-msi>=0.0.0a3", + # guac-session + "pyguacamole>=0.11", + "uvicorn[standard]>=0.18.2", + "gunicorn>=23.0.0", + "channels[daphne]>=4.0.0", + "setproctitle==1.3.2", + "CAPE-parsers>=0.1.36", + "maco==1.1.8", +] + +[project.optional-dependencies] +maco = ["maco"] +gcp = ["google-cloud-storage", "google-cloud-pubsub"] +yara = ["plyara"] +mcp = ["fastmcp", "httpx"] + +[tool.poetry.group.dev.dependencies] +black = ">=24.3.0" +isort = ">=5.10.1" +mypy = "1.14.1" +pytest = "7.2.2" +pytest-pretty = "1.1.0" +pytest-cov = "3.0.0" +pytest-mock = "3.7.0" +pytest-django = "4.5.2" +pytest-asyncio = "0.18.3" +pytest-xdist = "3.6.1" +pytest-freezer = "0.4.8" +tenacity = "8.1.0" +types-requests = ">=2.32" +httpretty = ">=1.1.4" +func-timeout = ">=4.3.5" +pre-commit = ">=2.19.0" + +[tool.poetry] +package-mode = false + +[tool.black] +line-length = 132 +include = "\\.py(_disabled)?$" + +[tool.isort] +profile = "black" +no_lines_before = ["FUTURE", "STDLIB"] +line_length = 132 +supported_extensions = ["py", "py_disabled"] + +[tool.flake8] +max-line-length = 132 +exclude = ".git,__pycache__,.cache,.venv" + +[tool.pytest.ini_options] +django_find_project = false +DJANGO_SETTINGS_MODULE = "web.settings" +pythonpath = [".", "web"] +testpaths = ["tests", "agent"] +norecursedirs = "tests/zip_compound" +asyncio_mode = "auto" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.ruff] +line-length = 132 +exclude = [ + "./analyzer/linux/dbus_next", +] + +[tool.ruff.lint] +select = [ + "F", # pyflakes + "E", # pycodestyle errors + "W", # pycodestyle warnings + # "I", # isort + # "N", # pep8-naming + "G", # flake8-logging-format +] + +ignore = [ + "E501", # ignore due to conflict with formatter + "N818", # exceptions don't need the Error suffix + "E741", # allow ambiguous variable names + "E402", + "W605", # ToDo to fix - Invalid escape sequence +] + +fixable = ["ALL"] + +[tool.ruff.lint.per-file-ignores] +"stubs/*" = [ + "N", # naming conventions don't matter in stubs + "F403", # star imports are okay in stubs + "F405", # star imports are okay in stubs +] + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +skip-magic-trailing-comma = false +line-ending = "auto" + +[tool.ruff.lint.isort] +known-first-party = ["libqtile", "test"] +default-section = "third-party" + +[tool.mypy] +warn_unused_configs = true +files = [ + "agent/**/*.py", +] diff --git a/requirements.txt b/requirements.txt index 68cc15aa58d..d14c89500c4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -496,9 +496,9 @@ daphne==4.2.1 ; python_version >= "3.10" and python_version < "4.0" \ deprecation==2.1.0 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff \ --hash=sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a -django-allauth==65.14.1 ; python_version >= "3.10" and python_version < "4.0" \ - --hash=sha256:204d8a07f217c41769a62aa4c7ae7afeb2886c841bebda6964e613e5be72e8b3 \ - --hash=sha256:2467656d0adc835607e407847054979ab5a943778f6eb8980ed2a29eee6c5790 +django-allauth==65.13.0 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:119c0cf1cc2e0d1a0fe2f13588f30951d64989256084de2d60f13ab9308f9fa0 \ + --hash=sha256:7d7b7e7ad603eb3864c142f051e2cce7be2f9a9c6945a51172ec83d48c6c843b django-crispy-forms==2.3 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:2db17ae08527201be1273f0df789e5f92819e23dd28fec69cffba7f3762e1a38 \ --hash=sha256:efc4c31e5202bbec6af70d383a35e12fc80ea769d464fb0e7fe21768bb138a20 @@ -516,9 +516,9 @@ django-recaptcha==4.0.0 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:5316438f97700c431d65351470d1255047e3f2cd9af0f2f13592b637dad9213e django-settings-export==1.2.1 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:fceeae49fc597f654c1217415d8e049fc81c930b7154f5d8f28c432db738ff79 -django==5.1.15 ; python_version >= "3.10" and python_version < "4.0" \ - --hash=sha256:117871e58d6eda37f09870b7d73a3d66567b03aecd515b386b1751177c413432 \ - --hash=sha256:46a356b5ff867bece73fc6365e081f21c569973403ee7e9b9a0316f27d0eb947 +django==5.1.14 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:2a4b9c20404fd1bf50aaaa5542a19d860594cba1354f688f642feb271b91df27 \ + --hash=sha256:b98409fb31fdd6e8c3a6ba2eef3415cc5c0020057b43b21ba7af6eff5f014831 djangorestframework==3.15.2 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:2b8871b062ba1aefc2de01f773875441a961fefbf79f5eed1e32b2f096944b20 \ --hash=sha256:36fe88cd2d6c6bec23dca9804bab2ba5517a8bb9d8f47ebc68981b56840107ad @@ -1440,81 +1440,86 @@ olefile==0.47 ; python_version >= "3.10" and python_version < "4.0" \ oletools==0.60.2 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:72ad8bd748fd0c4e7b5b4733af770d11543ebb2bf2697455f99f975fcd50cc96 \ --hash=sha256:ad452099f4695ffd8855113f453348200d195ee9fa341a09e197d66ee7e0b2c3 -orjson==3.11.6 ; python_version >= "3.10" and python_version < "4.0" \ - --hash=sha256:09dded2de64e77ac0b312ad59f35023548fb87393a57447e1bb36a26c181a90f \ - --hash=sha256:0a54c72259f35299fd033042367df781c2f66d10252955ca1efb7db309b954cb \ - --hash=sha256:0b14dd49f3462b014455a28a4d810d3549bf990567653eb43765cd847df09145 \ - --hash=sha256:132b0ab2e20c73afa85cf142e547511feb3d2f5b7943468984658f3952b467d4 \ - --hash=sha256:150f12e59d6864197770c78126e1a6e07a3da73d1728731bf3bc1e8b96ffdbe6 \ - --hash=sha256:1608999478664de848e5900ce41f25c4ecdfc4beacbc632b6fd55e1a586e5d38 \ - --hash=sha256:1f42da604ee65a6b87eef858c913ce3e5777872b19321d11e6fc6d21de89b64f \ - --hash=sha256:2a42efebc45afabb1448001e90458c4020d5c64fbac8a8dc4045b777db76cb5a \ - --hash=sha256:2a8eeed7d4544cf391a142b0dd06029dac588e96cc692d9ab1c3f05b1e57c7f6 \ - --hash=sha256:2c68de30131481150073d90a5d227a4a421982f42c025ecdfb66157f9579e06f \ - --hash=sha256:2c6b81f47b13dac2caa5d20fbc953c75eb802543abf48403a4703ed3bff225f0 \ - --hash=sha256:300360edf27c8c9bf7047345a94fddf3a8b8922df0ff69d71d854a170cb375cf \ - --hash=sha256:313dfd7184cde50c733fc0d5c8c0e2f09017b573afd11dc36bd7476b30b4cb17 \ - --hash=sha256:314e9c45e0b81b547e3a1cfa3df3e07a815821b3dac9fe8cb75014071d0c16a4 \ - --hash=sha256:351b96b614e3c37a27b8ab048239ebc1e0be76cc17481a430d70a77fb95d3844 \ - --hash=sha256:380f9709c275917af28feb086813923251e11ee10687257cd7f1ea188bcd4485 \ - --hash=sha256:3a63b5e7841ca8635214c6be7c0bf0246aa8c5cd4ef0c419b14362d0b2fb13de \ - --hash=sha256:40dc277999c2ef227dcc13072be879b4cfd325502daeb5c35ed768f706f2bf30 \ - --hash=sha256:46ebee78f709d3ba7a65384cfe285bb0763157c6d2f836e7bde2f12d33a867a2 \ - --hash=sha256:52263949f41b4a4822c6b1353bcc5ee2f7109d53a3b493501d3369d6d0e7937a \ - --hash=sha256:5ae45df804f2d344cffb36c43fdf03c82fb6cd247f5faa41e21891b40dfbf733 \ - --hash=sha256:6026db2692041d2a23fe2545606df591687787825ad5821971ef0974f2c47630 \ - --hash=sha256:6439e742fa7834a24698d358a27346bb203bff356ae0402e7f5df8f749c621a8 \ - --hash=sha256:647d6d034e463764e86670644bdcaf8e68b076e6e74783383b01085ae9ab334f \ - --hash=sha256:65dfa096f4e3a5e02834b681f539a87fbe85adc82001383c0db907557f666bfc \ - --hash=sha256:6dddf9ba706294906c56ef5150a958317b09aa3a8a48df1c52ccf22ec1907eac \ - --hash=sha256:6e0bb2c1ea30ef302f0f89f9bf3e7f9ab5e2af29dc9f80eb87aa99788e4e2d65 \ - --hash=sha256:6f03f30cd8953f75f2a439070c743c7336d10ee940da918d71c6f3556af3ddcf \ - --hash=sha256:71b7cbef8471324966c3738c90ba38775563ef01b512feb5ad4805682188d1b9 \ - --hash=sha256:72c5005eb45bd2535632d4f3bec7ad392832cfc46b62a3021da3b48a67734b45 \ - --hash=sha256:75682d62b1b16b61a30716d7a2ec1f4c36195de4a1c61f6665aedd947b93a5d5 \ - --hash=sha256:7ab85bdbc138e1f73a234db6bb2e4cc1f0fcec8f4bd2bd2430e957a01aadf746 \ - --hash=sha256:825e0a85d189533c6bff7e2fc417a28f6fcea53d27125c4551979aecd6c9a197 \ - --hash=sha256:8523b9cc4ef174ae52414f7699e95ee657c16aa18b3c3c285d48d7966cce9081 \ - --hash=sha256:8d1035d1b25732ec9f971e833a3e299d2b1a330236f75e6fd945ad982c76aaf3 \ - --hash=sha256:8d777ec41a327bd3b7de97ba7bce12cc1007815ca398e4e4de9ec56c022c090b \ - --hash=sha256:905ee036064ff1e1fd1fb800055ac477cdcb547a78c22c1bc2bbf8d5d1a6fb42 \ - --hash=sha256:925e2df51f60aa50f8797830f2adfc05330425803f4105875bb511ced98b7f89 \ - --hash=sha256:931607a8865d21682bb72de54231655c86df1870502d2962dbfd12c82890d077 \ - --hash=sha256:954dae4e080574672a1dfcf2a840eddef0f27bd89b0e94903dd0824e9c1db060 \ - --hash=sha256:955368c11808c89793e847830e1b1007503a5923ddadc108547d3b77df761044 \ - --hash=sha256:9a2d9746a5b5ce20c0908ada451eb56da4ffa01552a50789a0354d8636a02953 \ - --hash=sha256:9d576865a21e5cc6695be8fb78afc812079fd361ce6a027a7d41561b61b33a90 \ - --hash=sha256:a5a5468e5e60f7ef6d7f9044b06c8f94a3c56ba528c6e4f7f06ae95164b595ec \ - --hash=sha256:a613fc37e007143d5b6286dccb1394cd114b07832417006a02b620ddd8279e37 \ - --hash=sha256:a726fa86d2368cd57990f2bd95ef5495a6e613b08fc9585dfe121ec758fb08d1 \ - --hash=sha256:a8173e0d3f6081e7034c51cf984036d02f6bab2a2126de5a759d79f8e5a140e7 \ - --hash=sha256:af44baae65ef386ad971469a8557a0673bb042b0b9fd4397becd9c2dfaa02588 \ - --hash=sha256:afd177f5dd91666d31e9019f1b06d2fcdf8a409a1637ddcb5915085dede85680 \ - --hash=sha256:b04575417a26530637f6ab4b1f7b4f666eb0433491091da4de38611f97f2fcf3 \ - --hash=sha256:b2e2e2456788ca5ea75616c40da06fc885a7dc0389780e8a41bf7c5389ba257b \ - --hash=sha256:b376fb05f20a96ec117d47987dd3b39265c635725bda40661b4c5b73b77b5fde \ - --hash=sha256:b81ffd68f084b4e993e3867acb554a049fa7787cc8710bbcc1e26965580d99be \ - --hash=sha256:b83eb2e40e8c4da6d6b340ee6b1d6125f5195eb1b0ebb7eac23c6d9d4f92d224 \ - --hash=sha256:ba8daee3e999411b50f8b50dbb0a3071dd1845f3f9a1a0a6fa6de86d1689d84d \ - --hash=sha256:c310a48542094e4f7dbb6ac076880994986dda8ca9186a58c3cb70a3514d3231 \ - --hash=sha256:caaed4dad39e271adfadc106fab634d173b2bb23d9cf7e67bd645f879175ebfc \ - --hash=sha256:cbae5c34588dc79938dffb0b6fbe8c531f4dc8a6ad7f39759a9eb5d2da405ef2 \ - --hash=sha256:cded072b9f65fcfd188aead45efa5bd528ba552add619b3ad2a81f67400ec450 \ - --hash=sha256:ce374cb98411356ba906914441fc993f271a7a666d838d8de0e0900dd4a4bc12 \ - --hash=sha256:d8dfa7a5d387f15ecad94cb6b2d2d5f4aeea64efd8d526bfc03c9812d01e1cc0 \ - --hash=sha256:e0ab8d13aa2a3e98b4a43487c9205b2c92c38c054b4237777484d503357c8437 \ - --hash=sha256:e259e85a81d76d9665f03d6129e09e4435531870de5961ddcd0bf6e3a7fde7d7 \ - --hash=sha256:e4ae1670caabb598a88d385798692ce2a1b2f078971b3329cfb85253c6097f5b \ - --hash=sha256:f0f6e9f8ff7905660bc3c8a54cd4a675aa98f7f175cf00a59815e2ff42c0d916 \ - --hash=sha256:f3a135f83185c87c13ff231fcb7dbb2fa4332a376444bd65135b50ff4cc5265c \ - --hash=sha256:f4295948d65ace0a2d8f2c4ccc429668b7eb8af547578ec882e16bf79b0050b2 \ - --hash=sha256:f75c318640acbddc419733b57f8a07515e587a939d8f54363654041fd1f4e465 \ - --hash=sha256:f8515e5910f454fe9a8e13c2bb9dc4bae4c1836313e967e72eb8a4ad874f0248 \ - --hash=sha256:f884c7fb1020d44612bd7ac0db0babba0e2f78b68d9a650c7959bf99c783773f \ - --hash=sha256:f89d104c974eafd7436d7a5fdbc57f7a1e776789959a2f4f1b2eab5c62a339f4 \ - --hash=sha256:f9959c85576beae5cdcaaf39510b15105f1ee8b70d5dacd90152617f57be8c83 \ - --hash=sha256:fe515bb89d59e1e4b48637a964f480b35c0a2676de24e65e55310f6016cca7ce \ - --hash=sha256:fe71f6b283f4f1832204ab8235ce07adad145052614f77c876fcf0dac97bc06f +orjson==3.10.15 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:035fb83585e0f15e076759b6fedaf0abb460d1765b6a36f48018a52858443514 \ + --hash=sha256:05ca7fe452a2e9d8d9d706a2984c95b9c2ebc5db417ce0b7a49b91d50642a23e \ + --hash=sha256:0a4f27ea5617828e6b58922fdbec67b0aa4bb844e2d363b9244c47fa2180e665 \ + --hash=sha256:13242f12d295e83c2955756a574ddd6741c81e5b99f2bef8ed8d53e47a01e4b7 \ + --hash=sha256:17085a6aa91e1cd70ca8533989a18b5433e15d29c574582f76f821737c8d5806 \ + --hash=sha256:1e6d33efab6b71d67f22bf2962895d3dc6f82a6273a965fab762e64fa90dc399 \ + --hash=sha256:208beedfa807c922da4e81061dafa9c8489c6328934ca2a562efa707e049e561 \ + --hash=sha256:295c70f9dc154307777ba30fe29ff15c1bcc9dfc5c48632f37d20a607e9ba85a \ + --hash=sha256:305b38b2b8f8083cc3d618927d7f424349afce5975b316d33075ef0f73576b60 \ + --hash=sha256:33aedc3d903378e257047fee506f11e0833146ca3e57a1a1fb0ddb789876c1e1 \ + --hash=sha256:3614ea508d522a621384c1d6639016a5a2e4f027f3e4a1c93a51867615d28829 \ + --hash=sha256:3766ac4702f8f795ff3fa067968e806b4344af257011858cc3d6d8721588b53f \ + --hash=sha256:3a63bb41559b05360ded9132032239e47983a39b151af1201f07ec9370715c82 \ + --hash=sha256:43e17289ffdbbac8f39243916c893d2ae41a2ea1a9cbb060a56a4d75286351ae \ + --hash=sha256:552c883d03ad185f720d0c09583ebde257e41b9521b74ff40e08b7dec4559c04 \ + --hash=sha256:5dd9ef1639878cc3efffed349543cbf9372bdbd79f478615a1c633fe4e4180d1 \ + --hash=sha256:5e8afd6200e12771467a1a44e5ad780614b86abb4b11862ec54861a82d677746 \ + --hash=sha256:616e3e8d438d02e4854f70bfdc03a6bcdb697358dbaa6bcd19cbe24d24ece1f8 \ + --hash=sha256:63309e3ff924c62404923c80b9e2048c1f74ba4b615e7584584389ada50ed428 \ + --hash=sha256:6875210307d36c94873f553786a808af2788e362bd0cf4c8e66d976791e7b528 \ + --hash=sha256:6fd9bc64421e9fe9bd88039e7ce8e58d4fead67ca88e3a4014b143cec7684fd4 \ + --hash=sha256:7066b74f9f259849629e0d04db6609db4cf5b973248f455ba5d3bd58a4daaa5b \ + --hash=sha256:73cb85490aa6bf98abd20607ab5c8324c0acb48d6da7863a51be48505646c814 \ + --hash=sha256:763dadac05e4e9d2bc14938a45a2d0560549561287d41c465d3c58aec818b164 \ + --hash=sha256:7723ad949a0ea502df656948ddd8b392780a5beaa4c3b5f97e525191b102fff0 \ + --hash=sha256:781d54657063f361e89714293c095f506c533582ee40a426cb6489c48a637b81 \ + --hash=sha256:7946922ada8f3e0b7b958cc3eb22cfcf6c0df83d1fe5521b4a100103e3fa84c8 \ + --hash=sha256:7a1c73dcc8fadbd7c55802d9aa093b36878d34a3b3222c41052ce6b0fc65f8e8 \ + --hash=sha256:7c203f6f969210128af3acae0ef9ea6aab9782939f45f6fe02d05958fe761ef9 \ + --hash=sha256:7c2c79fa308e6edb0ffab0a31fd75a7841bf2a79a20ef08a3c6e3b26814c8ca8 \ + --hash=sha256:7c864a80a2d467d7786274fce0e4f93ef2a7ca4ff31f7fc5634225aaa4e9e98c \ + --hash=sha256:88dc3f65a026bd3175eb157fea994fca6ac7c4c8579fc5a86fc2114ad05705b7 \ + --hash=sha256:8918719572d662e18b8af66aef699d8c21072e54b6c82a3f8f6404c1f5ccd5e0 \ + --hash=sha256:9d11c0714fc85bfcf36ada1179400862da3288fc785c30e8297844c867d7505a \ + --hash=sha256:9e590a0477b23ecd5b0ac865b1b907b01b3c5535f5e8a8f6ab0e503efb896334 \ + --hash=sha256:9e992fd5cfb8b9f00bfad2fd7a05a4299db2bbe92e6440d9dd2fab27655b3182 \ + --hash=sha256:a2f708c62d026fb5340788ba94a55c23df4e1869fec74be455e0b2f5363b8507 \ + --hash=sha256:a330b9b4734f09a623f74a7490db713695e13b67c959713b78369f26b3dee6bf \ + --hash=sha256:a61a4622b7ff861f019974f73d8165be1bd9a0855e1cad18ee167acacabeb061 \ + --hash=sha256:a6be38bd103d2fd9bdfa31c2720b23b5d47c6796bcb1d1b598e3924441b4298d \ + --hash=sha256:abc7abecdbf67a173ef1316036ebbf54ce400ef2300b4e26a7b843bd446c2480 \ + --hash=sha256:acd271247691574416b3228db667b84775c497b245fa275c6ab90dc1ffbbd2b3 \ + --hash=sha256:b0482b21d0462eddd67e7fce10b89e0b6ac56570424662b685a0d6fccf581e13 \ + --hash=sha256:b299383825eafe642cbab34be762ccff9fd3408d72726a6b2a4506d410a71ab3 \ + --hash=sha256:b342567e5465bd99faa559507fe45e33fc76b9fb868a63f1642c6bc0735ad02a \ + --hash=sha256:b48f59114fe318f33bbaee8ebeda696d8ccc94c9e90bc27dbe72153094e26f41 \ + --hash=sha256:b7155eb1623347f0f22c38c9abdd738b287e39b9982e1da227503387b81b34ca \ + --hash=sha256:bae0e6ec2b7ba6895198cd981b7cca95d1487d0147c8ed751e5632ad16f031a6 \ + --hash=sha256:bb00b7bfbdf5d34a13180e4805d76b4567025da19a197645ca746fc2fb536586 \ + --hash=sha256:bb5cc3527036ae3d98b65e37b7986a918955f85332c1ee07f9d3f82f3a6899b5 \ + --hash=sha256:c03cd6eea1bd3b949d0d007c8d57049aa2b39bd49f58b4b2af571a5d3833d890 \ + --hash=sha256:c25774c9e88a3e0013d7d1a6c8056926b607a61edd423b50eb5c88fd7f2823ae \ + --hash=sha256:c33be3795e299f565681d69852ac8c1bc5c84863c0b0030b2b3468843be90388 \ + --hash=sha256:c4cc83960ab79a4031f3119cc4b1a1c627a3dc09df125b27c4201dff2af7eaa6 \ + --hash=sha256:cf45e0214c593660339ef63e875f32ddd5aa3b4adc15e662cdb80dc49e194f8e \ + --hash=sha256:d13b7fe322d75bf84464b075eafd8e7dd9eae05649aa2a5354cfa32f43c59f17 \ + --hash=sha256:d433bf32a363823863a96561a555227c18a522a8217a6f9400f00ddc70139ae2 \ + --hash=sha256:d569c1c462912acdd119ccbf719cf7102ea2c67dd03b99edcb1a3048651ac96b \ + --hash=sha256:d5ac11b659fd798228a7adba3e37c010e0152b78b1982897020a8e019a94882e \ + --hash=sha256:da03392674f59a95d03fa5fb9fe3a160b0511ad84b7a3914699ea5a1b3a38da2 \ + --hash=sha256:da9a18c500f19273e9e104cca8c1f0b40a6470bcccfc33afcc088045d0bf5ea6 \ + --hash=sha256:dadba0e7b6594216c214ef7894c4bd5f08d7c0135f4dd0145600be4fbcc16767 \ + --hash=sha256:dba5a1e85d554e3897fa9fe6fbcff2ed32d55008973ec9a2b992bd9a65d2352d \ + --hash=sha256:dd0099ae6aed5eb1fc84c9eb72b95505a3df4267e6962eb93cdd5af03be71c98 \ + --hash=sha256:ddbeef2481d895ab8be5185f2432c334d6dec1f5d1933a9c83014d188e102cef \ + --hash=sha256:e117eb299a35f2634e25ed120c37c641398826c2f5a3d3cc39f5993b96171b9e \ + --hash=sha256:e4759b109c37f635aa5c5cc93a1b26927bfde24b254bcc0e1149a9fada253d2d \ + --hash=sha256:e78c211d0074e783d824ce7bb85bf459f93a233eb67a5b5003498232ddfb0e8a \ + --hash=sha256:eca81f83b1b8c07449e1d6ff7074e82e3fd6777e588f1a6632127f286a968825 \ + --hash=sha256:eea80037b9fae5339b214f59308ef0589fc06dc870578b7cce6d71eb2096764c \ + --hash=sha256:ef5b87e7aa9545ddadd2309efe6824bd3dd64ac101c15dae0f2f597911d46eaa \ + --hash=sha256:efcf6c735c3d22ef60c4aa27a5238f1a477df85e9b15f2142f9d669beb2d13fd \ + --hash=sha256:f71eae9651465dff70aa80db92586ad5b92df46a9373ee55252109bb6b703307 \ + --hash=sha256:f93ce145b2db1252dd86af37d4165b6faa83072b46e3995ecc95d4b2301b725a \ + --hash=sha256:f95fb363d79366af56c3f26b71df40b9a583b07bbaaf5b317407c4d58497852e \ + --hash=sha256:f9875f5fea7492da8ec2444839dcc439b0ef298978f311103d0b7dfd775898ab \ + --hash=sha256:fd56a26a04f6ba5fb2045b0acc487a63162a958ed837648c5781e1fe3316cfbf \ + --hash=sha256:ff4f6edb1578960ed628a3b998fa54d78d9bb3e2eb2cfc5c2a09732431c678d0 \ + --hash=sha256:ffe19f3e8d68111e8644d4f4e267a069ca427926855582ff01fc012496d19969 packaging==24.2 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759 \ --hash=sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f @@ -1743,17 +1748,17 @@ propcache==0.4.1 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88 \ --hash=sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48 \ --hash=sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781 -protobuf==6.33.5 ; python_version >= "3.10" and python_version < "4.0" \ - --hash=sha256:3093804752167bcab3998bec9f1048baae6e29505adaf1afd14a37bddede533c \ - --hash=sha256:69915a973dd0f60f31a08b8318b73eab2bd6a392c79184b3612226b0a3f8ec02 \ - --hash=sha256:6ddcac2a081f8b7b9642c09406bc6a4290128fce5f471cddd165960bb9119e5c \ - --hash=sha256:8afa18e1d6d20af15b417e728e9f60f3aa108ee76f23c3b2c07a2c3b546d3afd \ - --hash=sha256:8f04fa32763dcdb4973d537d6b54e615cc61108c7cb38fe59310c3192d29510a \ - --hash=sha256:9b71e0281f36f179d00cbcb119cb19dec4d14a81393e5ea220f64b286173e190 \ - --hash=sha256:a3157e62729aafb8df6da2c03aa5c0937c7266c626ce11a278b6eb7963c4e37c \ - --hash=sha256:a5cb85982d95d906df1e2210e58f8e4f1e3cdc088e52c921a041f9c9a0386de5 \ - --hash=sha256:cbf16ba3350fb7b889fca858fb215967792dc125b35c7976ca4818bee3521cf0 \ - --hash=sha256:d71b040839446bac0f4d162e758bea99c8251161dae9d0983a3b88dee345153b +protobuf==6.33.4 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:0f12ddbf96912690c3582f9dffb55530ef32015ad8e678cd494312bd78314c4f \ + --hash=sha256:1fe3730068fcf2e595816a6c34fe66eeedd37d51d0400b72fabc848811fdc1bc \ + --hash=sha256:2fe67f6c014c84f655ee06f6f66213f9254b3a8b6bda6cda0ccd4232c73c06f0 \ + --hash=sha256:3df850c2f8db9934de4cf8f9152f8dc2558f49f298f37f90c517e8e5c84c30e9 \ + --hash=sha256:757c978f82e74d75cba88eddec479df9b99a42b31193313b75e492c06a51764e \ + --hash=sha256:8f11ffae31ec67fc2554c2ef891dcb561dae9a2a3ed941f9e134c2db06657dbc \ + --hash=sha256:918966612c8232fc6c24c78e1cd89784307f5814ad7506c308ee3cf86662850d \ + --hash=sha256:955478a89559fa4568f5a81dce77260eabc5c686f9e8366219ebd30debf06aa6 \ + --hash=sha256:c7c64f259c618f0bef7bee042075e390debbf9682334be2b67408ec7c1c09ee6 \ + --hash=sha256:dc2e61bca3b10470c1912d166fe0af67bfc20eb55971dcef8dfa48ce14f0ed91 psutil==6.1.1 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:018aeae2af92d943fdf1da6b58665124897cfc94faa2ca92098838f83e1b1bca \ --hash=sha256:0bdd4eab935276290ad3cb718e9809412895ca6b5b334f5a9111ee6d9aff9377 \ @@ -2047,9 +2052,9 @@ pygments==2.19.1 ; python_version >= "3.10" and python_version < "4.0" \ pyguacamole==0.11 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:7f8d8652ce2e86473d72a50e0c9d8a8e0c3c74e373c6b926ca4c851774cae608 \ --hash=sha256:d6facde097a1b1a3048b20fb2ff88b024744ceb2865fb912525da7ebb7779695 -pyjwt==2.12.0 ; python_version >= "3.10" and python_version < "4.0" \ - --hash=sha256:2f62390b667cd8257de560b850bb5a883102a388829274147f1d724453f8fb02 \ - --hash=sha256:9bb459d1bdd0387967d287f5656bf7ec2b9a26645d1961628cda1764e087fd6e +pyjwt==2.10.1 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953 \ + --hash=sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb pymongo==4.11 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:0197fe47e31bee488e82e7ab73e6a351a191bbd6e25cf4a380622e4b1ffcd143 \ --hash=sha256:06e82968ea031aebc18820898b102efed1ea8dc21b51eff2a81dc9ba4191fa6b \ @@ -2280,9 +2285,9 @@ realtime==2.4.2 ; python_version >= "3.10" and python_version < "4.0" \ requests-file==2.1.0 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:0f549a3f3b0699415ac04d167e9cb39bccfb730cb832b4d20be3d9867356e658 \ --hash=sha256:cf270de5a4c5874e84599fc5778303d496c10ae5e870bfa378818f35d21bda5c -requests==2.33.0 ; python_version >= "3.10" and python_version < "4.0" \ - --hash=sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b \ - --hash=sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652 +requests==2.32.4 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c \ + --hash=sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422 rich-click==1.9.4 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:af73dc68e85f3bebb80ce302a642b9fe3b65f3df0ceb42eb9a27c467c1b678c8 \ --hash=sha256:d70f39938bcecaf5543e8750828cbea94ef51853f7d0e174cda1e10543767389 @@ -2516,9 +2521,9 @@ sqlalchemy==2.0.41 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:dd5ec3aa6ae6e4d5b5de9357d2133c07be1aff6405b136dad753a16afb6717dd \ --hash=sha256:edba70118c4be3c2b1f90754d308d0b79c6fe2c0fdc52d8ddf603916f83f4db9 \ --hash=sha256:ff8e80c4c4932c10493ff97028decfdb622de69cae87e0f127a7ebe32b4069c6 -sqlparse==0.5.4 ; python_version >= "3.10" and python_version < "4.0" \ - --hash=sha256:4396a7d3cf1cd679c1be976cf3dc6e0a51d0111e87787e7a8d780e7d5a998f9e \ - --hash=sha256:99a9f0314977b76d776a0fcb8554de91b9bb8a18560631d6bc48721d07023dcb +sqlparse==0.5.3 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272 \ + --hash=sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca storage3==0.12.1 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:32ea8f5eb2f7185c2114a4f6ae66d577722e32503f0a30b56e7ed5c7f13e6b48 \ --hash=sha256:9da77fd4f406b019fdcba201e9916aefbf615ef87f551253ce427d8136459a34 @@ -2529,7 +2534,6 @@ stpyv8==13.1.201.22 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:4d737935167c52ed72e5a78264d9adfeaf089bf54693b88f12cbdb439a36a102 \ --hash=sha256:6cb5e8751aee2487cc3b5f21eac6d459041a7180a779941b64db5736e27276ee \ --hash=sha256:6dc40b656cea7fe541f6bdbad83b6b4ed51e5ead985b54c139319a731253a55e \ - --hash=sha256:6fdbc3a8b1aa941064ec0976a5a85761f50e9090468ce275c22d0774293d2668 \ --hash=sha256:8019f19b29621ccde85125d86f60f5814175b17670f5949d2671cf22cf453ea6 \ --hash=sha256:834b9761bb7f49da8b887847c7647495a2cf6c45f69e2124ae0e3f024493bc15 \ --hash=sha256:90568ff08dfaf0ebd3bf1c79f7d21db06d82eada412a6e914b995bead7c78666 \ @@ -2537,10 +2541,8 @@ stpyv8==13.1.201.22 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:b53df6114a88698ee6f3820cf46476e83ee09c9a67dd9f7cf58ca6a2928238b0 \ --hash=sha256:b9d9499ed2007cc097a5d2ae0cb18226b2bf3ca429301811b2e12a787a8f137e \ --hash=sha256:bf51578ec84dba6519d75ca81a154a070910e638da0ec384f4bf6d535f9b5218 \ - --hash=sha256:c0b258c7c5a79c5f19e636b93eece90d3cf9109af9a11c5394bdb807ed68e04a \ --hash=sha256:c24aa4215c64db7d67fc6c42c0d7731cabcf300596bf9c826ae74f426fe3b771 \ --hash=sha256:c4292843c8133fc99833aceef25925a97edf01031e186335582deb077b99d2bf \ - --hash=sha256:c4bf3048c96a6a1561861da0c74be842c79a71373d3bec0d53c4e8f6eaa7b6e8 \ --hash=sha256:c8189b8c4d87579f353705441757f11e2f2260578b82000925dadf0ed59a47e3 \ --hash=sha256:d00a220268d63d68490682b571d082d5b197de1f19d6f478a88357c61da94f7a \ --hash=sha256:da6d8f2945bd057057c64bc93ea3c064cc848b75f55d6d651120ee5d115e0761 \ @@ -2815,9 +2817,9 @@ websockets==14.2 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:f390024a47d904613577df83ba700bd189eedc09c57af0a904e5c39624621270 \ --hash=sha256:f8a86a269759026d2bde227652b87be79f8a734e582debf64c9d302faa1e9f03 \ --hash=sha256:fd475a974d5352390baf865309fe37dec6831aafc3014ffac1eea99e84e83fc2 -werkzeug==3.1.6 ; python_version >= "3.10" and python_version < "4.0" \ - --hash=sha256:210c6bede5a420a913956b4791a7f4d6843a43b6fcee4dfa08a65e93007d0d25 \ - --hash=sha256:7ddf3357bb9564e407607f988f683d72038551200c704012bb9a4c523d42f131 +werkzeug==3.1.5 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:5111e36e91086ece91f93268bb39b4a35c1e6f1feac762c9c822ded0a4e322dc \ + --hash=sha256:6a548b0e88955dd07ccb25539d7d0cc97417ee9e179677d22c7041c8f078ce67 win-unicode-console==0.5 ; python_version >= "3.10" and python_version < "4.0" and platform_python_implementation != "PyPy" and platform_system == "Windows" \ --hash=sha256:d4142d4d56d46f449d6f00536a73625a871cba040f0bc1a2e305a04578f07d1e xmltodict==0.14.2 ; python_version >= "3.10" and python_version < "4.0" \ diff --git a/web/apiv2/urls.py b/web/apiv2/urls.py index cd6e740ea89..737c4116012 100644 --- a/web/apiv2/urls.py +++ b/web/apiv2/urls.py @@ -76,6 +76,7 @@ # re_path(r"^tasks/add/(?P[A-Za-z0-9]+)/(?P\d+)/$", views.post_processing), re_path(r"^tasks/statistics/(?P\d+)/$", views.statistics_data), re_path(r"^exitnodes/$", views.exit_nodes_list), + re_path(r"^yara_uploader/$", views.yara_uploader, name="yara_uploader"), # re_path(r"^dist/tasks_reported$", views.dist_tasks_reported), # re_path(r"^dist/tasks_notification/(?P\d+)$", views.dist_tasks_notification), ] diff --git a/web/apiv2/views.py b/web/apiv2/views.py index 51f8be446c0..14915627fc0 100644 --- a/web/apiv2/views.py +++ b/web/apiv2/views.py @@ -10,11 +10,14 @@ import zipfile from datetime import datetime, timedelta from io import BytesIO -from urllib.parse import quote +from urllib.parse import quote, urljoin from wsgiref.util import FileWrapper +import plyara +import plyara.utils import pyzipper import requests +import yara from bson.objectid import ObjectId from django.conf import settings from django.contrib.auth.decorators import login_required @@ -128,7 +131,9 @@ DIST_ENABLED = False if dist_conf.distributed.enabled: - from lib.cuckoo.common.dist_db import create_session + from sqlalchemy import select + + from lib.cuckoo.common.dist_db import Node, create_session from lib.cuckoo.common.dist_db import Task as DTask dist_session = create_session( @@ -139,6 +144,7 @@ db: _Database = Database() +ALLOWED_YARA_CATEGORIES = ("binaries", "urls", "memory", "CAPE", "macro", "monitor") # Conditional decorator for web authentication class conditional_login_required: @@ -2895,3 +2901,192 @@ def dist_tasks_notification(request, task_id: int): # log.debug("reporting main_task_id: {}".format(task.main_task_id)) task.notificated = True + +@csrf_exempt +@api_view(["POST"]) +def yara_uploader(request): + try: + if not apiconf.yara_uploader.get("enabled"): + return Response({"error": True, "error_value": "Yara Uploader API is Disabled"}) + + category = request.data.get("category") + if not category or category not in ALLOWED_YARA_CATEGORIES: + return Response( + {"status": "error", "message": f"Invalid or missing category. Allowed categories: {ALLOWED_YARA_CATEGORIES}"}, + status=400, + ) + """ + if request.user.is_authenticated and request.user.username not in ALLOWED_UPLOADERS: + return Response( + {"status": "error", "message": f"User '{request.user.username}' is not authorized to upload YARA rules."}, status=403 + ) + """ + if "file" not in request.FILES: + return Response({"status": "error", "message": "No file provided"}, status=400) + + uploaded_file = request.FILES["file"] + + # Read content for processing + try: + content = uploaded_file.read().decode("utf-8") + except UnicodeDecodeError: + return Response({"status": "error", "message": "File must be a text file (UTF-8)"}, status=400) + + # Validate YARA + try: + yara.compile(source=content) + except yara.SyntaxError as e: + return Response({"status": "error", "message": f"YARA Syntax Error: {str(e)}"}, status=400) + except yara.Error as e: + return Response({"status": "error", "message": f"YARA Error: {str(e)}"}, status=400) + + try: + parser = plyara.Plyara() + rules = parser.parse_string(content) + + if not rules: + return Response({"status": "error", "message": "No YARA rules found in file"}, status=400) + + main_rule = rules[0] + + # Check for family + family = None + metadata = main_rule.get("metadata", []) + + for meta in metadata: + if "family" in meta: + family = meta["family"] + break + + if not family: + # Fallback: check cape_type + for meta in metadata: + if "cape_type" in meta: + cape_type_val = meta["cape_type"] + if cape_type_val and isinstance(cape_type_val, str): + family = cape_type_val.split(" ")[0] + break + + if not family: + return Response({"status": "error", "message": "Missing 'family' in metadata"}, status=400) + + # Now iterate all rules to inject cape_type / author if needed + for rule in rules: + rule_metadata = rule.get("metadata", []) + + has_cape_type = any("cape_type" in m for m in rule_metadata) + has_author = any("yara_created_by" in m for m in rule_metadata) # Using yara_created_by as key + + if not has_cape_type: + rule_metadata.append({"cape_type": f"{family} Payload"}) + + if request.user.is_authenticated and not has_author: + rule_metadata.append({"yara_created_by": request.user.username}) + + rule["metadata"] = rule_metadata + + # Define destination path + original_filename = os.path.basename(uploaded_file.name) # Basic safety + if category == "monitor": + dest_dir = os.path.join(CUCKOO_ROOT, "analyzer", "windows", "data", "yara") + else: + dest_dir = os.path.join(CUCKOO_ROOT, "data", "yara", category) + + # Ensure directory exists + if not os.path.exists(dest_dir): + os.makedirs(dest_dir, exist_ok=True) + + original_dest_path = os.path.join(dest_dir, original_filename) + + if os.path.exists(original_dest_path): + filename = original_filename + dest_path = original_dest_path + else: + # Fallback to standard naming + filename = f"{family}.yar" + dest_path = os.path.join(dest_dir, filename) + + # Check if file exists to append + if os.path.exists(dest_path): + with open(dest_path, "r", encoding="utf-8") as f: + existing_content = f.read() + + try: + existing_rules = parser.parse_string(existing_content) + existing_names = {r["rule_name"] for r in existing_rules} + + # Filter new rules + unique_rules = [] + for rule in rules: + if rule["rule_name"] not in existing_names: + unique_rules.append(rule) + + if not unique_rules: + # No new rules to add + msg = "All rules already exist. Nothing to add." + return Response({"status": "success", "message": msg}) + + append_content = "" + for rule in unique_rules: + append_content += "\n\n" + plyara.utils.rebuild_yara_rule(rule) + + content = existing_content + append_content + + except Exception as e: + return Response({"status": "error", "message": f"Failed to parse existing file for append: {str(e)}"}, status=500) + else: + # Rebuild content for new file + new_content = "" + for rule in rules: + new_content += plyara.utils.rebuild_yara_rule(rule) + "\n\n" + + content = new_content + + except Exception as e: + return Response({"status": "error", "message": f"Plyara parsing error: {str(e)}"}, status=400) + + # Save file + with open(dest_path, "w", encoding="utf-8") as f: + f.write(content) + + msg = "Rule saved! Thank you" + + # Distributed propagation + try: + if DIST_ENABLED: + # Prepare for propagation + files = {"file": (filename, content)} + with dist_session() as db_session: + nodes = db_session.execute(select(Node).where(Node.enabled.is_(True))).scalars().all() + + propagated_count = 0 + total_count = 0 + + for node in nodes: + total_count += 1 + prop_url = urljoin(node.url, "apiv2/yara_uploader/") + headers = {"Authorization": f"Token {node.apikey}"} + + try: + data = {"username": request.user.username, "category": category} + r = requests.post(prop_url, files=files, data=data, headers=headers, verify=False, timeout=10) + if r.status_code == 200: + propagated_count += 1 + except Exception: + pass + + msg += f" (Propagated to {propagated_count}/{total_count} workers)" + + except Exception as e: + msg += f" (Propagation failed: {str(e)})" + + return Response( + { + "status": "success", + "message": msg, + } + ) + + except Exception as e: + return Response({"status": "error", "message": str(e)}, status=500) + diff --git a/web/templates/submission/index.html b/web/templates/submission/index.html index 93d451b45f7..ed852350e95 100644 --- a/web/templates/submission/index.html +++ b/web/templates/submission/index.html @@ -564,6 +564,10 @@
Advance url URL for started browser + + background_processes + Number of hidden background processes to launch (default 1, 0 disables, max 10) + servicedesc Service description (Service package)