Back to skill

Security audit

Comfyui Flux

Security checks for vulnerabilities and agentic risk

Overview

This skill is mostly a local ComfyUI image generator, but it has under-scoped face-image handling and can send prompts or reference face images to an arbitrary endpoint through an environment variable.

Review before installing. Use only with a trusted local ComfyUI endpoint, check COMFYUI_URL before running, and do not provide face reference images unless you are comfortable with where that ComfyUI server stores and processes them. The skill should be tightened to loopback-only by default and should clearly separate or warn about face-consistent generation.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/pulid_generate.py:189
Finding

Configurable Unauthenticated ComfyUI Endpoint Can Expose Biometric Reference Images

Content
View full analysis
str: """Upload a reference image to ComfyUI's input folder. Returns the filename.""" filename = os.path.basename(image_path) with open(image_path, "rb") as f: image_data = f.read() # Multipart form upload boundary = uuid.uuid4().hex content_type, _ = mimetypes.guess_type(image_path) if content_type is None: content_type = "image/jpeg" 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") + image_data + f"\r\n--{boundary}--\r\n".encode("utf-8") req = urllib.request.Request( f"{COMFYUI_URL}/upload/image", data=body, headers={"Content-Type": f"multipart/form-data; boundary={boundary}"}, method="POST", ) with urllib.request.urlopen(req, timeout=30) as resp: result = json.loads(resp.read().decode()) uploaded_name = result.get("name", filename) print(f"Uploaded reference image: {uploaded_name}", file=sys.stderr) return uploaded_name ``` The upload is invoked automatically after the connectivity check: ```python # Upload reference image print("Uploading reference image...", file=sys.stderr) ref_name = upload_image(args. ...[truncated 2307 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (22)

Tainted flow: 'req' from os.environ.get (line 173, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
92% confidence
Finding

Even the health check uses COMFYUI_URL from the environment, so a caller can cause the script to connect to an arbitrary endpoint before any image generation begins. This is dangerous because it violates the local-only expectation and can be abused to probe or interact with unintended network services under the guise of a benign readiness check.

Content

Scanner excerpt · scripts/flux_generate.py (reported line 105)May include surrounding context.

python
"""Check if ComfyUI is running."""
    try:
        req = urllib.request.Request(f"{COMFYUI_URL}/system_stats")
        with urllib.request.urlopen(req, timeout=5) as resp:
            return True
    except Exception:
        return False

Tainted flow: 'req' from os.environ.get (line 104, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
96% confidence
Finding

The request target is derived from COMFYUI_URL, which is fully overrideable via an environment variable, so this script can POST prompts and workflow data to an arbitrary remote server rather than a local ComfyUI instance. In the context of a skill advertised as local-only image generation, this breaks the trust boundary and can leak user prompts, generated content metadata, and enable SSRF-like outbound connections to attacker-controlled infrastructure.

Content

Scanner excerpt · scripts/flux_generate.py (reported line 123)May include surrounding context.

python
data=data,
        headers={"Content-Type": "application/json"},
    )
    with urllib.request.urlopen(req, timeout=30) as resp:
        return json.loads(resp.read().decode())

Tainted flow: 'req' from os.environ.get (line 173, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
94% confidence
Finding

The polling request for job history also uses the environment-controlled COMFYUI_URL, allowing repeated outbound requests to a non-local service for up to the configured timeout. That makes the issue more dangerous than a single request because it can continuously transmit identifiers and interact with attacker-controlled services while appearing to perform normal local generation.

Content

Scanner excerpt · scripts/flux_generate.py (reported line 135)May include surrounding context.

python
elapsed = int(time.time() - start)
        try:
            req = urllib.request.Request(f"{COMFYUI_URL}/history/{prompt_id}")
            with urllib.request.urlopen(req, timeout=10) as resp:
                history = json.loads(resp.read().decode())
        except Exception:
            continue

Tainted flow: 'req' from os.environ.get (line 173, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
97% confidence
Finding

The image download step fetches arbitrary bytes from the environment-selected COMFYUI_URL and writes them directly to a user-chosen output path. This can exfiltrate data through remote image generation, save untrusted remote content locally, and further undermines the claim that all operations stay on the local machine.

Content

Scanner excerpt · scripts/flux_generate.py (reported line 174)May include surrounding context.

python
})
            url = f"{COMFYUI_URL}/view?{params}"
            req = urllib.request.Request(url)
            with urllib.request.urlopen(req, timeout=60) as resp:
                with open(output_path, "wb") as f:
                    f.write(resp.read())
            return True

Tainted flow: 'req' from os.environ.get (line 294, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/pulid_generate.py (reported line 191)May include surrounding context.

python
"""Check if ComfyUI is running."""
    try:
        req = urllib.request.Request(f"{COMFYUI_URL}/system_stats")
        with urllib.request.urlopen(req, timeout=5):
            return True
    except Exception:
        return False

Tainted flow: 'req' from os.environ.get (line 190, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
91% confidence
Finding

The script trusts COMFYUI_URL from the environment and uses it to upload a user-supplied reference face image to whatever host that variable points to. If an attacker or misconfigured environment changes COMFYUI_URL from localhost to a remote server, sensitive biometric image data and prompts can be exfiltrated without user awareness.

Content

Scanner excerpt · scripts/pulid_generate.py (reported line 222)May include surrounding context.

python
headers={"Content-Type": f"multipart/form-data; boundary={boundary}"},
        method="POST",
    )
    with urllib.request.urlopen(req, timeout=30) as resp:
        result = json.loads(resp.read().decode())

    uploaded_name = result.get("name", filename)

Tainted flow: 'req' from os.environ.get (line 190, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The workflow submission is sent to a URL derived from COMFYUI_URL without validation. While this request mainly sends prompts and workflow metadata, it can still leak user content and direct the tool to interact with an unintended remote service under attacker control.

Content

Scanner excerpt · scripts/pulid_generate.py (reported line 242)May include surrounding context.

python
data=data,
        headers={"Content-Type": "application/json"},
    )
    with urllib.request.urlopen(req, timeout=30) as resp:
        return json.loads(resp.read().decode())

Tainted flow: 'req' from os.environ.get (line 294, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/pulid_generate.py (reported line 254)May include surrounding context.

python
elapsed = int(time.time() - start)
        try:
            req = urllib.request.Request(f"{COMFYUI_URL}/history/{prompt_id}")
            with urllib.request.urlopen(req, timeout=10) as resp:
                history = json.loads(resp.read().decode())
        except Exception:
            continue

Tainted flow: 'req' from os.environ.get (line 294, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The script downloads image bytes from a host controlled by COMFYUI_URL and writes them directly to a user-specified path without validating the endpoint or content type. If COMFYUI_URL is attacker-controlled, this enables untrusted remote content to be written locally, creating a path for malicious file planting or overwriting user-expected outputs.

Content

Scanner excerpt · scripts/pulid_generate.py (reported line 295)May include surrounding context.

python
)
            url = f"{COMFYUI_URL}/view?{params}"
            req = urllib.request.Request(url)
            with urllib.request.urlopen(req, timeout=60) as resp:
                with open(output_path, "wb") as f:
                    f.write(resp.read())
            return True

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The manifest frames this as a general local image generation skill and even says to prefer another skill for face-consistent selfies, but the body exposes identity-preserving face generation with reference-image handling and custom PuLID workflows. That is a materially different capability with greater privacy, impersonation, and abuse risk, especially because it involves uploading a face image to a local service and producing likeness-preserving outputs.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The manifest frames this as a general local image generation skill and even says to prefer another skill for face-consistent selfies, but the body exposes identity-preserving face generation with reference-image handling and custom PuLID workflows. That is a materially different capability with greater privacy, impersonation, and abuse risk, especially because it involves uploading a face image to a local service and producing likeness-preserving outputs.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill documents behaviors that require network access to a local HTTP service and environment-variable-based configuration, yet it declares no tool scope or permissions boundaries. That makes the skill's effective capabilities broader and less auditable than the manifest suggests, increasing the risk of unintended invocation or misuse.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The manifest tells operators to prefer a different skill for face-consistent selfies, but the same skill then documents its own face-consistent generation path. This inconsistency can mislead routing and review, causing a higher-risk identity-preserving capability to be invoked under the guise of a lower-risk general image tool.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The trigger phrases are broad enough that this skill may be selected for loosely related prompts such as 'flux', 'local image', or PPT graphics without adequate disambiguation. Overbroad routing is dangerous here because the skill has networked local-service access and can perform higher-risk face-consistent generation, so accidental invocation expands exposure beyond the user's likely intent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The documentation encourages use of a reference face image for identity-consistent outputs but provides no warning about consent, biometric privacy, or impersonation risks. In context, this omission makes a sensitive capability easier to misuse for non-consensual likeness generation or handling of personal images without appropriate user safeguards.

Content

No source excerpt is available for this finding.

Ssd 1

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Stating that the local workflow has 'no content censorship' and 'can generate anything locally' explicitly frames the skill as a way to bypass safety controls. That messaging increases abuse risk by encouraging users or agents to route disallowed or harmful generation requests to this skill precisely because it lacks moderation.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The documentation says ComfyUI must run on localhost:8188, but the implementation defaults to 127.0.0.1:8200 and also permits arbitrary COMFYUI_URL overrides. This discrepancy is security-relevant because users and higher-level agents may trust the skill as local-only while the code actually allows different endpoints, weakening assumptions about where prompts and outputs are sent.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill is presented as generating images locally through localhost ComfyUI, but in practice every network operation uses COMFYUI_URL and can therefore target any URL supplied via the environment. In this skill context, that mismatch materially increases risk because users may provide sensitive prompts or expect zero external transmission, while the script can silently send data and retrieve results from remote infrastructure.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The file is explicitly documented and implemented to generate face-consistent images using a reference face image, including PuLID-based identity conditioning. The manifest for this skill says that for face-consistent selfies, users should prefer the wanx-image-generation skill instead, so this file expands the skill beyond its stated intended use and undermines that guidance.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The code uploads a reference face image to a ComfyUI service without any explicit privacy notice, destination confirmation, or consent check. Because the input is biometric/identity-related data, this is particularly sensitive, and the risk increases further if COMFYUI_URL is changed to a remote host.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

The manifest states that ComfyUI must be running at localhost:8188, while the detailed instructions say the default port is 8200 and provide an override example using 127.0.0.1:8200. This is an active contradiction in the skill's own documentation about how the service is actually reached.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
77% confidence
Finding

The top-level documentation frames the script as local generation via ComfyUI + Flux.1 Dev, but the implemented workflow materially depends on PuLID loaders, InsightFace analysis, reference-image upload, and ApplyPulidFlux identity conditioning. This documentation is not just incomplete about a minor detail; it presents a substantially different intent than plain local Flux image generation described at the skill level.

Content

No source excerpt is available for this finding.