The registry scan report on 1.1.4 listed exactly three findings, all of them
the folder-dialog process launches in __init__.py (yara rule
python_command_injection_risk, severity "info"), and no path-traversal
findings at all: the 1.1.4 containment guard did its job. An admin escalated
1.1.2, 1.1.3 and 1.1.4 from Flagged to Banned on those three hits anyway.
The pattern itself is not against the rules (Pixaroma ships an identical
PowerShell/osascript picker and stays listed), but arguing that point does not
make the package installable, so the surface is gone instead:
- __init__.py registers no HTTP route and launches no process. The Browse
button and POST /craftkit/browse_folder are removed.
- Folder approval moved to ComfyUI's own settings store under
CraftKit.AllowedFolders. The node button writes it through the frontend
settings API; Python only ever reads the file.
- craftkit_folder_guard.py drops remember_folder/dialog_available and the
allow_any flag, so containment can no longer be disabled at all. The legacy
allowed_folders.json is still read so folders approved with the old dialog
keep working.
- Comments avoid naming the module and call form the scanner matches, since
the scan reads source text.
Verified against a simulated install (10/10 cases) and end to end in ComfyUI:
button approves, Python accepts the folder and its subfolders, pre-update
approvals still work, system folders and .. traversal still refused.
The registry's HIGH-severity finding was never the COM/folder-picker code
we suspected in 1.1.1-1.1.3 — it's smart_batch_resize.py's input_folder
widget accepting any path and writing to it, an unconfined filesystem
write reachable via unauthenticated POST /prompt. Found via the
undocumented ?include_status_reason=true parameter on the versions API.
Adds craftkit_folder_guard.py (pattern adapted from Pixaroma's
_path_guard.py): a folder is only usable if it's inside ComfyUI's own
input/output/temp directories, or the user picked it in the native OS
folder dialog at some point (the click-OK happens outside anything a
request can reach), or allow_any is hand-edited into the config.
Also fixes a PowerShell console window flashing open behind the folder
dialog (missing CREATE_NO_WINDOW).
- Add section-header dividers (Resize/Filename/Output Location/Output
Format/Options) to js/smart_batch_resize.js — the node grew to ~19
widgets after the review fixes and needed grouping to stay scannable
- Move `delimiter` next to the other filename-building fields, and
reorder Output Location/Output Format/Options into a more logical
flow. This changes the node's widget order — workflows saved before
this change will need the node re-placed once (values shift on load)
- Update README: new screenshot, full input table rewritten to match
the new field order and the alpha_mode/output_format/quality/
preview_limit/upscale_if_smaller additions, "Why these nodes?" bullet
on the transparency handling
- Narrow the ProgressBar import guard to ImportError only, with a comment explaining why it stays (the module is imported standalone by the test suite outside a live ComfyUI)
- Extract the CRAFTKIT_DIALOG_ERROR: sentinel into a shared constant instead of three separate literals, use str.removeprefix
- Note that .avif/.heic can also fail on re-encode (with output_format=keep_source), not just decode
- os module imported properly instead of __import__("os")
- browse_folder distinguishes a real COM/dialog failure from a user cancel
- fix Marshal.PtrToStringUni + leak-safe cleanup in the folder-picker COM call
- lock browse_folder so two concurrent clicks can't open two dialogs
- drop redundant int(round()) in SmartResolutionMultiplier
- unify "CraftKit —" docstring prefix across nodes
- comment OUTPUT_NODE=True on the status-label nodes so it isn't "cleaned up" later
- add .jfif/.avif/.heic to SmartBatchResize's supported extensions, document non-recursive scanning
- add a progress bar to SmartBatchResize (degrades gracefully outside a live ComfyUI)
- add readme/requires-python and a dependency-rationale comment to pyproject.toml
- SmartBatchResize: only skip existing files if their actual dimensions still match current settings (was serving stale sizes after a settings change)
- SmartBatchResize: add alpha_mode (auto/flatten/keep) and output_format (keep_source/png/webp/jpg) with a quality control, replacing the undocumented silent alpha loss
- SmartProfileSwitch: warn when index is out of range instead of silently reading as slot 1 (catches an unconnected Smart Prompt Controller)
- SmartProfileSwitch JS: guard widget reorder against missing lookups, append rather than drop unrecognized widgets
- SmartPromptController JS: add the serialize override the other node JS files already have, preventing widget value drift
- Remove js/smart_batch_controller.js (dead code, referenced a node class that no longer exists)
- Untrack .claude/worktrees/* accidentally committed earlier; add .claude/ to .gitignore
- Reject use_original_name=False + use_counter=False (all files collapsed to one output name)
- Add preview_limit to cap in-memory preview tensors (was holding entire batch as float32)
- Recover from per-file failures instead of aborting the whole batch
- Fix _calc_new_size overshoot past longest_side and off-by-one truncation
- Add upscale_if_smaller to SmartBatchResize to match SmartResize
- Clip pixel values before uint8 cast (was wrapping on overflow)
- Support CUDA tensors and RGBA/grayscale images in SmartResize
- Handle empty batches; derive width/height from the actual output tensor
- Require POST + loopback/origin check on the browse_folder endpoint (CSRF)
- Add subprocess timeout to the folder picker dialog
- Isolate the browse_folder route registration so import failures don't take down all nodes
- Validate folder_custom against path traversal and self-overwrite
- Replace IS_CHANGED=NaN with a folder-listing hash so unrelated graph runs can still cache
- add IS_CHANGED so new/removed files in input_folder are picked up without a widget change
- fix EXIF orientation ignored on load (phone photos were saved rotated)
- when all files are skipped, load existing resized output from disk instead of returning
an empty list — fixes IndexError downstream (e.g. Preview Image) and removes the
misleading "No images processed" error
- fix stale docstring in Smart Resolution Multiplier (SeedVR2's resolution input, not max_resolution)
- rename Smart Category Config → Smart Profile Switch 🧩 (code, JS, locales, demo workflow)
- add banner.svg and embed it at the top of README
- replace fragile OpenFileDialog folder-browse hack with native IFileDialog/FOS_PICKFOLDERS picker — fixes wrong-folder bug on single-click select
- flip Smart Resize's upscale_if_smaller default to true — node now resizes to target by default, as the name implies
- add width/height source tooltip to Smart Resolution Multiplier's image input
- fix stale widgets_values in smart_batch_resize_demo.json (10 fields → 13 after prefix/counter fields were added)
- full README rewrite: correct inputs/defaults/outputs for all 5 nodes, new Smart Profile Switch section, Example workflows section, expanded "Why these nodes?"
- add new example workflow (seedvr2_smartresize_demo.json), updated screenshots
- add assets/*.ai to .gitignore
- bump version to 1.0.2
- Revised README to clarify node functionalities and typical use cases.
- Changed category for SmartResize, SmartBatchResize, and SmartSizeMultiplier nodes to "Craftopia".