ComfyUI-Manager/reports/scenario_intents.md
Dr.Lt.Data 4410ebc6a6
Some checks are pending
Publish to PyPI / build-and-publish (push) Waiting to run
Python Linting / Run Ruff (push) Waiting to run
fix(security): harden CSRF with Content-Type gate and expand E2E coverage (#2818)
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 16 no-body POST handlers (glob + legacy) to block
<form method=POST> CSRF that bypasses method-only gating. Move
comfyui_switch_version to a JSON body so the preflight requirement applies.
Split db_mode/policy/update/channel_url_list into GET(read) + POST(write).
Tighten do_fix (high → high+) and gate three previously-ungated config
setters at middle. Resynchronize openapi.yaml (27 paths, 30 operations,
ComfyUISwitchVersionParams as a shared $ref component). Add E2E harness
variants, Playwright config, CSRF/secgate suites, 39-endpoint coverage,
and a CHANGELOG.

Breaking: legacy per-op POST routes (install/uninstall/fix/disable/update/
reinstall/abort_current) are removed; callers already use queue/batch.
Legacy /manager/notice (v1) is removed; /v2/manager/notice is retained.

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)
2026-04-22 05:04:30 +09:00

425 lines
16 KiB
Markdown

# Scenario Intent Mapping
**Generated**: 2026-04-18
**Definition of "intent"**: For each scenario — **what real use case, user need, or protection concern does this scenario represent?** Answers "why does this scenario matter, what is it there to prove?"
Intent categories used:
- **User capability** — the user wants to accomplish task X
- **Data integrity** — the system must not corrupt state
- **Security boundary** — privilege / access must be enforced
- **Input resilience** — bad input must not crash or mis-operate
- **Idempotency** — operation can be retried safely
- **Observability** — the caller needs accurate state visibility
- **Concurrency safety** — parallel calls don't interfere
- **Recovery** — system can recover from failure / bad state
---
# Section 1 — Glob v2 Endpoints
## 1.1 Queue Management
### POST /v2/manager/queue/task (install)
| Scenario | Intent |
|---|---|
| Success (CNR) | User capability: install a registered pack at a specific version for reproducibility |
| Success (nightly/URL) | User capability: install unreleased or private pack from arbitrary git URL |
| Success (skip_post_install + already disabled) | Recovery: re-enable a previously disabled pack without full reinstall (optimization path) |
| Validation error (bad kind) | Input resilience: prevent arbitrary op execution via malformed kind; ensure schema gate is the truth |
| Validation error (missing ui_id/client_id) | Observability: every queued task must be traceable back to its originator |
| Invalid JSON body | Input resilience: malformed bytes don't crash the server |
| Worker auto-start | User capability: ease of use — installer doesn't need separate "start" call (legacy path does though) |
### POST /v2/manager/queue/task (uninstall)
| Scenario | Intent |
|---|---|
| Success | User capability: remove a pack that's no longer needed or causing issues |
| Target not installed | Idempotency: uninstall of non-present pack should not fail destructively |
### POST /v2/manager/queue/task (update)
| Scenario | Intent |
|---|---|
| Success | User capability: upgrade to a newer release to get fixes/features |
| Already up-to-date | Idempotency: safe to trigger update even when nothing new exists |
| Update fails mid-way | Data integrity: don't leave pack in partially-updated state |
### POST /v2/manager/queue/task (fix)
| Scenario | Intent |
|---|---|
| Success | Recovery: when dependencies drift or break, re-install them without re-cloning source |
| Missing deps pre-fix | Recovery: fix should heal the environment |
### POST /v2/manager/queue/task (disable)
| Scenario | Intent |
|---|---|
| Success | User capability: temporarily stop using a pack without losing it (reversible) |
| Already disabled | Idempotency: re-disable is a no-op |
### POST /v2/manager/queue/task (enable)
| Scenario | Intent |
|---|---|
| Success | User capability: restore a disabled pack to active use |
| Not disabled | Idempotency: no-op when already active |
### POST /v2/manager/queue/install_model
| Scenario | Intent |
|---|---|
| Success | User capability: download models from curated whitelist for model library |
| Missing client_id/ui_id | Observability: every download is traceable |
| Invalid metadata | Input resilience: malformed model requests rejected early |
| Not in whitelist | Security boundary: prevent arbitrary URL downloads (supply-chain protection) |
| Non-safetensors + lower security | Security boundary: block executable-format model files in lower-trust env |
### POST /v2/manager/queue/update_all
| Scenario | Intent |
|---|---|
| Success | User capability: one-click update of all installed packs |
| Security denied | Security boundary: bulk ops are more risky; require middle+ trust |
| Missing params | Observability: must know who initiated bulk op |
| mode=local | User capability: work offline using cached data |
| Desktop build | Data integrity: don't self-update comfyui-manager in bundled builds |
| Empty active set | Idempotency: safe to run on fresh install with nothing to update |
### POST /v2/manager/queue/update_comfyui
| Scenario | Intent |
|---|---|
| Success | User capability: update ComfyUI core itself |
| Missing params | Observability: traceability |
| stable=true explicit | User capability: override policy for one-off stable update regardless of config |
### POST /v2/manager/queue/reset
| Scenario | Intent |
|---|---|
| Success | Recovery: abort an in-progress batch; clear failed state |
| Already empty | Idempotency: safe to call repeatedly as cleanup |
### POST /v2/manager/queue/start
| Scenario | Intent |
|---|---|
| Worker not running | User capability: explicit trigger for the async worker |
| Already running | Concurrency safety: don't spawn duplicate workers (data corruption risk) |
| Empty queue | Idempotency: no error on empty queue |
### GET /v2/manager/queue/status
| Scenario | Intent |
|---|---|
| No filter | Observability: dashboard view of overall progress |
| client_id filter | Observability: per-client progress for multi-user UI |
| Unknown client_id | Input resilience: unknown id returns 0s, not error |
### GET /v2/manager/queue/history
| Scenario | Intent |
|---|---|
| id=<batch_id> | Observability: inspect an old batch for audit/debug |
| Path traversal | Security boundary: prevent arbitrary file reads via history endpoint |
| ui_id filter | Observability: detailed view for one task |
| client_id filter | Observability: per-client history |
| Pagination | Performance: avoid huge payload on long histories |
| Serialization failure | Input resilience: fail cleanly (400) rather than crash |
### GET /v2/manager/queue/history_list
| Scenario | Intent |
|---|---|
| Success | Observability: enumerate past batches |
| Empty | Idempotency: no crash on empty history dir |
| Path inaccessible | Input resilience: fail cleanly |
## 1.2 Custom Node Info
### GET /v2/customnode/getmappings
| Scenario | Intent |
|---|---|
| Success (mode=local/cache/remote) | User capability: UI resolves "missing nodes in workflow" to recommend packs |
| mode=nickname | User capability: shorter display names for UI |
| Missing mode | Input resilience: require explicit mode choice |
### GET /v2/customnode/fetch_updates (deprecated)
| Scenario | Intent |
|---|---|
| Always 410 | API contract: signal clients to migrate to queue-based flow; don't silently break |
### GET /v2/customnode/installed
| Scenario | Intent |
|---|---|
| mode=default | Observability: current state for UI |
| mode=imported | Observability: startup-time state for diff ("what changed since boot") |
| Empty | Idempotency: no crash on empty install |
### POST /v2/customnode/import_fail_info
| Scenario | Intent |
|---|---|
| Known failed pack | Recovery: show user exact traceback so they can decide fix vs report vs uninstall |
| Unknown pack | Input resilience: 400 rather than empty success (distinguishable) |
| Missing fields / non-dict | Input resilience: reject early |
### POST /v2/customnode/import_fail_info_bulk
| Scenario | Intent |
|---|---|
| cnr_ids list | Performance: batch lookup for dialog that shows multiple failed packs at once |
| urls list | Same, for git-URL-installed packs |
| Empty lists | Input resilience: require at least one query |
| Null for unknown | Observability: distinguish "no failure info" from "lookup failed" |
## 1.3 Snapshots
### GET /v2/snapshot/get_current
| Scenario | Intent |
|---|---|
| Success | Observability: inspect system state before taking a snapshot |
| Failure | Input resilience: fail cleanly |
### POST /v2/snapshot/save
| Scenario | Intent |
|---|---|
| Success | User capability: persist current state for later rollback |
| Multiple saves | Observability: each save is independently retrievable |
### GET /v2/snapshot/getlist
| Scenario | Intent |
|---|---|
| Success | User capability: choose which snapshot to restore/delete |
| Empty | Idempotency: no crash on empty snapshot dir |
### POST /v2/snapshot/remove
| Scenario | Intent |
|---|---|
| Success | User capability: housekeeping (remove old snapshots) |
| Nonexistent target | Idempotency: re-delete should not error |
| Path traversal | Security boundary: prevent deleting files outside snapshot dir |
| Missing target | Input resilience |
| Security denied | Security boundary: middle security required |
### POST /v2/snapshot/restore
| Scenario | Intent |
|---|---|
| Success | Recovery: rollback to a known-good state after bad update |
| Nonexistent | Input resilience |
| Path traversal | Security boundary |
| Security denied | Security boundary: middle+ required (restore is destructive) |
## 1.4 Configuration
### GET /v2/manager/db_mode
| Scenario | Intent |
|---|---|
| Success | Observability: UI shows current mode setting |
### POST /v2/manager/db_mode
| Scenario | Intent |
|---|---|
| Valid | User capability: switch between online/local DB for different network conditions |
| Malformed | Input resilience |
| Missing value | Input resilience: don't silently set unknown/empty |
### GET/POST /v2/manager/policy/update
Same as db_mode: observability of current policy + user choice to change update strategy (stable vs nightly) + input resilience.
### GET /v2/manager/channel_url_list
| Scenario | Intent |
|---|---|
| Success | Observability: show available channels in UI dropdown |
| "custom" selected | Input resilience: URL not in known list doesn't break display |
### POST /v2/manager/channel_url_list
| Scenario | Intent |
|---|---|
| Known name | User capability: switch between upstream vs fork vs private channel |
| Unknown name | Input resilience: silent no-op (don't crash on typo) |
| Malformed | Input resilience |
## 1.5 System
### GET /v2/manager/is_legacy_manager_ui
| Scenario | Intent |
|---|---|
| Success | User capability: frontend picks which UI variant to mount at page load |
### GET /v2/manager/version
| Scenario | Intent |
|---|---|
| Success | Observability: display version in UI (troubleshooting / support) |
| Idempotent | Data integrity: version doesn't change at runtime |
### POST /v2/manager/reboot
| Scenario | Intent |
|---|---|
| Success | User capability: apply changes that require restart (snapshot restore, ComfyUI version switch) |
| CLI session mode | Integration: cooperates with external process manager for clean restart |
| Security denied | Security boundary: middle required (restart affects all users) |
### GET /v2/comfyui_manager/comfyui_versions
| Scenario | Intent |
|---|---|
| Success | User capability: enumerate ComfyUI versions to pick one for rollback/upgrade |
| Git failure | Input resilience: fail cleanly if ComfyUI isn't a git repo |
### POST /v2/comfyui_manager/comfyui_switch_version
| Scenario | Intent |
|---|---|
| Success | User capability: switch ComfyUI to specific version (pin for reproducibility) |
| Missing params | Observability |
| Security denied | Security boundary: high+ required (massive blast radius — affects core behavior) |
---
# Section 2 — Legacy-only Endpoints
### POST /v2/manager/queue/batch
| Scenario | Intent |
|---|---|
| Single-kind batch | User capability: execute multiple operations of same type in one round-trip |
| Mixed-kind batch | User capability: apply a workflow (uninstall-then-install = reinstall) atomically |
| Partial failure (`failed` list) | Observability: distinguish which packs in the batch failed from ones that succeeded |
| Empty body | Idempotency: no-op if nothing to do |
| update_all sub-key | User capability: trigger bulk update as part of batch |
### GET /v2/customnode/getlist
| Scenario | Intent |
|---|---|
| Success | User capability: populate Custom Nodes Manager dialog with full available pack catalog |
| skip_update=true | Performance: fast load when user doesn't need remote fetch |
| Channel resolution | Observability: user sees which channel data came from |
### GET /customnode/alternatives
| Scenario | Intent |
|---|---|
| Success | User capability: recommend alternative packs when one is discontinued/unavailable |
### GET /v2/externalmodel/getlist
| Scenario | Intent |
|---|---|
| Success | User capability: browse curated model catalog |
| `installed` flag per model | Observability: which models already present |
| HuggingFace sentinel | User capability: HF-hosted models via standard URL |
| Custom save_path | User capability: custom model placement |
### GET /v2/customnode/versions/{node_name}
| Scenario | Intent |
|---|---|
| Known CNR | User capability: pick a specific version to install (stability over latest) |
| Unknown pack | Input resilience |
### GET /v2/customnode/disabled_versions/{node_name}
| Scenario | Intent |
|---|---|
| Has disabled | User capability: see what versions are available to re-enable without fresh install |
| None | Input resilience |
### POST /v2/customnode/install/git_url
| Scenario | Intent |
|---|---|
| Success | User capability: install arbitrary git pack (for advanced users / private packs) |
| Already installed | Idempotency |
| Clone failure | Input resilience: bad URL returns error; no corrupt state |
| Security denied | Security boundary: high+ required (arbitrary code execution risk) |
### POST /v2/customnode/install/pip
| Scenario | Intent |
|---|---|
| Success | User capability: install pip packages needed by a pack |
| Security denied | Security boundary: high+ required (arbitrary package execution risk) |
### GET /v2/manager/notice
| Scenario | Intent |
|---|---|
| GitHub reachable | User capability: see latest Manager news/changelog inline |
| GitHub unreachable | Input resilience: don't block UI on external service failure |
| Non-git ComfyUI | Observability: warn user that their install is non-standard |
| Outdated ComfyUI | Observability: warn user they're too old to be safe |
| Desktop variant | User capability: correct footer for desktop distribution |
---
# Section 3 — Cross-cutting Scenarios
Some scenarios recur across many endpoints with consistent intent:
| Scenario pattern | Applies to | Unified intent |
|---|---|---|
| Malformed JSON body | all POST endpoints accepting JSON | Input resilience — protect against corrupted bytes / wrong content-type |
| Missing required field | all POST endpoints with schemas | Input resilience + Observability (traceability fields mandatory) |
| Path traversal in target/id | snapshot/remove, snapshot/restore, queue/history | Security boundary — prevent arbitrary filesystem access |
| Security level denial (middle/middle+/high+) | destructive endpoints | Security boundary — tier privileged ops per deployment risk profile |
| Idempotent re-call on empty state | queue/reset, history_list, snapshot/getlist, installed | Idempotency — safe to poll or retry |
| Repeated read returns same value | version, db_mode, policy/update | Data integrity — config/runtime state is stable |
| Empty collection returned cleanly | history, getlist, installed, alternatives | Input resilience — empty is valid, not an error |
---
# Section 4 — Intent Coverage Summary
| Intent category | # scenarios | Notes |
|---|---:|---|
| User capability (positive user need) | 62 | The "happy paths" |
| Input resilience | 32 | Mostly 400s for bad input |
| Security boundary | 15 | Security levels + path traversal |
| Idempotency | 14 | No-op / retry safety |
| Observability | 16 | State visibility + traceability |
| Data integrity | 8 | Config/state stability |
| Recovery | 5 | Fix, restore, reset |
| Concurrency safety | 2 | Worker dedup |
Total unique scenarios mapped: ~154 (matches Report A).
---
# Section 5 — Why This Mapping Matters
For each scenario, the **intent** drives the TEST design:
- **User capability** scenarios need end-to-end effect verification (feature works as promised)
- **Input resilience** scenarios need negative tests (bad inputs rejected cleanly)
- **Security boundary** scenarios need permission gate tests (403 proven per security level)
- **Idempotency** scenarios need repeat-call tests (no state drift)
- **Observability** scenarios need response-correctness tests (UI can trust the data)
- **Data integrity** scenarios need consistency tests (no runtime mutation of constants)
- **Recovery** scenarios need fault-injection tests (broken state → fix heals it)
- **Concurrency safety** scenarios need parallel-call tests (no duplicate workers/tasks)
Gaps in current E2E suite are best understood by intent: missing tests are typically for **security boundary** (403 gates), **input resilience edge cases** (path traversal, missing value keys), and **recovery** (fix/restore). These are the hardest to reach in simple E2E but matter most for production safety.
---
*End of Scenario Intent Mapping*