Back to skill

Security audit

ComfyUI Skill for OpenClaw

Security checks for vulnerabilities and agentic risk

Overview

This skill appears purpose-built for ComfyUI workflow management, but its local Web UI exposes powerful unauthenticated controls for credentials, server requests, updates, and dependency installation.

Install only if you trust the publisher and will run it on a machine where localhost access is trusted. Treat the Web UI as an administrative service: keep it bound to loopback, do not expose it over a network, review repository URLs before dependency installation, avoid storing high-value tokens until authentication/secret-handling is improved, and prefer isolated ComfyUI/Python environments.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (5)

T03 · Remote Payload Retrieval and Execution

Error
Location
ui/dependency_installer.py:366
Finding

Unauthenticated Arbitrary Repository Installation and Dependency Code Execution

Content
View full analysis
dict: """Install missing custom nodes and/or models.""" repo_urls = data.get("repo_urls", []) models = data.get("models", []) locale = data.get("locale", "zh") if not repo_urls and not models: raise HTTPException( status_code=400, detail="repo_urls list or models list is required", ) try: server = get_server_by_id(server_id) if server is None: raise FileNotFoundError(f"Server '{server_id}' not found") installer = DependencyInstaller(server["url"], server.get("auth", "")) report = await asyncio.to_thread( installer.install_all, repo_urls, models or None ) ``` ```python # ui/dependency_installer.py:366-395 def _try_git_clone(self, repo_url: str, pkg_name: str) -> InstallResult | None: """Attempt installation via direct git clone.""" custom_nodes_dir = Path(self._guess_comfyui_path()) / "custom_nodes" if not custom_nodes_dir.is_dir(): return None target_dir = custom_nodes_dir / pkg_name if target_dir.exists(): return InstallResult( success=True, package_name=pkg_name, source=repo_url, message="directory already exists", method="git_clone", needs_restart=True, ) try: result = subprocess.run( ["git", "clone", repo_url, str(target_dir)], capture_output=True, text=True, timeout=300, ) if result.returncode != 0: return InstallResult( success ...[truncated 3025 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
ui/services.py:567
Finding

Unauthenticated API Discloses and Modifies Plaintext Server Credentials

Content
View full analysis
dict: return service.get_config() @app.post("/api/config") async def save_config(config: ConfigModel) -> dict: saved = service.save_config(config.model_dump()) return {"status": "success", "config": saved} @app.get("/api/servers") async def list_servers() -> dict: servers = service.list_servers() config = service.get_config() return {"servers": servers, "default_server": config.get("default_server", "")} ``` ```python # ui/services.py:99-104 def get_config(self) -> dict[str, Any]: return get_runtime_config() def save_config(self, config: dict[str, Any]) -> dict[str, Any]: _write_json(CONFIG_PATH, config) return config ``` ```python # ui/services.py:567-579 payload = { "id": str(server.get("id") or "").strip(), "name": str(server.get("name") or "").strip(), "url": str(server.get("url") or "").strip(), "auth": str(server.get("auth") or ""), "comfy_api_key": str(server.get("comfy_api_key") or ""), "enabled": bool(server.get("enabled", True)), "output_dir": str(server.get("output_dir") or "./outputs").strip() or "./outputs", "unsupported": unsupported, } ``` ```python # scripts/shared/json_utils.py:12-18 def save_json(path: str | Path, data: Any) -> None: target = Path(path) target.parent.mkdir(parents=True, exist_ok=True) with target.open("w", encoding="utf-8") as file: json.dump(data, file, ensure_ascii=False, indent=2) file.write("\n") ``` ### Technical Analysis The management API does not implement authentication or authorization. Both `/api/config` and `/api/servers` return stored server authentic ...[truncated 1739 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
ui/app.py:284
Finding

Server-Side Request Forgery Through Unrestricted ComfyUI Server URLs

Content
View full analysis
str: value = value.strip() if not value: raise ValueError("Server URL is required") return value ``` ```python # ui/app.py:200-208 @app.post("/api/servers/test-connection") async def test_connection(body: dict) -> dict: url = str(body.get("url") or "").strip() auth = str(body.get("auth") or "").strip() if not url: return {"status": "offline", "message": "URL is empty"} ok, message = await asyncio.to_thread(test_server_connection, url, auth) result: dict = {"status": "online" if ok else "offline"} ``` ```python # ui/app.py:284-326 server_url = str(server.get("url", "")).rstrip("/") if not server_url: raise HTTPException(status_code=400, detail="Server has no URL configured") server_auth = str(server.get("auth", "")) content = await image.read() import urllib.request import urllib.error boundary = f"----ComfyUIBoundary{id(content)}" filename = image.filename or "upload.png" content_type = image.content_type or "image/png" body = ( f"--{boundary}\r\n" f'Content-Disposition: form-data; name="image"; filename="{filename}"\r\n' f"Content-Type: {content_type}\r\n\r\n" ).encode("utf-8") + content + f"\r\n--{boundary}--\r\n".encode("utf-8") req = urllib.request.Request( f"{server_url}/upload/image", data=body, headers={"Content-Type": f"multipart/form-data; boundary={boundary}"}, method="POST", ) if server_auth: req.add_header("Authorization", f"Bearer {server_auth}") try: with urllib.request.urlopen(req) as resp: return json.loads(resp.read()) ``` ### Technical Analysis URL validation only checks whether a ...[truncated 2022 chars]
Remediation
View remediation

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/shared/frontend_update.py:186
Finding

Unsigned Frontend Update Allows Unverified Asset Replacement and Unsafe Archive Links

Content
View full analysis
dict: """Download the latest frontend build and replace ui/static atomically.""" local = _read_local_version(static_dir) or {} release = _fetch_release() if not release: return {"success": False, "message": "Failed to fetch latest frontend release"} asset = next( (item for item in release.get("assets", []) if item.get("name") == FRONTEND_ARCHIVE_NAME), None, ) if not asset: return {"success": False, "message": f"{FRONTEND_ARCHIVE_NAME} not found in latest release"} download_url = asset.get("browser_download_url") if not download_url: return {"success": False, "message": f"{FRONTEND_ARCHIVE_NAME} has no download URL"} parent_dir = static_dir.parent parent_dir.mkdir(parents=True, exist_ok=True) backup_dir = parent_dir / f"{static_dir.name}.bak" swap_dir = parent_dir / f"{static_dir.name}.next" try: with tempfile.TemporaryDirectory(prefix="openclaw-frontend-") as tmpdir: tmp_path = Path(tmpdir) archive_path = tmp_path / FRONTEND_ARCHIVE_NAME extract_dir = tmp_path / "static" _download_to_path(download_url, archive_path) extract_dir.mkdir() _extract_archive(archive_path, extract_dir) _validate_static_dir(extract_dir) _swap_static_dir(extract_dir, static_dir, swap_dir, backup_dir) ``` ```python # scripts/shared/frontend_update.py:186-204 def _download_to_path(url: str, path: Path) -> None: req = urllib.request.Request(url, headers={"Accept": "application/octet-stream"}) with ...[truncated 3252 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding

Core CLI and Runtime Dependencies Are Installed Without Immutable Version or Hash Verification

Content
View full analysis
**Prerequisites**: Install the CLI tool once: `pip install comfyui-skill-cli`. All commands must be run from this project's root directory. ``` ```text # requirements.txt:1-4 fastapi>=0.100.0 uvicorn>=0.23.0 pydantic>=2.0.0 requests>=2.31.0 ``` ### Technical Analysis The Skill’s primary execution component is an external PyPI package installed without an exact version or package hash. The runtime requirements likewise use open-ended lower bounds, with no upper bounds, lockfile, or cryptographic hashes. As a result, two installations performed at different times may retrieve materially different code. A future compromised, malicious, or incompatible release can be accepted automatically without a change to the reviewed Skill artifact. No evidence was found that the named packages are currently malicious. The vulnerability is the absence of reproducible dependency resolution and artifact verification. ### Attack Path 1. A dependency publisher account, package release process, or package-index distribution path is compromised. 2. A malicious version satisfying the open-ended requirement is published. 3. A user follows the documented installation command or runs the update script. 4. pip resolves the newly published version because no exact version or hash is required. 5. Package installation hooks execute, and the package is subsequently imported or invoked by the application. ### Impact Assessment A malicious dependency can execute with the privileges of the installing user or application environment. Potential consequences include: - Theft of configuration credentials and API keys. - Modification of workflows, outputs, and source files. - Arbitrary network access. - Compromise of ComfyUI workflow execution. - Persistent m ...[truncated 229 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (79)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill is presented as a workflow-execution CLI, but it also instructs the agent to launch a local Web UI process and automatically open a browser. That is a materially different behavior class with process-spawning and side effects on the local machine, and users may not expect it from a workflow runner unless clearly permissioned.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill is presented as a workflow-execution CLI, but it also instructs the agent to launch a local Web UI process and automatically open a browser. That is a materially different behavior class with process-spawning and side effects on the local machine, and users may not expect it from a workflow runner unless clearly permissioned.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The skill is presented as a workflow-execution CLI, but it also instructs the agent to launch a local Web UI process and automatically open a browser. That is a materially different behavior class with process-spawning and side effects on the local machine, and users may not expect it from a workflow runner unless clearly permissioned.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill is presented as a workflow-execution CLI, but it also instructs the agent to launch a local Web UI process and automatically open a browser. That is a materially different behavior class with process-spawning and side effects on the local machine, and users may not expect it from a workflow runner unless clearly permissioned.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill is presented as a workflow-execution CLI, but it also instructs the agent to launch a local Web UI process and automatically open a browser. That is a materially different behavior class with process-spawning and side effects on the local machine, and users may not expect it from a workflow runner unless clearly permissioned.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill is presented as a workflow-execution CLI, but it also instructs the agent to launch a local Web UI process and automatically open a browser. That is a materially different behavior class with process-spawning and side effects on the local machine, and users may not expect it from a workflow runner unless clearly permissioned.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill is presented as a workflow-execution CLI, but it also instructs the agent to launch a local Web UI process and automatically open a browser. That is a materially different behavior class with process-spawning and side effects on the local machine, and users may not expect it from a workflow runner unless clearly permissioned.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill is presented as a workflow-execution CLI, but it also instructs the agent to launch a local Web UI process and automatically open a browser. That is a materially different behavior class with process-spawning and side effects on the local machine, and users may not expect it from a workflow runner unless clearly permissioned.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill is presented as a workflow-execution CLI, but it also instructs the agent to launch a local Web UI process and automatically open a browser. That is a materially different behavior class with process-spawning and side effects on the local machine, and users may not expect it from a workflow runner unless clearly permissioned.

Content

No source excerpt is available for this finding.

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · docs/comfyui-native-routes.md (reported line 184)May include surrounding context.

md
| `/prompt` | `GET` | Inspect current prompt queue status and execution information. | No body | Not used |
| `/history` | `GET` | Read execution history for finished or recorded runs. | No body | Not used directly |
| `/history/{prompt_id}` | `GET` | Read the history entry for one run. This is how the client polls for completion. | Path param `prompt_id` | Used now |
| `/history` | `POST` | Clear history or delete a history item. | JSON body, operation-specific | Not used |
| `/queue` | `GET` | Inspect the current execution queue. | No body | Not used |
| `/queue` | `POST` | Manage queue operations such as clearing pending or running work. | JSON body, operation-specific | Candidate |
| `/interrupt` | `POST` | Stop the currently running workflow. | Usually empty body | Candidate |

Possible Typosquatting: 'uvicorn' resembles popular package 'gunicorn'

High
Category
Supply Chain
Confidence
70% confidence
Finding

Package name closely resembles a popular package, suggesting possible typosquatting. Attackers publish malicious packages with similar names to trick developers into installing them.

Content

No source excerpt is available for this finding.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Confidence
85% confidence
Finding

os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.

Content

Scanner excerpt · scripts/shared/updater.py (reported line 256)May include surrounding context.

python
def restart_server() -> None:
    """Replace the current process with a fresh instance."""
    logger.info("Restarting server...")
    os.execv(sys.executable, [sys.executable] + sys.argv)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · ui/open_ui.py (reported line 71)May include surrounding context.

python
if os.name == "nt":
        process = subprocess.Popen([python_bin, str(SCRIPT_DIR / "app.py")], **kwargs)
    else:
        env = os.environ.copy()
        env.setdefault("PYTHON_BIN", python_bin)
        kwargs["env"] = env
        process = subprocess.Popen([str(launcher)], **kwargs)

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · ui/static/assets/index-D_BSx2UW.js (reported line 67)May include surrounding context.

js
* LICENSE.md file in the root directory of this source tree.
 *
 * @license MIT
 */const sw="6";try{window.__reactRouterVersion=sw}catch{}const aw="startTransition",Qc=rh[aw];function ep(e){let{basename:t,children:n,future:r,window:o}=e,l=p.useRef();l.current==null&&(l.current=pv({window:o,v5Compat:!0}));let i=l.current,[a,u]=p.useState({action:i.action,location:i.location}),{v7_startTransition:c}=r||{},d=p.useCallback(f=>{c&&Qc?Qc(()=>u(f)):u(f)},[u,c]);return p.useLayoutEffect(()=>i.listen(d),[i,d]),p.useEffect(()=>rw(r),[r]),p.createElement(lw,{basename:t,children:n,location:a.location,navigationType:a.action,navigator:i,future:r})}var Jc;(function(e){e.UseScrollRestoration="useScrollRestoration",e.UseSubmit="useSubmit",e.UseSubmitFetcher="useSubmitFetcher",e.UseFetcher="useFetcher",e.useViewTransitionState="useViewTransitionState"})(Jc||(Jc={}));var Yc;(function(e){e.UseFetcher="useFetcher",e.UseFetchers="useFetchers",e.UseScrollRestoration="useScrollRestoration"})(Yc||(Yc={}));const En=p.forwardRef(function({id:t,label:n,description:r,indeterminate:o=!1,className:l="",disabled:i,...a},u){const c=p.useId(),d=t??c,f=p.useRef(null);p.useEffect(()=>{f.current&&(f.current.indeterminate=o)},[o]);function m(w){if(f.current=w,typeof u=="function"){u(w);return}u&&(u.current=w)}return s.jsxs("label",{htmlFor:d,className:["checkbox-field",i?"is-disabled":"",l].filter(Boolean).join(" "),children:[s.jsx("input",{...a,ref:m,id:d,type:"checkbox",disabled:i,className:"checkbox-field-input"}),s.jsx("span",{className:"checkbox-field-control","aria-hidden":"true"}),n!=null||r!=null?s.jsxs("span",{className:"checkbox-field-copy",children:[n!=null?s.jsx("span",{className:"checkbox-field-label",children:n}):null,r!=null?s.jsx("span",{className:"checkbox-field-description",children:r}):null]}):null]})});function Dn({open:e,title:t,onClose:n,actions:r,footerStart:o,width:l="normal",initialFocusRef:i,children:a}){const u=p.useRef(n),c=p.useRef(null);return p.useEffect(()=>{u.current=n}
...[truncated 27 chars]

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The code can trigger dependency installation from repository URLs returned by the backend via /api/servers/{id}/install-dependencies, effectively enabling remote code introduction onto managed servers. Since ComfyUI custom nodes are executable code, installing arbitrary or compromised repositories can lead to full server compromise, data theft, persistence, or lateral movement.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This markdown file lists commands such as workflow delete, server remove, and config import that can delete configuration or overwrite existing state, but it does not warn users about possible data loss or recommend backups/confirmation before use. Under the markdown criteria for SQP-2, descriptions of behaviors that can affect user data or system integrity should include an explicit warning.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The markdown lists commands for deleting workflows, removing servers, and importing configuration that changes local state, but it does not provide any caution about irreversible changes, overwriting configuration, or the need to back up data first. Because this is user-facing skill documentation, omission of such warnings can lead to unintended data loss or configuration disruption.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
97% confidence
Finding

The skill requests or implies powerful capabilities including shell, file read/write, network, and environment access, but does not declare any explicit tool scope or permission boundaries. That makes it hard for a host agent to enforce least privilege and increases the chance the skill can perform unintended system, filesystem, or network actions during normal use.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The activation guidance includes broad natural-language triggers like 'generate an image' and 'draw a picture,' which are common everyday requests. In an agent ecosystem, that can cause the skill to activate unexpectedly and then invoke shell commands, network operations, dependency installation flows, or file modifications in contexts where the user did not specifically intend to use this integration.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The single-workflow auto-configuration flow directs the agent to write workflow.json and schema.json into the local project tree without requiring explicit user acknowledgement that files will be created or modified. Silent local writes can surprise users, overwrite existing content, or be abused to persist attacker-controlled workflow definitions and parameter mappings on disk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The FAQ says the tool can submit jobs, wait for completion, and download generated images automatically, but it gives no warning that this causes network activity and writes files to disk. In an agent context, especially one driven by shell commands, this can lead to unexpected external connections and filesystem side effects without clear user awareness or policy checks.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The FAQ recommends running an automatic dependency installation command that installs custom nodes and models via ComfyUI Manager, but it does not warn that this modifies the local system and may fetch untrusted third-party components. In an agent-execution setting, this expands risk significantly because the agent could trigger package/model installation with broad system impact and supply-chain exposure.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · docs/comfyui-native-routes.md (reported line 75)May include surrounding context.

Example:

bash
curl -X POST http://127.0.0.1:8188/prompt \
  -H 'Content-Type: application/json' \
  -d '{
    "prompt": {

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/shared/frontend_update.py (reported line 16)May include surrounding context.

python
logger = logging.getLogger(__name__)

FRONTEND_REPO = "HuangYuChuh/ComfyUI_Skills_OpenClaw-frontend"
GITHUB_API_URL = f"https://api.github.com/repos/{FRONTEND_REPO}/releases/tags/latest"
FRONTEND_ARCHIVE_NAME = "frontend-dist.tar.gz"
VERSION_ASSET_NAME = "version.json"
CACHE_TTL = 600  # 10 minutes

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This function downloads remote assets and replaces the existing static directory via backup/rename operations, which can overwrite deployed frontend files. While the docstring explains the behavior for developers, there is no user-facing confirmation, prompt, or visible disclosure in this code before the destructive update occurs.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.install_untrusted_source

Install source points to URL shortener or raw IP.

Warn
Code
suspicious.install_untrusted_source
Location
config.example.json:6