save_paths is reachable without auth, so saving "/" made every media
file on the machine servable. Reject filesystem/drive roots, the home
directory and its ancestors (realpath-resolved, so symlinks count), and
ignore such entries already in config.json.
ComfyUI registers custom_nodes in folder_names_and_paths, so treating
every registered folder as a download root let a queued prompt drop a
.py file there that runs on the next restart.
- Skip custom_nodes when collecting allowed roots
- save_response only writes model/data extensions (.safetensors, .gguf,
.ckpt, .pth, .json, ...), so any future code-holding folder is still
protected
- KikoSaveImage: reject absolute or '..' filename_prefix directories that
resolved outside the output folder
- Film grain: use a local torch.Generator instead of reseeding the global
RNG; strength 0 no longer skips the toe adjustment
- Purge VRAM: IS_CHANGED returns NaN so caching never skips the purge
- Resolution calculator: clamp outputs to >= 8
- Plot sampler params: font size floor of 1 for images under 32px wide
- Gemini prompt: drop Image.fromarray mode= argument (removed in Pillow)
- Delete kikotools/core (registry/settings) and batch_prompts/node_simple
which nothing imports
- pytest.ini used the [tool:pytest] header and was ignored; make it the
single pytest config (drop the pyproject duplicate), keep
--strict-markers
- Add pytest-asyncio to dev deps; enforce 85% coverage via
[tool.coverage.report] fail_under
- Reject filenames with path components; reduce server-derived names
(Content-Disposition, CivitAI redirect, URL) to a safe basename
- save_path must resolve inside models_dir or a registered model folder
(including extra_model_paths.yaml roots); relative paths resolve
against ComfyUI's base path
- Only http/https for user URLs and redirects; drop the Authorization
header on cross-host redirects
- Stream to <file>.part and rename on success, removing the partial file
on any failure, so a dropped connection no longer leaves a truncated
model that later runs treat as complete
- 60s timeout on every request; shared streaming loop in BaseDownloader
- Custom downloader honours Content-Disposition before the default name
/view served any readable file (e.g. ~/.ssh/id_rsa) to anyone who could
reach the server; the only guard was a '..' substring check.
- Resolve paths with realpath and require commonpath containment in the
saved paths or ComfyUI input/output directories
- /view serves image/video/audio only, /thumbnail and load_media images
only; the extension check applies to the resolved path, so symlinks
can't smuggle other files
- /images 403s outside the roots; page/per_page validated and clamped
- /list_directories (folder picker) returns directory names only
- load_media refuses disallowed paths from a tampered selections.json
- Route handlers moved to module level so they are unit tested
- Update stale tests to the (image, info) return contract
- LoRA strength strings: step <= 0 looped forever and reversed ranges
returned [] (ZeroDivisionError later); now raise ValueError, and cap
generated values at 1000
- Flux sampler params: unparseable steps/guidance/shift/denoise/seed
tokens raise with the field name instead of being dropped, and an
empty LoRA folder raises instead of returning (None, [])
- Detect Flux schnell via unet_config guidance_embed (the ModelType.FLOW
check never matched Flux)
- IS_CHANGED: re-run Flux params when a seed is '?'; LoRA folder batch
hashes file list + mtimes instead of always re-executing
- LoRA folder scan uses commonpath containment across all lora roots
- Prompt separators must be whole lines; failed encodes raise
- Escape file names, paths and image metadata interpolated into
innerHTML in the local image loader and save image viewer (XSS via
crafted file names or a shared workflow's filename_prefix)
- Save image viewer: bind document drag listeners once and remove them
in disconnectedCallback instead of adding two per run; drop debug
logging and the window.kikoSaveImageTest helper
- Local image loader: check response.ok and surface 400/403 errors,
disconnect the ResizeObserver on node removal
- Gemini prompt: restore the user's node color after the run flash
- Workflow timer: chain onRemoved, merge properties in onConfigure
- Seed history: build entries with textContent
Previously split_latent_batch and join_latent_batch only handled the
"samples" key, silently dropping metadata like noise_mask or batch_index.
This broke inpaint and masked workflows after a round-trip conversion.
Add 4 utility nodes for converting between batched tensors and lists,
eliminating the dependency on Impact Pack for this common operation:
- ImageBatchToImageList (OUTPUT_IS_LIST)
- ImageListToImageBatch (INPUT_IS_LIST)
- LatentBatchToLatentList (OUTPUT_IS_LIST)
- LatentListToLatentBatch (INPUT_IS_LIST)
JS Math.random() only has 53 bits of integer precision, making the
64-bit range produce non-uniform values. 32-bit (0xFFFFFFFF) is the
standard ComfyUI seed range and works correctly in both Python and JS.
Also removes the redundant mode input from SeedHistoryNode, relying on
ComfyUI's built-in control_after_generate widget instead.