* feat(security): add dedicated install flags decoupled from security_level
Gate 'install via git URL' and 'install via pip' with dedicated opt-in
boolean flags (allow_git_url_install / allow_pip_install) in config.ini
[default], fully replacing the security_level term on those surfaces
(REPLACE, not AND — a strict level no longer denies when the flag is on;
a weak level no longer allows when the flag is off).
- glob/manager_server.py: pure predicate is_dedicated_install_allowed
(flag AND loopback, request-time args.listen); REPLACE gates at
/customnode/install/git_url and /customnode/install/pip; batch
unknown-URL arm routes through the same full predicate at the risky
position (loopback term is load-bearing — the middle entry gate has
no network-position term; the entry gate itself stays in force);
unknown-pip in batch stays unconditionally blocked; new
SECURITY_MESSAGE_FLAG_* denial constants name the responsible flag;
security_403_response gains flag_token (comfyui_outdated keeps precedence)
- glob/manager_core.py: register both keys (read via get_bool default-false,
write list, exception fallback); "true"-only truthy; restart-only activation
- js/common.js: 403 dialog copy names the responsible flag at the two
install call sites
- README.md: security-policy docs for both flags (per-surface scope incl.
the batch entry-gate qualifier, REPLACE decoupling, loopback bound,
opt-in config snippet, default-deny + migration note); stale tier lists
corrected against the actual gates
- CHANGELOG.md: opt-in migration note + accepted residual risk (flags
bypass the forced-strong outdated-ComfyUI hardening on loopback,
opt-in only), decoupling claim qualified for the batch entry gate
Tests: unit suite (predicate truth table, REPLACE litmus both directions,
AST binding-proofs against live handlers, subprocess-isolated config
contract) plus a real-server E2E suite that mounts the Manager-under-test
via git worktree (exact-SHA pin, detached) against a real ComfyUI and
exercises both flag surfaces and both arms — deny arms (403 + flag-naming
body/log + no install artifact), git-URL allow arm (real clone), pip allow
arm as a two-phase reservation oracle — with zero-residual self-clean.
Module skips without E2E_COMFYUI_ROOT; unit suite unaffected.
The manager-v4 branch ships the identical policy (shared invariants +
config contract); this tree uses the degraded predicate 'flag AND
loopback' (no personal_cloud-equivalent mode here).
* bump version to v3.41
Defense-in-depth over GET→POST alone: reject the three CORS-safelisted
simple-form Content-Types (x-www-form-urlencoded, multipart/form-data,
text/plain) on 5 no-body POST handlers (snapshot/save,
manager/queue/{reset,start,update_comfyui}, manager/reboot) to block
<form method=POST> CSRF that bypasses method-only gating. Convert 10 pure
state-changing endpoints (fetch_updates, queue/{update_all,reset,start,
update_comfyui}, snapshot/{remove,restore,save}, comfyui_switch_version,
reboot) from GET to POST and split 5 config endpoints
(db_mode/preview_method/channel_url_list/policy/{component,update}) into
GET(read) + POST(write, JSON body). Emit the in_progress + done event pair
from the /manager/queue/install sync-enable fast-path so client UI
finalizes (previously only queue/start's empty worker done fired, leaving
item.restart unset and the Enable button visible after a successful enable).
Harden js/custom-nodes-manager.js completion path: await onQueueCompleted
with try/catch (surfaces silent turbogrid stale-item throws), replace the
{}.length == 0 no-op empty guard, set install_context before queue/install
to avoid a sync-completion race, wrap classList/updateCell in try/catch.
Resynchronize openapi.yaml with the converted routes (method → post, query
params → requestBody JSON schema, sibling post on 5 split endpoints).
Update 31 JS fetchApi call sites across 7 files; add
tests/test_csrf_content_type_helper.py covering 5 Content-Type cases via
aiohttp TestClient.
Reported-by: XlabAI Team of Tencent Xuanwu Lab
CVSS: 8.1 (AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H)
- Add litellm==1.82.7 and litellm==1.82.8 to blacklist (PYSEC-2026-2)
- Add ultralytics==8.3.42 to blacklist
- Replace substring matching with exact version set matching
- Remove early break to detect multiple malicious packages
- Add detection for ComfyUI PR #11261 (per-queue preview override)
- Return DISABLED status when native feature is detected
- Improve UI loading state and prevent flash of enabled state
- Add accessibility attributes and visual feedback for disabled state
- Show user notification when feature transitions to native
- Version bump to 3.39
* Improve comfyui version listing
* Fix ComfyUI semver selection and stable update
* Fix nightly current detection on default branch
* Fix: use tag_ref.name explicitly and cache get_remote_name result
- Use tag_ref.name instead of tag_ref object for checkout
- Cache get_remote_name() result to avoid duplicate calls
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Dr.Lt.Data <dr.lt.data@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Use --ff-only flag to detect non-fast-forward situations
- Create backup branch before resetting divergent local branch
- Reset to remote branch when fast-forward is not possible
- Use time.strftime() instead of datetime for better compatibility
- Bump version to 3.38.2
- Migrate Manager data path: default/ComfyUI-Manager → __manager
- Force security_level=strong on outdated ComfyUI (block installations)
- Auto-migrate config.ini only; backup legacy files for manual verification
- Raise weak/normal- to normal during migration
- Add /manager/startup_alerts API for UI warnings
- Differentiate 403 responses: comfyui_outdated vs security_level
- Block startup scripts execution on old ComfyUI
Requires ComfyUI v0.3.76+ for full functionality.
Backward compatible with older versions (uses legacy path).
* Previously, only `uv` installed inside a venv was properly handled. Now `uv` installed outside the venv is also supported.
* Even if `use_uv=False`, `uv` is used as a fallback when `pip` is unavailable.
* Even if `use_uv=True`, `pip` is used as a fallback when `uv` is unavailable.
https://github.com/Comfy-Org/ComfyUI-Manager/issues/2125
* set channel=default, mode=cache for git clone
* fix(manager_util): use normalized_name of package in fix_broken
Signed-off-by: bigcat88 <bigcat88@icloud.com>
---------
Signed-off-by: bigcat88 <bigcat88@icloud.com>