Files
ComfyAssets-ComfyUI_PromptM…/tools/vendor/frontend-coverage-baseline.json
T
Vito 69de20a427 feat(system): Sources & scans (#309)
* feat(system): report configured, effective and watched sources

GET /prompt_manager/settings adds settings.monitoring_enabled, a sources
block (configured roots with valid/code/reason, effective roots with their
listing index and watch state, auto_detected) and the monitor state
(present, observer_alive, scheduled). Every path is a public path.

A source is "watching" only while a live observer scheduled it, "manual"
when monitoring is off or the process has no monitor, and otherwise
"restart_pending".

POST accepts monitoring_enabled as a strict bool, saved under
gallery.monitoring.enabled; restart_required_fields names it when the
running observer does not already match. A save that changes the sources
runs as a "Source change" records hold, so it is refused with 409 busy
while a scan runs. Case variants of one folder are saved once.

POST /prompt_manager/settings/validate-source {path} checks a path
lexically (no "..", no control characters, native path forms only) and
then against the gallery-root rules, answering a code and reason, and
writes nothing.

* fix(system): no existence oracle in source validation

A gallery root's containment in the allowed ComfyUI folders is now
decided before its existence and type, so a path outside them answers
"outside-allowed" whether or not it exists. validate-source also folds
the filesystem-root and home-directory codes into "outside-allowed", with
one reason, and logs the detail server-side only. Paths inside the
allowed folders still say "not-found" or "not-directory". The legacy
settings save shares the same check.

* feat(scan): scan one selected source

POST /prompt_manager/jobs/scan accepts roots: indexes into the effective
sources, strict non-negative ints (no bools or floats), deduplicated, at
most 64, non-empty when present. An index past the sources is a 400
"unknown-source" and starts nothing. The job walks only the selected
sources, resolved when it was requested, and keeps each file's listing
root_index. Its scope records files, roots (null for every source) and
output_folders, the number of folders walked, which the log's first line
counts. The legacy /scan route still scans every source, and attaching to
a scoped job keeps its payload keys.

* feat(system): manage sources and start scans natively

System > Sources & scans renders the concept's section on the server's
sources: one row per effective source with its watch state and a scan
scoped to that source, the 3.2.0 update notice with the legacy dismissal,
Add source checked by validate-source, Remove, monitoring and worker
threads as host drafts behind the one Save row, and Refresh files.
A save refused by a running scan names the job. Thumbnail generation
hands off to the legacy gallery until S5.

Deviations D98, D113 and D-NEW-S4-1 are recorded in TESTING.md.

* fix(security): decide source containment as text before any filesystem access

Every caller (validate-source, POST /settings, config.json at load) now
refuses a source through one check, sources.source_problem: the form
check first, then GalleryConfig.gallery_root_problem, which compares the
normalised spelling with the allowed folders before anything resolves,
stats or opens the path. A UNC share, the \\?\ and \\.\ namespaces, a
device or any outside folder is refused with no SMB request, no
executor stall and no existence or timing oracle; only then is the path
resolved and contained again, which still catches a symlink out. The
filesystem root and the home directory answer outside-allowed with the
same reason as any outside path, so no caller learns the home path.

Also:
- POST /settings runs the form checks and parses the roots off the loop;
  an unusable source is a coded 400 invalid-source naming its public
  path (as text) and its index.
- ComfyUI folders outside the base directory are path anchors, so their
  sources round-trip and never collide by folder name.
- A per-source scan sends each index with its public path; a stale or
  shifted index is 409 sources-changed and starts nothing.
- A source still watched after monitoring was switched off is
  "stopping".
- Roots are compared as folders, not spellings, so an unchanged legacy
  resend takes no source-change hold.
- Tests: no realpath/stat/lstat of an outside path (native and Windows
  forms with ntpath fakes), the alike answers with /, ~ and a link to ~,
  an event instead of a timer wait, and a teardown that drains jobs
  before the temporary database goes.

* fix(system): say what Sources saved, refused and still watches

- A per-source scan sends the source's public path; when the sources
  changed since they were read the toast says the server's reason and
  the list and saved values are read again.
- "watching until restart" for a source the running monitor still
  watches after monitoring was saved off, and the status adds "Restart
  ComfyUI to apply: Watch for new outputs." from restart_required_fields.
- A save refused for one unusable source marks that row and the status
  is the server's message naming it.
- A valid configured source the effective list holds under another
  spelling reads "same folder as another source".
- The busy toast names the job the 409 named (ApiError keeps a
  validated running job and refused source), falling back to the list.
- The Add source dialog's listeners live on the page scope and Enter
  adds; its markup is compared with the concept (D127).
- TESTING.md: D-NEW-S4-1 is D127; D-NEW-S4-2 records the new copy; the
  Sources contract describes the lexical containment, invalid-source and
  sources-changed.

* fix(security): guard the Windows form probes on a token of their own

The no-filesystem guard matched "C:", which on a Windows runner is also
the temporary ComfyUI tree the save may resolve for its trusted first
entry. The probe paths now carry a token no trusted directory holds.

* docs(system): number the Sources states deviation D128
2026-10-23 13:15:06 -07:00

543 lines
10 KiB
JSON

{
"admin.js": {
"lines": 31,
"branches": 58,
"functions": 26,
"statements": 31
},
"api-client.js": {
"lines": 98,
"branches": 96,
"functions": 98,
"statements": 98
},
"classic-node.js": {
"lines": 98,
"branches": 79,
"functions": 98,
"statements": 98
},
"comfy-metadata.js": {
"lines": 97,
"branches": 92,
"functions": 98,
"statements": 97
},
"data-actions.js": {
"lines": 98,
"branches": 98,
"functions": 98,
"statements": 98
},
"focused-node.js": {
"lines": 97,
"branches": 87,
"functions": 90,
"statements": 97
},
"gallery-settings.js": {
"lines": 98,
"branches": 94,
"functions": 98,
"statements": 98
},
"gallery.js": {
"lines": 41,
"branches": 63,
"functions": 44,
"statements": 41
},
"html-escape.js": {
"lines": 98,
"branches": 98,
"functions": 98,
"statements": 98
},
"image-helpers.js": {
"lines": 96,
"branches": 91,
"functions": 98,
"statements": 96
},
"list-state.js": {
"lines": 98,
"branches": 92,
"functions": 98,
"statements": 98
},
"metadata-autocomplete.js": {
"lines": 98,
"branches": 82,
"functions": 85,
"statements": 98
},
"png-metadata.js": {
"lines": 98,
"branches": 96,
"functions": 98,
"statements": 98
},
"prompt-bridge-adapter.js": {
"lines": 98,
"branches": 93,
"functions": 98,
"statements": 98
},
"prompt-bridge-host.js": {
"lines": 98,
"branches": 98,
"functions": 98,
"statements": 98
},
"prompt-library.js": {
"lines": 98,
"branches": 94,
"functions": 98,
"statements": 98
},
"prompt-list-sort.js": {
"lines": 98,
"branches": 98,
"functions": 98,
"statements": 98
},
"scan-progress.js": {
"lines": 98,
"branches": 98,
"functions": 98,
"statements": 98
},
"shared/toolkit/core.js": {
"lines": 98,
"branches": 96,
"functions": 98,
"statements": 98
},
"shared/toolkit/prompt-bridge.js": {
"lines": 98,
"branches": 94,
"functions": 98,
"statements": 98
},
"shared/toolkit/suggestions.js": {
"lines": 98,
"branches": 98,
"functions": 98,
"statements": 98
},
"sse-stream.js": {
"lines": 98,
"branches": 90,
"functions": 98,
"statements": 98
},
"tag-autocomplete.js": {
"lines": 98,
"branches": 94,
"functions": 98,
"statements": 98
},
"tags-page.js": {
"lines": 57,
"branches": 48,
"functions": 56,
"statements": 57
},
"theme-bridge.js": {
"lines": 90,
"branches": 53,
"functions": 98,
"statements": 90
},
"view-router.js": {
"lines": 98,
"branches": 98,
"functions": 98,
"statements": 98
},
"workspace/activity-job-view.mjs": {
"lines": 97,
"branches": 86,
"functions": 98,
"statements": 97
},
"workspace/activity-scan.mjs": {
"lines": 96,
"branches": 78,
"functions": 98,
"statements": 96
},
"workspace/activity-store.mjs": {
"lines": 98,
"branches": 96,
"functions": 98,
"statements": 98
},
"workspace/activity.mjs": {
"lines": 96,
"branches": 87,
"functions": 98,
"statements": 96
},
"workspace/file-inspector.mjs": {
"lines": 96,
"branches": 93,
"functions": 92,
"statements": 96
},
"workspace/heading.mjs": {
"lines": 98,
"branches": 98,
"functions": 98,
"statements": 98
},
"workspace/history-media.mjs": {
"lines": 98,
"branches": 86,
"functions": 98,
"statements": 98
},
"workspace/history-state.mjs": {
"lines": 97,
"branches": 91,
"functions": 98,
"statements": 97
},
"workspace/history.mjs": {
"lines": 97,
"branches": 87,
"functions": 98,
"statements": 97
},
"workspace/icons.mjs": {
"lines": 98,
"branches": 98,
"functions": 98,
"statements": 98
},
"workspace/image-layout.mjs": {
"lines": 98,
"branches": 98,
"functions": 98,
"statements": 98
},
"workspace/image-tagging-state.mjs": {
"lines": 98,
"branches": 94,
"functions": 98,
"statements": 98
},
"workspace/image-tagging.mjs": {
"lines": 97,
"branches": 90,
"functions": 88,
"statements": 97
},
"workspace/images-category.mjs": {
"lines": 98,
"branches": 98,
"functions": 98,
"statements": 98
},
"workspace/images-export.mjs": {
"lines": 98,
"branches": 98,
"functions": 98,
"statements": 98
},
"workspace/images-favorites.mjs": {
"lines": 98,
"branches": 94,
"functions": 85,
"statements": 98
},
"workspace/images-selection.mjs": {
"lines": 98,
"branches": 93,
"functions": 98,
"statements": 98
},
"workspace/images-state.mjs": {
"lines": 98,
"branches": 98,
"functions": 98,
"statements": 98
},
"workspace/images-tagging-bulk.mjs": {
"lines": 98,
"branches": 93,
"functions": 86,
"statements": 98
},
"workspace/images-tagging-review.mjs": {
"lines": 98,
"branches": 93,
"functions": 98,
"statements": 98
},
"workspace/images-tagging.mjs": {
"lines": 98,
"branches": 98,
"functions": 98,
"statements": 98
},
"workspace/images.mjs": {
"lines": 97,
"branches": 89,
"functions": 98,
"statements": 97
},
"workspace/index.mjs": {
"lines": 98,
"branches": 97,
"functions": 98,
"statements": 98
},
"workspace/inspect.mjs": {
"lines": 98,
"branches": 92,
"functions": 93,
"statements": 98
},
"workspace/inspection-state.mjs": {
"lines": 98,
"branches": 94,
"functions": 98,
"statements": 98
},
"workspace/library-editor-state.mjs": {
"lines": 96,
"branches": 92,
"functions": 98,
"statements": 96
},
"workspace/library-editor.mjs": {
"lines": 98,
"branches": 93,
"functions": 92,
"statements": 98
},
"workspace/library-layout.mjs": {
"lines": 98,
"branches": 98,
"functions": 98,
"statements": 98
},
"workspace/library-state.mjs": {
"lines": 98,
"branches": 95,
"functions": 98,
"statements": 98
},
"workspace/library.mjs": {
"lines": 96,
"branches": 85,
"functions": 96,
"statements": 96
},
"workspace/local-inspection.mjs": {
"lines": 98,
"branches": 91,
"functions": 98,
"statements": 98
},
"workspace/local-parameters.mjs": {
"lines": 98,
"branches": 94,
"functions": 98,
"statements": 98
},
"workspace/local-projection.mjs": {
"lines": 97,
"branches": 96,
"functions": 98,
"statements": 97
},
"workspace/local-resolver-stages.mjs": {
"lines": 94,
"branches": 93,
"functions": 98,
"statements": 94
},
"workspace/local-resolver-support.mjs": {
"lines": 96,
"branches": 80,
"functions": 98,
"statements": 96
},
"workspace/local-resolver.mjs": {
"lines": 96,
"branches": 86,
"functions": 98,
"statements": 96
},
"workspace/local-semantics.mjs": {
"lines": 98,
"branches": 98,
"functions": 98,
"statements": 98
},
"workspace/local-session.mjs": {
"lines": 98,
"branches": 92,
"functions": 98,
"statements": 98
},
"workspace/local-video.mjs": {
"lines": 98,
"branches": 94,
"functions": 98,
"statements": 98
},
"workspace/media-viewer.mjs": {
"lines": 98,
"branches": 93,
"functions": 98,
"statements": 98
},
"workspace/plural.mjs": {
"lines": 98,
"branches": 98,
"functions": 98,
"statements": 98
},
"workspace/preferences.mjs": {
"lines": 98,
"branches": 96,
"functions": 98,
"statements": 98
},
"workspace/prompt-blocks.mjs": {
"lines": 90,
"branches": 92,
"functions": 98,
"statements": 90
},
"workspace/prompt-inspector.mjs": {
"lines": 98,
"branches": 98,
"functions": 98,
"statements": 98
},
"workspace/prompt-sender.mjs": {
"lines": 98,
"branches": 91,
"functions": 98,
"statements": 98
},
"workspace/py-json.mjs": {
"lines": 97,
"branches": 92,
"functions": 98,
"statements": 97
},
"workspace/recorded-run.mjs": {
"lines": 95,
"branches": 83,
"functions": 80,
"statements": 95
},
"workspace/saved-views.mjs": {
"lines": 98,
"branches": 95,
"functions": 95,
"statements": 98
},
"workspace/state.mjs": {
"lines": 98,
"branches": 98,
"functions": 98,
"statements": 98
},
"workspace/system-appearance.mjs": {
"lines": 98,
"branches": 93,
"functions": 98,
"statements": 98
},
"workspace/system-controls.mjs": {
"lines": 98,
"branches": 87,
"functions": 89,
"statements": 98
},
"workspace/system-overview.mjs": {
"lines": 96,
"branches": 92,
"functions": 98,
"statements": 96
},
"workspace/system-sources.mjs": {
"lines": 98,
"branches": 88,
"functions": 95,
"statements": 98
},
"workspace/system-state.mjs": {
"lines": 98,
"branches": 94,
"functions": 98,
"statements": 98
},
"workspace/system.mjs": {
"lines": 98,
"branches": 92,
"functions": 86,
"statements": 98
},
"workspace/theme.mjs": {
"lines": 98,
"branches": 93,
"functions": 98,
"statements": 98
},
"workspace/toast.mjs": {
"lines": 98,
"branches": 98,
"functions": 98,
"statements": 98
},
"workspace/toolbar.mjs": {
"lines": 96,
"branches": 96,
"functions": 98,
"statements": 96
},
"workspace/topbar.mjs": {
"lines": 98,
"branches": 98,
"functions": 83,
"statements": 98
},
"workspace/viewer-state.mjs": {
"lines": 98,
"branches": 95,
"functions": 98,
"statements": 98
},
"workspace/vocabulary-dialog.mjs": {
"lines": 98,
"branches": 88,
"functions": 95,
"statements": 98
},
"workspace/vocabulary-state.mjs": {
"lines": 98,
"branches": 96,
"functions": 98,
"statements": 98
},
"workspace/vocabulary.mjs": {
"lines": 98,
"branches": 91,
"functions": 93,
"statements": 98
},
"workspace/workspace-dialogs.mjs": {
"lines": 97,
"branches": 95,
"functions": 98,
"statements": 97
}
}