One Bug Made the Cut: Lucent on ComfyUI An audit of ComfyUI by a former Stability AI CISO found one vulnerability that made the cut: an arbitrary file write outside the output directory via dataset "Save" nodes, filed as a Medium-severity GitHub advisory. The audit, which cost about 1.8 cents in cloud compute, concluded ComfyUI is well-built under its single-trusted-operator threat model but noted that multi-user mode is not authentication and the Manager's security levels allow more at default than the name suggests. I aimed the hunter from the last post at something bigger and more popular than hermes-agent https://github.com/NousResearch/hermes-agent : ComfyUI https://github.com/Comfy-Org/ComfyUI , together with its companion . If you have not run it, ComfyUI is a node-graph editor for image and video generation: you wire nodes into a workflow load a checkpoint, encode a prompt, sample, decode, save and a local server executes the graph. It became the power-user's interface for Stable Diffusion and the open image and video models that followed, the tool people reach for when a single-box web UI runs out of room. The project carries tens of thousands of GitHub stars and a custom-node ecosystem in the thousands, and the team behind it has since spun out as Comfy Org. I have history with this one: I was CISO at https://github.com/ltdrdata/ComfyUI-Manager ComfyUI-Manager Stability AI https://stability.ai , where ComfyUI sat in the stack I was responsible for, so pointing the hunter at it was personal as much as a stress test. And the surface is wide. ComfyUI serves an unauthenticated HTTP API that executes user-submitted workflow graphs, it loads third-party "custom nodes" as Python, and the Manager can install packages, switch versions, and reboot the server. A lot for a bug-hunter to pull on. The honest headline first: ComfyUI is well-built. At its defaults it holds, and the audit did not turn up a dropped remote-code-execution hole. The more useful result is about its threat model. ComfyUI assumes a single trusted operator on localhost, and under that assumption its defenses are consistent. The risk is in the things a user might assume are security boundaries and that are not. Multi-user mode is not authentication. The Manager's "security levels" allow more at the default than the name suggests. And nothing in the product stops you putting the whole thing on a network it was never designed to face. A word on the tool, because it cuts against the last post. The cheap part is the local automated sweep, this time a 14B Qwen reading 645 files one at a time. It found nothing real. ComfyUI is hardened enough that a shallow per-file pass slides off it; its real weak points are cross-file and architectural, which a per-file reader does not see. Everything below came out of the slower layer that reads the actual source against the evidence ladder and tries to disprove each lead. That is the part I said last time was where the value is. The cloud bill for the run was about 1.8 cents . What made the cut Lucent surfaced the usual stack of leads. The discipline from last time still applies: read each one against the source, try to break it. But a target with a published SECURITY.md adds a second ruler. Does the thing even count as a vulnerability under the project's own threat model? Measured that way, one finding made the cut. | What | Where | Verdict | |---|---|---| Arbitrary file write outside output/ | dataset "Save" nodes, from a loaded workflow | In scope: filed as a GitHub advisory Medium | | Path traversal → read an image from outside the models tree | /experiment/models/preview | Real; image-bounded here, browser vector blocked by default. A later round found a verbatim read in this class | torch.load with no weights only on attacker bytes | LoadTrainingDataset , via /prompt | Real, but latent on a default torch ≥2.6 blocks it ; RCE only on stale torch<2.6 | | Admin actions reboot, snapshot/restore reachable unauthenticated at the default | ComfyUI-Manager | Hardening: admin / denial-of-service, not RCE | install model fetches an unchecked URL for .safetensors names | ComfyUI-Manager | Hardening: SSRF + model poisoning | | Bearer tokens written in cleartext to a web-served log directory | API-node request logger | Low | | Websocket client-id can be taken over → another client's outputs | /ws | Low | The one that counts is the file write. ComfyUI's experimental dataset "Save" nodes take a free-text folder name and join it straight onto the output directory: os.path.join get output directory , folder name at nodes dataset.py:255 , with no containment check. Then they os.makedirs it and write. An absolute path or a .. chain walks out of output/ . The vector is the one ComfyUI's SECURITY.md names as in scope: a workflow built from nothing but built-in nodes, the kind people share as JSON and bake into PNGs, writes a file wherever the author chose the moment you load and run it. A two-node graph is the whole exploit: curl -s -X POST http://127.0.0.1:8188/prompt -d '{"prompt":{ "1":{"class type":"EmptyImage","inputs":{"width":32,"height":32,"batch size":1,"color":65280}}, "2":{"class type":"SaveImageDataSetToFolder", "inputs":{"images": "1",0 ,"folder name":"/tmp/comfyui live escape abs","filename prefix":"pwn"}}}}' - 200 {"prompt id":"…","node errors":{}} I ran it against a fresh 0.22.0 clean git clone and pip install , --cpu , bound to loopback, no extra flags . The server accepted the graph and wrote the file outside its sandbox. Its own log is the receipt: INFO Saved 1 images to /tmp/comfyui live escape abs. INFO Saved 1 images to …/comfyui-live/output/../../../../../tmp/comfyui live escape rel. Absolute paths and .. chains both land. The control is what makes it a bug and not a feature: a standard SaveImage node handed the same traversal errors out and never creates the directory. It routes through folder paths.get save image path , which raises "outside the output folder." The dataset Save nodes just omit the guard their mainstream siblings have. The attacker picks the directory. The filename is fixed and the bytes are a PNG shaped with EmptyImage , which is still enough to drop a file into an autostart or config path the server user can write. It scores Medium. There is no remote, no-interaction version; it needs you to load and run the attacker's workflow on your own box. The fix is the guard the siblings already use, +19/−4 in one file. I filed it as a GitHub security advisory. The image-read traversal is the same root-cause mistake: an experimental endpoint that forgot the containment its siblings /view , uploads, user data all have. This endpoint is bounded. It leaks images plus a yes/no file-existence oracle, not arbitrary text, and the cross-origin browser path is blocked by ComfyUI's default middleware. So from this sweep I filed only the write and set the read aside. That call aged badly. The read-traversal class is real enough that a sibling LoadImage version was already public, and a later round of grinding turned up a read in the same class that returns any file byte-for-byte. ComfyUI has since contained the shared helper both of them ran through, on 3 July 2026, and published the LoadImage traversal as GHSA-rvxv-29p8-pxgq on 15 July. That one is the strongest finding in the whole audit, filed and since fixed upstream. I wrote it up separately: Reading Any File in ComfyUI /reading-any-file-in-comfyui . The pickle finding is the honest walk-back. LoadTrainingDataset calls torch.load without weights only , reachable through the unauthenticated /prompt executor with no operator action. For a while it looked like a clean two-request remote-code-execution chain: upload a malicious pickle, then submit the graph that loads it. Then I checked what pip install torch resolves to. It pulls the latest wheel, where weights only=True is the default and the payload is refused. So on a default install it is latent, not exploitable; a working exploit needs a stale or deliberately pinned torch<2.6 . The lead I killed The one I most wanted to be real was the math node. ComfyUI's MathExpression evaluates a workflow-supplied string through simple eval , and workflows travel constantly as JSON files and baked into PNGs from untrusted sources. That is untrusted input straight into an evaluator, no misconfiguration required. I ran all eight of the well-known simpleeval sandbox escapes through the exact call ComfyUI makes. Every one was refused, both on the version a fresh install pulls and on a years-old release. It is a fragile design, resting on one general-purpose sandbox continuing to hold, but it is not a hole today, so it came off the list. What I filed, and what I'd send along I filed one advisory. The file write is the single finding that crosses ComfyUI's own line, a workflow of built-in nodes writing outside the expected directories, so that is the one I reported, with a few-line fix. The rest is a hardening list rather than advisories: the image-read traversal bounded at this endpoint, though the class later produced a filed bug, linked above , the pickle path needs a stale torch , and the Manager's admin-and-SSRF gaps real, but not code execution . The most useful non-code thing for the maintainers is documentation: say plainly that multi-user is not authentication, that the Manager's default security level allows administrative actions, and that ComfyUI assumes a trusted operator and should not be exposed to an untrusted network. Source was read but not modified, on Comfy-Org/ComfyUI and ComfyUI-Manager at their current main. Severities are my own estimates. The file-write finding is filed as a GitHub security advisory; the live findings were reproduced against a fresh 0.22.0 install on loopback. Disclosure timeline: audit run and findings drafted 1 June 2026 against ComfyUI main 70a2e1a and ComfyUI-Manager. The file-write finding was reported to Comfy Org directly. The stronger file-read finding from the later rounds was reported as a GitHub security advisory on 1 June 2026, and ComfyUI shipped path containment for the shared helper on 3 July 2026 96e0e35 , hardened 2 August 41a3e16 . Published 11 August 2026. Reproduction details throughout are given against 0.22.0.