From ae1739f4b62d4dd716d94898f7a2c42a561e24a8 Mon Sep 17 00:00:00 2001 From: doomedraven Date: Wed, 11 Feb 2026 16:38:20 +0100 Subject: [PATCH 1/4] guac fix (#2903) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * guac fix ``` ✦ I have resolved the reported issues in the Guacamole web interface integration. 1. Fixed `guac-web.service` startup error: Updated web/web/asgi.py to explicitly register the http handler in ProtocolTypeRouter, complying with Channels 4.x requirements. 2. Fixed "slow and unresponsive" session: In web/guac/consumers.py, wrapped blocking GuacamoleClient methods (handshake and send) with sync_to_async. This prevents the synchronous calls from blocking the ASGI event loop, which was causing the lag and crashes. 3. Prevented infinite loops: Updated the open method in web/guac/consumers.py to correctly handle GuacamoleClient disconnection (EOF) by breaking the receive loop and closing the WebSocket, preventing high CPU usage on disconnect. The files web/web/asgi.py and web/guac/consumers.py have been updated and syntax checked. ``` * Apply suggestion from @gemini-code-assist[bot] Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * add daphne * fix * more fixes? * fix * fix * sync * Update consumers.py --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- conf/default/web.conf.default | 17 +++ poetry.lock | 80 ++++--------- pyproject.toml | 2 +- web/guac/consumers.py | 204 +++++++++++++++++++++++----------- web/guac/routing.py | 2 +- web/web/asgi.py | 51 +++++---- web/web/settings.py | 3 + 7 files changed, 214 insertions(+), 145 deletions(-) diff --git a/conf/default/web.conf.default b/conf/default/web.conf.default index e8bfe9b141e..665d1032bf6 100644 --- a/conf/default/web.conf.default +++ b/conf/default/web.conf.default @@ -197,12 +197,29 @@ vnc_host = localhost # You might need to add your server IP to ALLOWED_HOSTS in web/web/settings.py if it not ["*""] # vnc or rdp guest_protocol = vnc +# TIP: For KVM/QEMU, using 'qxl' or 'virtio' video drivers in your VM XML +# definition provides much better VNC performance than 'vga' or 'cirrus'. guacd_recording_path = /opt/CAPEv2/storage/guacrecordings guest_width = 1280 guest_height = 1024 # rdp settings guest_rdp_port = 3389 ignore_rdp_cert = false +# RDP Performance Optimizations (Yes = Disable feature for better performance) +rdp_disable_wallpaper = yes +rdp_disable_theming = yes +rdp_enable_font_smoothing = no +rdp_enable_full_window_drag = no +rdp_enable_desktop_composition = no +rdp_enable_menu_animations = no +# VNC Performance Optimizations +# Color depth: 8, 16, 24, 32. 16 is a great balance for performance. +vnc_color_depth = 16 +# Cursor: 'local' renders the mouse on your browser (feels instant). +# 'remote' waits for the server (feels laggy). +vnc_cursor = local +# Audio (enable only if needed, consumes bandwidth) +enable_audio = no [packages] # VM tags may be used to specify on which guest machines a sample should be run diff --git a/poetry.lock b/poetry.lock index ef1710580e0..64b8e862adb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -629,22 +629,23 @@ files = [ [[package]] name = "channels" -version = "3.0.5" -description = "Brings async, event-driven capabilities to Django. Django 2.2 and up only." +version = "4.2.2" +description = "Brings async, event-driven capabilities to Django." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" groups = ["main"] files = [ - {file = "channels-3.0.5-py3-none-any.whl", hash = "sha256:3813b8025bf85509769793aca720e6c3b1c5bde1cb253a961252bf0242b60a26"}, - {file = "channels-3.0.5.tar.gz", hash = "sha256:a3dc3339cc033e7c2afe083fb3dedf74fc5009815967e317e080e7bfdc92ea26"}, + {file = "channels-4.2.2-py3-none-any.whl", hash = "sha256:ff36a6e1576cacf40bcdc615fa7aece7a709fc4fdd2dc87f2971f4061ffdaa81"}, + {file = "channels-4.2.2.tar.gz", hash = "sha256:8d7208e48ab8fdb972aaeae8311ce920637d97656ffc7ae5eca4f93f84bcd9a0"}, ] [package.dependencies] -asgiref = ">=3.5.0,<4" -daphne = ">=3.0,<4" -Django = ">=2.2" +asgiref = ">=3.6.0,<4" +daphne = {version = ">=4.0.0", optional = true, markers = "extra == \"daphne\""} +Django = ">=4.2" [package.extras] +daphne = ["daphne (>=4.0.0)"] tests = ["async-timeout", "coverage (>=4.5,<5.0)", "pytest", "pytest-asyncio", "pytest-django"] [[package]] @@ -1099,23 +1100,23 @@ files = [ [[package]] name = "daphne" -version = "3.0.2" +version = "4.2.1" description = "Django ASGI (HTTP/WebSocket) server" optional = false -python-versions = ">=3.6" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "daphne-3.0.2-py3-none-any.whl", hash = "sha256:a9af943c79717bc52fe64a3c236ae5d3adccc8b5be19c881b442d2c3db233393"}, - {file = "daphne-3.0.2.tar.gz", hash = "sha256:76ffae916ba3aa66b46996c14fa713e46004788167a4873d647544e750e0e99f"}, + {file = "daphne-4.2.1-py3-none-any.whl", hash = "sha256:881e96b387b95b35ad85acd855f229d7f5b79073d6649089c8a33f661885e055"}, + {file = "daphne-4.2.1.tar.gz", hash = "sha256:5f898e700a1fda7addf1541d7c328606415e96a7bd768405f0463c312fcb31b3"}, ] [package.dependencies] -asgiref = ">=3.2.10,<4" -autobahn = ">=0.18" -twisted = {version = ">=18.7", extras = ["tls"]} +asgiref = ">=3.5.2,<4" +autobahn = ">=22.4.2" +twisted = {version = ">=22.4", extras = ["tls"]} [package.extras] -tests = ["hypothesis (==4.23)", "pytest (>=3.10,<4.0)", "pytest-asyncio (>=0.8,<1.0)"] +tests = ["black", "django", "flake8", "flake8-bugbear", "hypothesis", "mypy", "pytest", "pytest-asyncio", "pytest-cov", "tox"] [[package]] name = "deprecation" @@ -2137,24 +2138,6 @@ typing-extensions = ">=4.12,<5.0" [package.extras] protobuf = ["grpcio-tools (>=1.76.0)"] -[[package]] -name = "grpcio-status" -version = "1.71.2" -description = "Status proto mapping for gRPC" -optional = true -python-versions = ">=3.9" -groups = ["main"] -markers = "python_version <= \"3.13\" and extra == \"gcp\"" -files = [ - {file = "grpcio_status-1.71.2-py3-none-any.whl", hash = "sha256:803c98cb6a8b7dc6dbb785b1111aed739f241ab5e9da0bba96888aa74704cfd3"}, - {file = "grpcio_status-1.71.2.tar.gz", hash = "sha256:c7a97e176df71cdc2c179cd1847d7fc86cca5832ad12e9798d7fed6b7a1aab50"}, -] - -[package.dependencies] -googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.71.2" -protobuf = ">=5.26.1,<6.0dev" - [[package]] name = "grpcio-status" version = "1.76.0" @@ -2162,7 +2145,7 @@ description = "Status proto mapping for gRPC" optional = true python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.14\" and extra == \"gcp\"" +markers = "extra == \"gcp\"" files = [ {file = "grpcio_status-1.76.0-py3-none-any.whl", hash = "sha256:380568794055a8efbbd8871162df92012e0228a5f6dffaf57f2a00c534103b18"}, {file = "grpcio_status-1.76.0.tar.gz", hash = "sha256:25fcbfec74c15d1a1cb5da3fab8ee9672852dc16a5a9eeb5baf7d7a9952943cd"}, @@ -4098,28 +4081,6 @@ protobuf = ">=3.19.0,<7.0.0" [package.extras] testing = ["google-api-core (>=1.31.5)"] -[[package]] -name = "protobuf" -version = "5.29.3" -description = "" -optional = false -python-versions = ">=3.8" -groups = ["main"] -markers = "python_version <= \"3.13\"" -files = [ - {file = "protobuf-5.29.3-cp310-abi3-win32.whl", hash = "sha256:3ea51771449e1035f26069c4c7fd51fba990d07bc55ba80701c78f886bf9c888"}, - {file = "protobuf-5.29.3-cp310-abi3-win_amd64.whl", hash = "sha256:a4fa6f80816a9a0678429e84973f2f98cbc218cca434abe8db2ad0bffc98503a"}, - {file = "protobuf-5.29.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8434404bbf139aa9e1300dbf989667a83d42ddda9153d8ab76e0d5dcaca484e"}, - {file = "protobuf-5.29.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:daaf63f70f25e8689c072cfad4334ca0ac1d1e05a92fc15c54eb9cf23c3efd84"}, - {file = "protobuf-5.29.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:c027e08a08be10b67c06bf2370b99c811c466398c357e615ca88c91c07f0910f"}, - {file = "protobuf-5.29.3-cp38-cp38-win32.whl", hash = "sha256:84a57163a0ccef3f96e4b6a20516cedcf5bb3a95a657131c5c3ac62200d23252"}, - {file = "protobuf-5.29.3-cp38-cp38-win_amd64.whl", hash = "sha256:b89c115d877892a512f79a8114564fb435943b59067615894c3b13cd3e1fa107"}, - {file = "protobuf-5.29.3-cp39-cp39-win32.whl", hash = "sha256:0eb32bfa5219fc8d4111803e9a690658aa2e6366384fd0851064b963b6d1f2a7"}, - {file = "protobuf-5.29.3-cp39-cp39-win_amd64.whl", hash = "sha256:6ce8cc3389a20693bfde6c6562e03474c40851b44975c9b2bf6df7d8c4f864da"}, - {file = "protobuf-5.29.3-py3-none-any.whl", hash = "sha256:0a18ed4a24198528f2333802eb075e59dea9d679ab7a6c5efb017a59004d849f"}, - {file = "protobuf-5.29.3.tar.gz", hash = "sha256:5da0f41edaf117bde316404bad1a486cb4ededf8e4a54891296f648e8e076620"}, -] - [[package]] name = "protobuf" version = "6.33.4" @@ -4127,7 +4088,6 @@ description = "" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.14\"" files = [ {file = "protobuf-6.33.4-cp310-abi3-win32.whl", hash = "sha256:918966612c8232fc6c24c78e1cd89784307f5814ad7506c308ee3cf86662850d"}, {file = "protobuf-6.33.4-cp310-abi3-win_amd64.whl", hash = "sha256:8f11ffae31ec67fc2554c2ef891dcb561dae9a2a3ed941f9e134c2db06657dbc"}, @@ -6921,4 +6881,4 @@ maco = ["maco"] [metadata] lock-version = "2.1" python-versions = ">=3.10, <4.0" -content-hash = "6c5dc0d0893e137fefd9aeb8180be1ee168a4815aa3529ae4482f15581ed66f6" +content-hash = "f034d453baf2de501d8b1475d394670f7f459b98107eea75d01191d90a12bb80" diff --git a/pyproject.toml b/pyproject.toml index 16a203e9e56..43718f41946 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,7 +85,7 @@ dependencies = [ "pyguacamole>=0.11", "uvicorn[standard]>=0.18.2", "gunicorn>=23.0.0", - "channels>=3.0.5", + "channels[daphne]>=4.0.0", "setproctitle==1.3.2", "CAPE-parsers>=0.1.36", "maco==1.1.8", diff --git a/web/guac/consumers.py b/web/guac/consumers.py index fb966d16727..d6f08fd2904 100644 --- a/web/guac/consumers.py +++ b/web/guac/consumers.py @@ -6,90 +6,170 @@ from channels.generic.websocket import AsyncWebsocketConsumer from guacamole.client import GuacamoleClient +# Ensure this import path matches your project structure from lib.cuckoo.common.config import Config logger = logging.getLogger("guac-session") web_cfg = Config("web") - class GuacamoleWebSocketConsumer(AsyncWebsocketConsumer): - client = None - task = None + # Channels 4: Explicitly declare supported subprotocols + subprotocols = ["guacamole"] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.client = None + self.task = None async def connect(self): """ Initiate the GuacamoleClient and create a connection to it. """ - guacd_hostname = web_cfg.guacamole.guacd_host or "localhost" - guacd_port = int(web_cfg.guacamole.guacd_port) or 4822 - guacd_recording_path = web_cfg.guacamole.guacd_recording_path or "" - guest_protocol = web_cfg.guacamole.guest_protocol or "vnc" - guest_width = int(web_cfg.guacamole.guest_width) or 1280 - guest_height = int(web_cfg.guacamole.guest_height) or 1024 - guest_username = web_cfg.guacamole.username or "" - guest_password = web_cfg.guacamole.password or "" - - params = urllib.parse.parse_qs(self.scope["query_string"].decode()) - - if "rdp" in guest_protocol: - hosts = params.get("guest_ip", "") - guest_host = hosts[0] - guest_port = int(web_cfg.guacamole.guest_rdp_port) or 3389 - ignore_cert = "true" if web_cfg.guacamole.ignore_rdp_cert is True else "false" - else: - guest_host = web_cfg.guacamole.vnc_host or "localhost" - ports = params.get("vncport", ["5900"]) - guest_port = int(ports[0]) - ignore_cert = "false" - - guacd_recording_name = params.get("recording_name", ["task-recording"])[0] - - self.client = GuacamoleClient(guacd_hostname, guacd_port) - - self.client.handshake( - protocol=guest_protocol, - width=guest_width, - height=guest_height, - hostname=guest_host, - port=guest_port, - username=guest_username, - password=guest_password, - recording_path=guacd_recording_path, - recording_name=guacd_recording_name, - ignore_cert=ignore_cert, - ) - - if self.client.connected: - # start receiving data from GuacamoleClient - loop = asyncio.get_event_loop() - self.task = loop.create_task(self.open()) - - # Accept connection - await self.accept(subprotocol="guacamole") - else: + try: + # Capture session_id from URL route for logging context + session_id = self.scope["url_route"]["kwargs"].get("session_id", "unknown") + + # 1. Parse Configuration & Parameters inside a try block + # This prevents 500 errors from reaching the client as HTML + guacd_hostname = web_cfg.guacamole.guacd_host or "localhost" + guacd_port = int(web_cfg.guacamole.guacd_port) or 4822 + guacd_recording_path = web_cfg.guacamole.guacd_recording_path or "" + guest_protocol = web_cfg.guacamole.guest_protocol or "vnc" + guest_width = int(web_cfg.guacamole.guest_width) or 1280 + guest_height = int(web_cfg.guacamole.guest_height) or 1024 + guest_username = web_cfg.guacamole.username or "" + guest_password = web_cfg.guacamole.password or "" + + # Safe decoding of query string + query_string = self.scope.get("query_string", b"").decode() + params = urllib.parse.parse_qs(query_string) + + if "rdp" in guest_protocol: + hosts = params.get("guest_ip", [""]) + guest_host = hosts[0] + guest_port = int(web_cfg.guacamole.guest_rdp_port) or 3389 + ignore_cert = "true" if web_cfg.guacamole.ignore_rdp_cert is True else "false" + + # RDP Performance Optimizations + # Default to safe/fast values if not present in config + disable_wallpaper = "true" if getattr(web_cfg.guacamole, "rdp_disable_wallpaper", "yes") == "yes" else "false" + disable_theming = "true" if getattr(web_cfg.guacamole, "rdp_disable_theming", "yes") == "yes" else "false" + enable_font_smoothing = "true" if getattr(web_cfg.guacamole, "rdp_enable_font_smoothing", "no") == "yes" else "false" + enable_full_window_drag = "true" if getattr(web_cfg.guacamole, "rdp_enable_full_window_drag", "no") == "yes" else "false" + enable_desktop_composition = "true" if getattr(web_cfg.guacamole, "rdp_enable_desktop_composition", "no") == "yes" else "false" + enable_menu_animations = "true" if getattr(web_cfg.guacamole, "rdp_enable_menu_animations", "no") == "yes" else "false" + enable_audio = "audio" if getattr(web_cfg.guacamole, "enable_audio", "no") == "yes" else None + + extra_args = { + "disable-wallpaper": disable_wallpaper, + "disable-theming": disable_theming, + "enable-font-smoothing": enable_font_smoothing, + "enable-full-window-drag": enable_full_window_drag, + "enable-desktop-composition": enable_desktop_composition, + "enable-menu-animations": enable_menu_animations, + } + if enable_audio: + extra_args["enable-audio"] = "true" + + else: + guest_host = web_cfg.guacamole.vnc_host or "localhost" + ports = params.get("vncport", ["5900"]) + guest_port = int(ports[0]) + ignore_cert = "false" + + # VNC Performance Optimizations + vnc_color_depth = str(getattr(web_cfg.guacamole, "vnc_color_depth", 16)) + vnc_cursor = getattr(web_cfg.guacamole, "vnc_cursor", "local") + + extra_args = { + "color-depth": vnc_color_depth, + "cursor": vnc_cursor, + } + + guacd_recording_name = params.get("recording_name", ["task-recording"])[0] + + # 2. Connect to Guacamole Daemon (guacd) + self.client = GuacamoleClient(guacd_hostname, guacd_port) + + await sync_to_async(self.client.handshake)( + protocol=guest_protocol, + width=guest_width, + height=guest_height, + hostname=guest_host, + port=guest_port, + username=guest_username, + password=guest_password, + recording_path=guacd_recording_path, + recording_name=guacd_recording_name, + ignore_cert=ignore_cert, + **extra_args + ) + + if self.client.connected: + # 3. Accept the WebSocket connection specifically for 'guacamole' + # Accept first to ensure the channel is open before sending data + await self.accept(subprotocol="guacamole") + logger.info("Guacamole connection accepted for session %s.", session_id) + + # 4. Start the background reader task + # Use asyncio.create_task instead of get_event_loop() + self.task = asyncio.create_task(self.read_guacd()) + else: + logger.warning("Guacamole handshake failed. Closing connection.") + await self.close() + + except Exception as e: + logger.error("Error during Guacamole connect: %s", str(e)) await self.close() async def disconnect(self, code): """ Close the GuacamoleClient connection on WebSocket disconnect. """ - self.task.cancel() - await sync_to_async(self.client.close)() + # Cancel the reader task if it exists + if self.task: + self.task.cancel() + try: + await self.task + except asyncio.CancelledError: + pass + + # Close the client safely + if self.client: + try: + await sync_to_async(self.client.close)() + except Exception as e: + logger.error("Error closing guacamole client: %s", str(e)) async def receive(self, text_data=None, bytes_data=None): """ Handle data received in the WebSocket, send to GuacamoleClient. """ - if text_data is not None: - logger.debug("To server: %s", text_data) - self.client.send(text_data) - - async def open(self): + if text_data and self.client: + # logger.debug("To server: %s", text_data) # Verbose logging can slow down RDP + try: + await sync_to_async(self.client.send)(text_data) + except Exception as e: + logger.error("Failed to send data to guacd: %s", str(e)) + + async def read_guacd(self): """ Receive data from GuacamoleClient and pass it to the WebSocket """ - while True: - content = await sync_to_async(self.client.receive)() - if content: - logger.debug("From server: %s", content) - await self.send(text_data=content) + try: + while True: + # This blocks in a thread, releasing the async loop + # thread_sensitive=False allows this to run in a separate thread pool, not blocking the main thread + content = await sync_to_async(self.client.receive, thread_sensitive=False)() + if content: + # logger.debug("From server: %s", content) + await self.send(text_data=content) + else: + break + except asyncio.CancelledError: + pass # Task cancellation is normal on disconnect + except Exception as e: + logger.error("Exception in Guacamole message loop: %s", e) + finally: + # Ensure we close the websocket if the guacd connection dies + await self.close() diff --git a/web/guac/routing.py b/web/guac/routing.py index 1d9f1b8027f..6ea31022a96 100644 --- a/web/guac/routing.py +++ b/web/guac/routing.py @@ -4,7 +4,7 @@ websocket_urlpatterns = [ re_path( - r"guac/websocket-tunnel/(?P\w+)", + r"^guac/websocket-tunnel/(?P\w+)/?$", GuacamoleWebSocketConsumer.as_asgi(), ), ] diff --git a/web/web/asgi.py b/web/web/asgi.py index 3eef46d26cb..13add491895 100644 --- a/web/web/asgi.py +++ b/web/web/asgi.py @@ -1,37 +1,46 @@ -# Copyright (C) 2010-2015 Cuckoo Foundation. -# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org -# See the file 'docs/LICENSE' for copying permission. - """ ASGI config for web project. -Please read https://channels.readthedocs.io/en/latest/deploying.html#nginx-supervisor-ubuntu """ - import sys - -# These lines ensure that imports used by the ASGI daemon can be found -from os import chdir, environ +import os from os.path import abspath, dirname, join -from channels.auth import AuthMiddlewareStack -from channels.routing import ProtocolTypeRouter, URLRouter -from django.core.asgi import get_asgi_application +# --- 1. SETUP PATHS FIRST (Moved from bottom to top) --- +# Add / and /web (relative to CAPE/Cuckoo install location) to our path +# This ensures imports below can actually find the modules. +current_dir = dirname(abspath(__file__)) # The directory this file is in +webdir = abspath(join(current_dir, "..")) # The parent directory (web) + +sys.path.append(abspath(join(webdir, ".."))) # Add CAPE root +sys.path.append(webdir) # Add web root +os.chdir(webdir) # Change working directory -environ.setdefault("DJANGO_SETTINGS_MODULE", "web.guac_settings") +# --- 2. DJANGO SETUP --- +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "web.guac_settings") +# Initialize Django ASGI application early to ensure the AppRegistry +# is populated before importing code that may import ORM models. +from django.core.asgi import get_asgi_application django_asgi_app = get_asgi_application() +# --- 3. CHANNELS IMPORTS --- +from channels.auth import AuthMiddlewareStack +from channels.routing import ProtocolTypeRouter, URLRouter +from channels.security.websocket import AllowedHostsOriginValidator # Import this + +# Import local routing after Django is setup import guac.routing +# --- 4. APPLICATION DEFINITION --- application = ProtocolTypeRouter( { - "websocket": AuthMiddlewareStack(URLRouter(guac.routing.websocket_urlpatterns)), + "http": django_asgi_app, + # Wrap the websocket router in AllowedHostsOriginValidator + # This prevents 403 Forbidden errors that confuse the Guacamole client + "websocket": AllowedHostsOriginValidator( + AuthMiddlewareStack( + URLRouter(guac.routing.websocket_urlpatterns) + ) + ), } ) - -# Add / and /web (relative to CAPE install location) to our path -webdir = abspath(join(dirname(abspath(__file__)), "..")) -sys.path.append(abspath(join(webdir, ".."))) -sys.path.append(webdir) - -chdir(webdir) diff --git a/web/web/settings.py b/web/web/settings.py index b285414c582..103a2588b00 100644 --- a/web/web/settings.py +++ b/web/web/settings.py @@ -231,8 +231,11 @@ # Python dotted path to the WSGI application used by Django's runserver_plus. WSGI_APPLICATION = "web.wsgi.application" +# Ensure ASGI_APPLICATION points to your updated file +ASGI_APPLICATION = "web.asgi.application" INSTALLED_APPS = [ + "daphne", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", From be9a41b6b2cfe6c5e9652fbdaeb18344e2c08625 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 11 Feb 2026 15:39:00 +0000 Subject: [PATCH 2/4] ci: Update requirements.txt --- requirements.txt | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/requirements.txt b/requirements.txt index a4213b2131b..23603b775bd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -271,9 +271,9 @@ cffi==1.17.1 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99 \ --hash=sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87 \ --hash=sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b -channels==3.0.5 ; python_version >= "3.10" and python_version < "4.0" \ - --hash=sha256:3813b8025bf85509769793aca720e6c3b1c5bde1cb253a961252bf0242b60a26 \ - --hash=sha256:a3dc3339cc033e7c2afe083fb3dedf74fc5009815967e317e080e7bfdc92ea26 +channels==4.2.2 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:8d7208e48ab8fdb972aaeae8311ce920637d97656ffc7ae5eca4f93f84bcd9a0 \ + --hash=sha256:ff36a6e1576cacf40bcdc615fa7aece7a709fc4fdd2dc87f2971f4061ffdaa81 chardet==4.0.0 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa \ --hash=sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5 @@ -490,9 +490,9 @@ cython==3.0.11 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:eeb6860b0f4bfa402de8929833fe5370fa34069c7ebacb2d543cb017f21fb891 \ --hash=sha256:f3953d2f504176f929862e5579cfc421860c33e9707f585d70d24e1096accdf7 \ --hash=sha256:f988f7f8164a6079c705c39e2d75dbe9967e3dacafe041420d9af7b9ee424162 -daphne==3.0.2 ; python_version >= "3.10" and python_version < "4.0" \ - --hash=sha256:76ffae916ba3aa66b46996c14fa713e46004788167a4873d647544e750e0e99f \ - --hash=sha256:a9af943c79717bc52fe64a3c236ae5d3adccc8b5be19c881b442d2c3db233393 +daphne==4.2.1 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:5f898e700a1fda7addf1541d7c328606415e96a7bd768405f0463c312fcb31b3 \ + --hash=sha256:881e96b387b95b35ad85acd855f229d7f5b79073d6649089c8a33f661885e055 deprecation==2.1.0 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff \ --hash=sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a @@ -1748,19 +1748,7 @@ propcache==0.4.1 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88 \ --hash=sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48 \ --hash=sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781 -protobuf==5.29.3 ; python_version >= "3.10" and python_version <= "3.13" \ - --hash=sha256:0a18ed4a24198528f2333802eb075e59dea9d679ab7a6c5efb017a59004d849f \ - --hash=sha256:0eb32bfa5219fc8d4111803e9a690658aa2e6366384fd0851064b963b6d1f2a7 \ - --hash=sha256:3ea51771449e1035f26069c4c7fd51fba990d07bc55ba80701c78f886bf9c888 \ - --hash=sha256:5da0f41edaf117bde316404bad1a486cb4ededf8e4a54891296f648e8e076620 \ - --hash=sha256:6ce8cc3389a20693bfde6c6562e03474c40851b44975c9b2bf6df7d8c4f864da \ - --hash=sha256:84a57163a0ccef3f96e4b6a20516cedcf5bb3a95a657131c5c3ac62200d23252 \ - --hash=sha256:a4fa6f80816a9a0678429e84973f2f98cbc218cca434abe8db2ad0bffc98503a \ - --hash=sha256:a8434404bbf139aa9e1300dbf989667a83d42ddda9153d8ab76e0d5dcaca484e \ - --hash=sha256:b89c115d877892a512f79a8114564fb435943b59067615894c3b13cd3e1fa107 \ - --hash=sha256:c027e08a08be10b67c06bf2370b99c811c466398c357e615ca88c91c07f0910f \ - --hash=sha256:daaf63f70f25e8689c072cfad4334ca0ac1d1e05a92fc15c54eb9cf23c3efd84 -protobuf==6.33.4 ; python_version >= "3.14" and python_version < "4.0" \ +protobuf==6.33.4 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:0f12ddbf96912690c3582f9dffb55530ef32015ad8e678cd494312bd78314c4f \ --hash=sha256:1fe3730068fcf2e595816a6c34fe66eeedd37d51d0400b72fabc848811fdc1bc \ --hash=sha256:2fe67f6c014c84f655ee06f6f66213f9254b3a8b6bda6cda0ccd4232c73c06f0 \ @@ -2638,9 +2626,9 @@ unicorn==2.1.1 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:b0f139adb1c9406f57d25cab96ad7a6d3cbb9119f5480ebecedd4f5d7cb024fb \ --hash=sha256:d4a08dbf222c5481bc909a9aa404b79874f6e67f5ba7c47036d03c68ab7371a7 \ --hash=sha256:f0ebcfaba67ef0ebcd05ee3560268f1c6f683bdd08ff496888741a163d29735d -urllib3==2.6.3 ; python_version >= "3.10" and python_version < "4.0" \ - --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \ - --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4 +urllib3==2.3.0 ; python_version >= "3.10" and python_version < "4.0" \ + --hash=sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df \ + --hash=sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d uvicorn==0.18.3 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:0abd429ebb41e604ed8d2be6c60530de3408f250e8d2d84967d85ba9e86fe3af \ --hash=sha256:9a66e7c42a2a95222f76ec24a4b754c158261c4696e683b9dadc72b590e0311b From f20b0fab306d7fbfe8a510e48e86680092963fa5 Mon Sep 17 00:00:00 2001 From: William Metcalf Date: Wed, 11 Feb 2026 10:25:08 -0600 Subject: [PATCH 3/4] Add additional Java runtime search paths to jar package (#2911) * ci: Update requirements.txt * Add additional Java runtime search paths to jar package The jar analysis package only searched for Oracle JRE (Java/jre*). This adds fallback paths for Microsoft OpenJDK, Eclipse Adoptium Temurin (JDK and JRE), Oracle JDK, and the deprecated OpenJDK Chocolatey package. Oracle paths remain first to preserve existing behavior. Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: GitHub Actions Co-authored-by: Claude Opus 4.6 --- analyzer/windows/modules/packages/jar.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/analyzer/windows/modules/packages/jar.py b/analyzer/windows/modules/packages/jar.py index 9b4917eac80..d2d5fef6d4e 100644 --- a/analyzer/windows/modules/packages/jar.py +++ b/analyzer/windows/modules/packages/jar.py @@ -11,6 +11,12 @@ class Jar(Package): PATHS = [ ("ProgramFiles", "Java", "jre*", "bin", "java.exe"), + ("ProgramFiles", "Java", "jdk*", "bin", "java.exe"), + ("ProgramFiles", "Java", "jdk-*", "bin", "java.exe"), + ("ProgramFiles", "Microsoft", "jdk-*", "bin", "java.exe"), + ("ProgramFiles", "Eclipse Adoptium", "jdk-*", "bin", "java.exe"), + ("ProgramFiles", "Eclipse Adoptium", "jre-*", "bin", "java.exe"), + ("ProgramFiles", "OpenJDK", "jdk-*", "bin", "java.exe"), ] summary = "Executes a java class using java.exe." description = f"""Uses 'java.exe -jar [path]' to run the given sample. From a5eab8570fa61bd93a3297b0bea3fcc002363114 Mon Sep 17 00:00:00 2001 From: doomedraven Date: Wed, 11 Feb 2026 17:48:21 +0100 Subject: [PATCH 4/4] docs: update changelog to mention daphne dependency --- changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/changelog.md b/changelog.md index 3b42169a879..30169c4f3d7 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,7 @@ +### [11.02.2026] +* Guacamole integration: + * New dependency `channels[daphne]>=4.0.0` added. + ### [04.02.2026] * Network Analysis: * Integrated process mapping directly into `network` processing module.