Is Comfy-Org/ComfyUI safe?
ComfyUI is an AI python_package analyzed by SkillTotal's deterministic static scanner. The scan found no malicious indicators, though 6 risky constructs are reported for review. It can: dynamic code execution, filesystem read, filesystem write, network egress and shell execution — capabilities are what the code can do, not a verdict on intent. Risk score 10/100 (low).
ComfyUI 0.27.0
- Python shell/command execution
- Python dynamic code execution
- Python filesystem write/delete
No malicious indicators found by static analysis.
Automated static-analysis result. It can contain false positives and false negatives, and is not a claim about the intent of Comfy-Org/ComfyUI's authors. Report a false positive.
Findings (6)
The code turns strings into live code at runtime (eval / new Function / exec).
self.model = self.model_class(**config).eval()
self.vae = VAE_16k().eval()
self.vocoder = BigVGANVocoder(
bigvgan_config
).eval()self.model = build_from_state_dict(state_dict, dtype=self.dtype, device=offload_device, operations=comfy.ops.manual_cast).eval()
return ModelLoader().load_from_state_dict(state_dict).eval()
fl = FaceLandmarker(device=offload_device, dtype=self.dtype, operations=None, detector_variant=variant).eval()
out = ModelLoader().load_from_state_dict(sd).eval()
Why it matters: If those strings aren't fixed and trusted, they become a way to run arbitrary code.
Fix: Avoid evaluating dynamically constructed code; if unavoidable, ensure the input is a trusted constant and never derived from external data.
The component can run operating-system commands or spawn processes.
subprocess.check_call([sys.executable, '-s', '-m', 'pip', 'install', '-r', repo_req_path])
out = subprocess.check_output(['nvidia-smi', '-L'])
Why it matters: Powerful and often legitimate — confirm the commands aren't built from untrusted input.
Fix: Confirm the command and its arguments are fully controlled and not derived from untrusted input; avoid shell=True.
A server is bound to all network interfaces (0.0.0.0), not just your own machine.
if os.name == 'nt' and address == '0.0.0.0':
Why it matters: Without authentication, other hosts on the network can reach it.
Fix: Bind to 127.0.0.1 for local-only use, or require authentication and restrict access if remote exposure is intended.
The component reads files from disk.
with open(file) as f:
with open(abs_path, "rb") as f:
with open(os.fspath(fp), "rb") as f:
with open(path, "rb") as f:
with open(file_path, "r", encoding="utf-8") as f:
with open(entry['path'], 'r', encoding='utf-8') as f:
with open(self.get_users_file()) as f:
with open(json_path, 'r') as f:
with open(frag_path, 'r') as f:
with open(json_path, 'r') as f:
with open(json_config) as f:
with open(json_config) as f:
with open(env_file, encoding="utf-8") as f:
file_digest = hashlib.sha256(open(path, 'rb').read()).hexdigest()
with open(path, 'rb') as f:
with open(config_path, 'r') as stream:
with open(textmodel_json_config) as f:
with open(safetensors_path, "rb") as f:
with open(self._source, "rb") as f:
return Path(self._source).read_bytes()
with open(file_path, "rb") as f:
("images", open(image, "rb") if isinstance(image, str) else tensor_to_filelike(image))open(image, "rb") if isinstance(image, str) else tensor_to_filelike(image),
with open(filepath, "rb") as f:
with open(file, "rb") as f:
Why it matters: Usually legitimate, but worth confirming it can't be steered into reading sensitive files.
Fix: Confirm which files are read and that paths cannot be influenced by untrusted input to reach sensitive locations.
The component writes or deletes files on disk.
shutil.copy(repo_update_py_path, os.path.join(cur_path, "update_new.py"))
shutil.copy(repo_req_path, req_path)
shutil.copy(stable_update_script, stable_update_script_to)
with open(file, "w") as f:
with open(tmp_path, "wb") as f:
os.remove(tmp_path)
os.remove(p)
os.remove(temp_path)
shutil.copy(db_path, backup_path)
shutil.copy(backup_path, db_path)
os.remove(backup_path)
with open(self.get_users_file(), "w") as f:
os.unlink(tmp_path)
os.remove(path)
shutil.move(source, dest)
with open(frag_path, 'w') as f:
with open(json_path, 'w') as f:
with urllib.request.urlopen(url) as response, open(path, 'wb') as f:
shutil.copyfileobj(response, f)
self.file = open(self.filename, 'a')
self.file = open(self.filename, 'w')
with open(sync_stub_path, "w") as f:
with open(output_path, "wb") as f:
shutil.copy2(self._source, dest)
with open(dest, "wb") as f:
Why it matters: Usually legitimate, but worth confirming the paths can't be controlled by untrusted input.
Fix: Confirm which files are written/deleted and that paths cannot be influenced by untrusted input.
The component makes outbound network requests.
from aiohttp import web
self.routes: web.RouteTableDef = web.RouteTableDef()
return web.json_response("".join([(l["t"] + " - " + l["m"]) for l in app.logger.get_logs()]))return web.json_response({
"entries": list(app.logger.get_logs()),
"size": {"cols": self.terminal_service.cols, "rows": self.terminal_service.rows}
})return web.Response(status=200)
return web.json_response(response)
return web.json_response({"error": "Invalid directory type"}, status=400)return web.json_response([f"{entry.name} [{directory_type}]" for entry in sorted_files], status=200)self._app = web.Application()
from aiohttp import web
raise web.HTTPUnauthorized() from e
return web.json_response(self.get_settings(request))
return web.json_response(value)
return web.Response(status=200)
return web.Response(status=400)
return web.Response(status=200)
import urllib.parse
from aiohttp import web
ROUTES = web.RouteTableDef()
return web.json_response(
{"error": {"code": code, "message": message, "details": details or {}}},
status=status,
)encoded_filename = urllib.parse.quote(filename, safe="")
url += f"&subfolder={urllib.parse.quote(subfolder, safe='')}"return web.Response(status=200 if exists else 404)
return web.json_response(payload.model_dump(mode="json", exclude_none=True))
return web.json_response(payload.model_dump(mode="json", exclude_none=True), status=200)
Why it matters: Usually legitimate, but confirm the destinations are expected and no sensitive data leaves.
Fix: Confirm the destination hosts are expected and that no sensitive data is sent off-host.
Check your own component
Run the same evidence-backed scan on any MCP server, agent skill, or package.
Scan your own componentOr get notified if this component's risk changes:
How we determine this: deterministic static analysis (regex + AST), evidence-anchored, no code execution. Methodology →