Back to skill

Security audit

UGC Manual

Security checks for vulnerabilities and agentic risk

Overview

The skill largely does what it claims, but it handles sensitive face and voice media with third-party upload, unclear consent boundaries, and unsafe URL/temp-file handling that should be reviewed before installation.

Install only if you are comfortable sending face images, voice recordings, audio URLs, and generated media through ComfyDeploy. Use only media you own or have consent to process, provide a least-privileged ComfyDeploy API key, avoid private/internal URLs, and prefer running it in a constrained environment until upload consent, URL limits, timeouts, temp-file cleanup, and dependency locking are improved.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (3)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate.py:204
Finding
Unbounded Downloads from User-Controlled and API-Controlled URLs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate.py:204-239` **Vulnerability Type**: Unbounded network response handling and missing request timeouts **Risk Level**: Medium ### Vulnerable Code ```python print(f"Downloading from: {video_url}") response = requests.get(video_url) response.raise_for_status() with open(output_path, "wb") as f: f.write(response.content) ``` ```python if is_url(args.audio): # Download the audio first, then convert print(f"Downloading audio from URL: {args.audio}") response = requests.get(args.audio) response.raise_for_status() # Save to temp file ext = Path(urlparse(args.audio).path).suffix or ".mp3" fd, temp_audio = tempfile.mkstemp(suffix=ext) os.close(fd) with open(temp_audio, "wb") as f: f.write(response.content) ``` ### Technical Analysis The script downloads remote audio and generated video using `requests.get()` without connect or read timeouts. It also accesses `response.content`, which buffers the entire response in memory before writing it to disk. No maximum response size, `Content-Length` validation, streaming limit, media-type validation, or redirect policy is applied. The audio URL is directly controlled by the user. The video URL originates from the remote ComfyDeploy result and could become attacker-controlled if the service, workflow, account, or response is compromised. The absence of timeouts also affects availability because a server can accept a connection and then transmit data indefinitely or extremely slowly. ### Attack Path 1. An attacker supplies an HTTP or HTTPS URL through `--audio`. 2. The URL points to a server that returns an extremely large response, an endless stream, or a deliberately slow response. 3. The script calls `requests.get(args.audio)` without a timeout or size restriction. 4. Accessing `response.content` attempts to buffer the complete response in process memory. 5. The process hangs or exhausts memory; writin ...[truncated 675 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Set explicit connect and read timeouts, for example: ```python with requests.get( url, stream=True, timeout=(10, 60), allow_redirects=True, ) as response: response.raise_for_status() ``` - Stream downloads in bounded chunks instead of using `response.content`. - Maintain a byte counter and abort when a documented maximum audio or video size is exceeded. - Reject responses whose declared `Content-Length` exceeds the configured limit. - Validate `Content-Type` against an allowlist before processing the response. - Limit redirects and revalidate the scheme and destination after each redirect. - Consider restricting remote destinations or blocking private, loopback, link-local, and metadata-service addresses if URLs can be supplied by untrusted users. - Delete any partially written output if a download fails or exceeds its limit. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate.py:231
Finding
Sensitive Temporary Audio File Is Not Reliably Deleted<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate.py:231-242` **Vulnerability Type**: Incomplete cleanup of sensitive temporary files **Risk Level**: Medium ### Vulnerable Code ```python # Save to temp file ext = Path(urlparse(args.audio).path).suffix or ".mp3" fd, temp_audio = tempfile.mkstemp(suffix=ext) os.close(fd) with open(temp_audio, "wb") as f: f.write(response.content) # Convert to WAV temp_wav = convert_audio_to_wav(temp_audio) os.unlink(temp_audio) # Clean up temp audio audio_url = upload_file(temp_wav, api_key) ``` The exception cleanup only handles the converted WAV file: ```python except Exception as e: # Cleanup on error if 'temp_wav' in locals() and temp_wav and os.path.exists(temp_wav): os.unlink(temp_wav) print(f"Error: {e}", file=sys.stderr) sys.exit(1) ``` ### Technical Analysis The original downloaded audio is deleted only after `convert_audio_to_wav()` returns successfully. If writing the file fails, FFmpeg rejects the input, FFmpeg is unavailable, conversion is interrupted, or another exception occurs before `os.unlink(temp_audio)`, the temporary source recording remains on disk. The outer exception handler cleans `temp_wav` but does not track or delete `temp_audio`. Because the Skill is explicitly designed to process user voice recordings, the residual file may contain privacy-sensitive voice or biometric information. `tempfile.mkstemp()` securely creates the file, so the primary issue is lifecycle management rather than predictable naming or initial file permissions. ### Attack Path 1. A user or attacker provides malformed, truncated, unsupported, or otherwise conversion-failing audio through a URL. 2. The script downloads the content into `temp_audio`. 3. `convert_audio_to_wav(temp_audio)` invokes FFmpeg and fails before the following `os.unlink(temp_audio)` call. 4. Control transfers to the outer exception handler. 5. The handler deletes only `temp_wav`; the downloaded sourc ...[truncated 665 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Track both `temp_audio` and `temp_wav` from initialization and remove them in a `finally` block. - Prefer a scoped `TemporaryDirectory` so every intermediate artifact is removed together. - Ensure cleanup occurs after download, conversion, upload, polling, and output-download failures. - Avoid printing sensitive temporary paths where logs may be retained. - Document the local and remote retention lifecycle for image, voice, and generated-video data. Example structure: ```python temp_audio = None temp_wav = None try: # Download, convert, upload, and process. ... finally: for path in (temp_audio, temp_wav): if path and os.path.exists(path): try: os.unlink(path) except OSError: pass ``` ]]>

T08 · Insecure Dependencies

Note
Location
scripts/pyproject.toml:7
Finding
Runtime Dependency Is Not Reproducibly Pinned<![CDATA[ ## Vulnerability Details **File Location**: `scripts/pyproject.toml:7` **Vulnerability Type**: Uncontrolled future dependency resolution **Risk Level**: Low ### Vulnerable Code ```toml dependencies = [ "requests>=2.28.0", ] ``` ### Technical Analysis The project accepts any future `requests` release satisfying the lower-bound constraint. No reviewed lockfile is present in the audited project structure. This does not establish that the current `requests` package is malicious or vulnerable. However, runtime dependency resolution is not reproducible and can select package versions that were not reviewed with the Skill. The risk is particularly relevant when `uv run` resolves and installs dependencies in an automated environment. ### Attack Path 1. The Skill is executed in an environment without an existing locked dependency installation. 2. The package resolver retrieves the newest release satisfying `requests>=2.28.0`. 3. A future compromised, defective, or unexpectedly incompatible release is selected without a source-code change in this project. 4. The installed package is imported by `generate.py`. 5. Any malicious package initialization code would execute with the same filesystem, environment, and network permissions as the Skill process. This is a supply-chain risk scenario rather than evidence that the currently named dependency is malicious. ### Impact Assessment If dependency resolution were compromised, code could execute with the privileges of the user or service account running the Skill. That could expose the `COMFY_DEPLOY_API_KEY`, user media available to the process, writable files, and network access. The likelihood is limited because the dependency uses the legitimate `requests` package name from the normal package ecosystem; no typosquatting or unsafe custom index was identified. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Generate and commit a reviewed `uv.lock` file. - Run deployment and automation with locked or frozen dependency resolution. - Use package hashes where the installation workflow supports them. - Update dependencies through a controlled review process rather than resolving unrestricted future versions at execution time. - Periodically scan locked dependencies for known vulnerabilities. - Configure trusted package indexes explicitly in controlled deployment environments. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (17)

Tainted flow: 'headers' from os.environ.get (line 111, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
files = {"file": (os.path.basename(file_path), f, content_type)}
        headers = {"Authorization": f"Bearer {api_key}"}
        
        response = requests.post(
            f"{COMFY_DEPLOY_API_URL}/file/upload",
            headers=headers,
            files=files
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'headers' from os.environ.get (line 111, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
}
    }
    
    response = requests.post(
        f"{COMFY_DEPLOY_API_URL}/run/deployment/queue",
        headers=headers,
        json=payload
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'headers' from os.environ.get (line 111, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
if time.time() - start_time > timeout:
            raise TimeoutError(f"Run did not complete within {timeout} seconds")
        
        response = requests.get(
            f"{COMFY_DEPLOY_API_URL}/run/{run_id}",
            headers=headers
        )
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Credential Access

High
Category
Privilege Escalation
Content
def get_api_key():
    """Get API key from environment."""
    api_key = os.environ.get("COMFY_DEPLOY_API_KEY")
    if not api_key:
        raise ValueError(
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill advertises and documents capabilities that require shell execution, network access, and likely environment access, but it does not declare any tool scope or permissions boundaries. This creates a mismatch between what the agent may do and what reviewers or users can assess, increasing the risk of unintended command execution or data handling without explicit approval.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The skill says it should not be used when AI voice generation is needed, but later lists externally generated TTS as a normal use case. This contradiction can misroute requests and bypass intended control separation between skills, making it easier to use synthetic voices in a workflow represented as manual user audio.

External Transmission

Medium
Category
Data Exfiltration
Content
## API Details

**Endpoint:** `https://api.comfydeploy.com/api/run/deployment/queue`
**Deployment ID:** `075ce7d3-81a6-4e3e-ab0e-7a25edf601b5`

## Required Inputs
Confidence
88% confidence
Finding
The documented workflow sends user media to an external service at api.comfydeploy.com, which is a genuine external transmission path. External transmission is not inherently malicious, but in this context it is security-relevant because the uploaded content includes face images and voice audio that can be sensitive and potentially identifying.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The skill accepts image and audio inputs and specifies an external API endpoint, but it does not warn that those user-provided files will be transmitted to a third party. Because image and voice recordings are highly sensitive biometric and personal data, failing to disclose external transfer can violate user expectations, consent requirements, and privacy controls.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The manifest frames the skill as operating on a user's own recording, but the later documentation broadens it to arbitrary audio sources, including externally generated or non-user content. That inconsistency weakens safety expectations and can cause the skill to be applied to third-party audio or content types the user did not intend, undermining consent and policy boundaries.

Description-Behavior Mismatch

Medium
Confidence
88% confidence
Finding
The skill accepts arbitrary remote image and audio URLs, downloads remote audio locally, and uploads media to a third-party cloud service, but the skill description frames the tool as operating on user-provided files. This mismatch can cause unanticipated data egress and creates SSRF-style risk against internal or sensitive URLs if the execution environment can reach them.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests

# ComfyDeploy API configuration
COMFY_DEPLOY_API_URL = "https://api.comfydeploy.com/api"
DEPLOYMENT_ID = "075ce7d3-81a6-4e3e-ab0e-7a25edf601b5"
Confidence
82% confidence
Finding
The hardcoded external API endpoint establishes a built-in data egress path to a third-party service. While not inherently malicious, this is security-significant because the skill handles user image/audio content and the manifest does not clearly communicate off-platform transfer.

Context-Inappropriate Capability

Medium
Confidence
86% confidence
Finding
The script depends on a third-party cloud API and reads a bearer credential from the environment, but that external dependency and credential use are not disclosed in the skill description. In a skill context, undisclosed secret usage and off-platform processing are security-relevant because operators may unknowingly grant networked data access and expose user media to an external processor.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
]
    
    try:
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
raise RuntimeError(f"Failed to convert audio: {e.stderr}")
    except FileNotFoundError:
        raise RuntimeError(
            "FFmpeg not found. Install with: sudo apt install ffmpeg"
        )
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

External Transmission

Medium
Category
Data Exfiltration
Content
}
    }
    
    response = requests.post(
        f"{COMFY_DEPLOY_API_URL}/run/deployment/queue",
        headers=headers,
        json=payload
Confidence
90% confidence
Finding
This request queues processing on an external cloud service using user-provided media URLs, meaning user content and associated metadata leave the local environment. In the context of a skill that appears simple and local from its description, undisclosed external transmission materially increases privacy and compliance risk.

Context-Inappropriate Capability

Low
Confidence
75% confidence
Finding
The manifest presents the skill as a straightforward media transformation, but the implementation shells out to ffmpeg for audio conversion. Local subprocess execution is an additional capability that is not described and is not inherent from the manifest wording alone.

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Static analysis

No suspicious patterns detected.