SkillTotal

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_package · https://github.com/Comfy-Org/ComfyUI
LOW
10
/ 100 risk score
Snapshot · scanned Jul 3, 2026 · ComfyUI@0.27.0 · engine 0.24.0 / ruleset 25
No malicious indicators - review capabilities before installing
Notable — review in context (capabilities are not malware):
  • 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.

Capabilities — what this component can do (not a risk score):
dynamic code executionfilesystem readfilesystem writenetwork egressshell execution

Findings (6)

HIGHPython dynamic code executionST-DYN-PY

The code turns strings into live code at runtime (eval / new Function / exec).

self.model = self.model_class(**config).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.

HIGHPython shell/command executionST-SHELL-PY

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.

MEDIUMServer bound to all network interfacesST-EXPOSE-BIND

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.

MEDIUMPython filesystem readST-FS-PY-READ

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(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_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),

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.

MEDIUMPython filesystem write/deleteST-FS-PY-WRITE

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:
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:
shutil.move(source, dest)
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:

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.

MEDIUMPython network egressST-NET-PY

The component makes outbound network requests.

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.json_response({"error": "Invalid directory type"}, status=400)
return web.json_response([f"{entry.name} [{directory_type}]" for entry in sorted_files], status=200)
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)
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 component

Or get notified if this component's risk changes:

How we determine this: deterministic static analysis (regex + AST), evidence-anchored, no code execution. Methodology →