mirror of
https://github.com/Comfy-Org/ComfyUI-Manager.git
synced 2026-05-09 00:22:51 +08:00
Lets clients detect CSRF-POST backend support via ComfyUI core's feature_flags instead of parsing version strings. Absence of the flag indicates a Manager version < 4.2.1 that is incompatible with POST-only state-mutation endpoints. Follow-up to #2818; no endpoint or security behavior change.
133 lines
4.9 KiB
Python
133 lines
4.9 KiB
Python
import os
|
|
import logging
|
|
from aiohttp import web
|
|
from .common.manager_security import HANDLER_POLICY
|
|
from .common import manager_security
|
|
from comfy.cli_args import args
|
|
|
|
|
|
# Register server-push feature flag so ComfyUI_frontend (and other clients)
|
|
# can detect CSRF-POST backend capability as a semantic contract (vs version
|
|
# string parsing). See PR #2818 for context; clients use this flag to decide
|
|
# whether to invoke POST state-mutation endpoints. Manager versions prior to
|
|
# 4.2.1 do not set this flag — clients should treat its absence as
|
|
# 'incompatible with POST-only state-mutation endpoints'.
|
|
try:
|
|
from comfy_api import feature_flags as _core_feature_flags
|
|
_mgr_flags = (
|
|
_core_feature_flags.SERVER_FEATURE_FLAGS
|
|
.setdefault('extension', {})
|
|
.setdefault('manager', {})
|
|
)
|
|
_mgr_flags['supports_csrf_post'] = True
|
|
except ImportError:
|
|
# Older ComfyUI core without comfy_api.feature_flags module.
|
|
# Manager functions but clients will not observe the flag.
|
|
pass
|
|
|
|
|
|
def prestartup():
|
|
from . import prestartup_script # noqa: F401
|
|
logging.info('[PRE] ComfyUI-Manager')
|
|
|
|
|
|
def start():
|
|
logging.info('[START] ComfyUI-Manager')
|
|
from .common import cm_global # noqa: F401
|
|
|
|
if args.enable_manager:
|
|
if args.enable_manager_legacy_ui:
|
|
try:
|
|
from .legacy import manager_server # noqa: F401
|
|
from .legacy import share_3rdparty # noqa: F401
|
|
from .legacy import manager_core as core
|
|
import nodes
|
|
|
|
logging.info("[ComfyUI-Manager] Legacy UI is enabled.")
|
|
nodes.EXTENSION_WEB_DIRS['comfyui-manager-legacy'] = os.path.join(os.path.dirname(__file__), 'js')
|
|
except Exception as e:
|
|
# WI-V: upgraded silent `print` to a proper logging.error with
|
|
# traceback so future legacy-UI load failures are visible in
|
|
# the log, not swallowed. The original `print` could be lost
|
|
# depending on how stdout is captured.
|
|
import traceback
|
|
logging.error(
|
|
"[ComfyUI-Manager] Error enabling legacy frontend: "
|
|
f"{type(e).__name__}: {e}\n{traceback.format_exc()}"
|
|
)
|
|
core = None
|
|
else:
|
|
from .glob import manager_server # noqa: F401
|
|
from .glob import share_3rdparty # noqa: F401
|
|
from .glob import manager_core as core
|
|
|
|
if core is not None:
|
|
manager_security.is_personal_cloud_mode = core.get_config()['network_mode'].lower() == 'personal_cloud'
|
|
|
|
|
|
def should_be_disabled(fullpath:str) -> bool:
|
|
"""
|
|
1. Disables the legacy ComfyUI-Manager.
|
|
2. The blocklist can be expanded later based on policies.
|
|
"""
|
|
if args.enable_manager:
|
|
# In cases where installation is done via a zip archive, the directory name may not be comfyui-manager, and it may not contain a git repository.
|
|
# It is assumed that any installed legacy ComfyUI-Manager will have at least 'comfyui-manager' in its directory name.
|
|
dir_name = os.path.basename(fullpath).lower()
|
|
if 'comfyui-manager' in dir_name:
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
def get_client_ip(request):
|
|
peername = request.transport.get_extra_info("peername")
|
|
if peername is not None:
|
|
# Grab the first two values - there can be more, ie. with --listen
|
|
host, port = peername[:2]
|
|
return host
|
|
|
|
return "unknown"
|
|
|
|
|
|
def create_middleware():
|
|
connected_clients = set()
|
|
is_local_mode = manager_security.is_loopback(args.listen)
|
|
|
|
@web.middleware
|
|
async def manager_middleware(request: web.Request, handler):
|
|
nonlocal connected_clients
|
|
|
|
# security policy for remote environments
|
|
prev_client_count = len(connected_clients)
|
|
client_ip = get_client_ip(request)
|
|
connected_clients.add(client_ip)
|
|
next_client_count = len(connected_clients)
|
|
|
|
if prev_client_count == 1 and next_client_count > 1:
|
|
manager_security.multiple_remote_alert()
|
|
|
|
policy = manager_security.get_handler_policy(handler)
|
|
is_banned = False
|
|
|
|
# policy check
|
|
if len(connected_clients) > 1:
|
|
if is_local_mode:
|
|
if HANDLER_POLICY.MULTIPLE_REMOTE_BAN_NON_LOCAL in policy:
|
|
is_banned = True
|
|
if HANDLER_POLICY.MULTIPLE_REMOTE_BAN_NOT_PERSONAL_CLOUD in policy:
|
|
is_banned = not manager_security.is_personal_cloud_mode
|
|
|
|
if HANDLER_POLICY.BANNED in policy:
|
|
is_banned = True
|
|
|
|
if is_banned:
|
|
logging.warning(f"[Manager] Banning request from {client_ip}: {request.path}")
|
|
response = web.Response(text="[Manager] This request is banned.", status=403)
|
|
else:
|
|
response: web.Response = await handler(request)
|
|
|
|
return response
|
|
|
|
return manager_middleware
|
|
|