Back to skill

Security audit

VEED UGC

Security checks for vulnerabilities and agentic risk

Overview

This skill is coherent for generating AI promotional videos, but its local file upload and output download paths are too broadly scoped for a media-generation tool.

Review before installing. Use this only with non-sensitive images and scripts you are comfortable sending to ComfyDeploy and downstream media services. Avoid passing arbitrary local paths to `--image`; use only intended image files. Prefer running it in a restricted workspace with a narrowly scoped ComfyDeploy API key, and consider pinning dependencies before use.

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

Error
Location
scripts/generate.py:42
Finding

Arbitrary Local File Upload to an External Service

Content
View full analysis

Vulnerability Details

File Location: scripts/generate.py:42-74, invoked from scripts/generate.py:197-208
Vulnerability Type: Unrestricted local file upload and sensitive information disclosure
Risk Level: High

Vulnerable Code

python
def upload_file(client: httpx.Client, api_key: str, file_path: str) -> str:
    """Upload a file to ComfyDeploy and return the URL."""
    path = Path(file_path)
    print(f"[DEBUG] upload_file called for: {file_path}", flush=True)
    
    if not path.exists():
        raise FileNotFoundError(f"File not found: {file_path}")
    
    # Determine content type
    suffix = path.suffix.lower()
    if suffix in [".jpg", ".jpeg"]:
        content_type = "image/jpeg"
    elif suffix == ".png":
        content_type = "image/png"
    elif suffix == ".webp":
        content_type = "image/webp"
    else:
        content_type = "application/octet-stream"
    
    print(f"[DEBUG] Content type: {content_type}", flush=True)
    
    # Upload file using multipart form
    print(f"[DEBUG] Opening file and preparing upload...", flush=True)
    with open(path, "rb") as f:
        files = {"file": (path.name, f, content_type)}
        print(f"[DEBUG] Sending POST to {API_BASE}/file/upload", flush=True)
        response = client.post(
            f"{API_BASE}/file/upload",
            headers={"Authorization": f"Bearer {api_key}"},
            files=files
        )

The upload is reached through the following argument handling:

python
# Determine if image is URL or file
image_url = args.image

# Create HTTP client
with httpx.Client(timeout=120.0) as client:
    # Upload image if it's a local file
    if not args.image.startswith(("http://", "https://")):
        print("Uploading image...")
        image_url = upload_file(client, api_key, args.image)
    else:
        print(f"Using image URL: {image_url}")

T

...[truncated 2095 chars]

Remediation
View remediation

Remediation Suggestions

  1. Permit only explicitly supported image formats such as JPEG, PNG, and WebP; reject all other extensions instead of using application/octet-stream.
  2. Decode the file with a maintained image library and reject files that cannot be parsed as the claimed image type.
  3. Validate the file's magic bytes rather than trusting its extension.
  4. Resolve the path with Path.resolve() and require it to remain within an approved input directory or workspace.
  5. Reject symbolic links where they are not required, or validate the final resolved target.
  6. Enforce a conservative maximum file size before opening or uploading the file.
  7. Clearly notify the user that the image will be transmitted to ComfyDeploy and require confirmation where the execution environment supports interactive approval.
  8. Avoid printing unnecessary local path information in logs.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate.py:130
Finding

Unrestricted Retrieval of API-Supplied Output URLs

Content
View full analysis

Vulnerability Details

File Location: scripts/generate.py:130-171, called from scripts/generate.py:228-230
Vulnerability Type: Unvalidated remote URL retrieval, SSRF exposure, and unbounded download
Risk Level: Medium

Vulnerable Code

python
def download_output(client: httpx.Client, output_url: str, output_path: str):
    """Download the output video."""
    print(f"Downloading from: {output_url}")
    response = client.get(output_url, follow_redirects=True)
    if response.status_code != 200:
        raise Exception(f"Failed to download output: {response.status_code}")
    
    with open(output_path, "wb") as f:
        f.write(response.content)
    
    print(f"Saved: {output_path}")

The URL is selected directly from API-controlled output data:

python
def find_video_output(outputs: list) -> str | None:
    """Find video URL in outputs."""
    for output in outputs:
        data = output.get("data", {})
        
        # Check for video files
        if "files" in data:
            for file_info in data["files"]:
                url = file_info.get("url", "")
                if any(ext in url.lower() for ext in [".mp4", ".webm", ".mov"]):
                    return url
        
        # Check for gifs/videos array
        if "gifs" in data:
            for gif_info in data["gifs"]:
                url = gif_info.get("url", "")
                if url:
                    return url
        
        # Check for videos array
        if "videos" in data:
            for video_info in data["videos"]:
                url = video_info.get("url", "")
                if url:
                    return url
        
        # Check direct url
        url = data.get("url", "")
        if any(ext in url.lower() for ext in [".mp4", ".webm", ".mov"]):
            return url
    
    return None

The selected value is passed to the ...[truncated 2680 chars]

Remediation
View remediation

Remediation Suggestions

  1. Allow only https output URLs.
  2. Maintain an explicit allowlist of expected ComfyDeploy or approved media-storage hostnames.
  3. Resolve destination addresses and reject loopback, private, link-local, multicast, reserved, and cloud metadata ranges.
  4. Disable automatic redirects or validate the scheme, hostname, port, and resolved address at every redirect.
  5. Stream the response to disk in bounded chunks instead of reading response.content.
  6. Enforce a maximum download size using Content-Length where available and a running byte counter while streaming.
  7. Require an expected video content type and optionally inspect the downloaded media signature.
  8. Set separate connection, read, write, and pool timeouts.
  9. Write first to a securely created temporary file and move it to the requested destination only after successful validation.

T08 · Insecure Dependencies

Warning
Location
scripts/generate.py:2
Finding

Unbounded Third-Party Dependency Resolution

Content
View full analysis

Vulnerability Details

File Location: scripts/generate.py:2-7
Vulnerability Type: Unpinned dependency and non-reproducible dependency resolution
Risk Level: Medium

Vulnerable Code

python
# /// script
# requires-python = ">=3.10"
# dependencies = [
#     "httpx>=0.25.0",
# ]
# ///

The documented execution method in SKILL.md uses uv run, which processes this dependency declaration:

bash
uv run ~/.clawdbot/skills/veed-ugc/scripts/generate.py \
  --image "https://example.com/person-with-product.png" \
  --script "Hola! Les quiero mostrar este producto increíble que acabo de probar." \
  --output "ugc-video.mp4"

Technical Analysis

The dependency specification accepts any httpx release at or above version 0.25.0. There is no upper version boundary, lockfile, integrity hash, or recorded transitive dependency set.

As a result, executing the documented uv run command at different times can install different package versions. Future releases or altered transitive dependencies can therefore become part of the Skill's effective code without being included in the reviewed project.

The dependency name is legitimate and there is no evidence of typosquatting, dependency confusion, or a currently malicious package. The finding concerns unsafe, unbounded supply-chain resolution rather than proof that the present dependency is malicious.

Attack Path

  1. A future direct or transitive dependency release is compromised, malicious, or contains a severe vulnerability.
  2. A user invokes the Skill through the documented uv run command in an environment without a previously locked resolution.
  3. The package resolver selects the newer version because it satisfies httpx>=0.25.0.
  4. The package is downloaded and imported into the Skill process.
  5. Malicious package code or vulnerable behavior executes with the permissions and environment access of the user running the Sk ...[truncated 582 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin httpx to a specific reviewed version rather than using an open-ended lower bound.
  2. Lock all transitive dependencies using a committed lockfile supported by the chosen execution workflow.
  3. Use package hashes or another integrity-verification mechanism where supported.
  4. Retrieve packages only from explicitly configured, trusted registries.
  5. Update dependencies through a controlled review process that includes vulnerability scanning and regression testing.
  6. Periodically refresh pins so security patches are adopted deliberately rather than through uncontrolled runtime resolution.
  7. Consider executing the Skill in a restricted environment with minimal filesystem, environment-variable, and network access to reduce dependency compromise impact.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (8)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/generate.py (reported line 33)May include surrounding context.

python
def get_api_key(provided_key: str | None) -> str | None:
    """Get API key from argument or environment."""
    if provided_key:
        return provided_key
    return os.environ.get("COMFY_DEPLOY_API_KEY")

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding

The skill documents and exemplifies network access to a third-party API and likely use of environment-backed API credentials, but it declares no explicit tool scope or permissions boundary. That mismatch increases the chance an agent will run the skill with broader capabilities than users expect, enabling unintended data egress or access to sensitive environment variables during execution.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
86% confidence
Finding

The documented endpoint indicates the skill depends on a third-party network service for processing user content. In this context, the skill handles images of people and promotional scripts, so external transmission can expose personal likenesses, proprietary creative assets, or customer campaign material if not clearly constrained and consented to.

Content

Scanner excerpt · SKILL.md (reported line 21)May include surrounding context.

md
## API Details

**Endpoint:** `https://api.comfydeploy.com/api/run/deployment/queue`
**Deployment ID:** `627c8fb5-1285-4074-a17c-ae54f8a5b5c6`

## Required Inputs

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

The example script and surrounding guidance center on a specific Spanish/Argentinian phrasing style, and the documented constraints repeatedly reinforce that exact wording should be used. Because the skill does not state that other languages/locales are supported or give the user an explicit choice, this can be read as imposing a locale-specific output style by default.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
88% confidence
Finding

The skill sends user-supplied script content, image references, and authorization-backed requests to an external service. This is dangerous if users are not clearly warned that potentially sensitive marketing assets, personal images, or confidential text will leave the local environment and be processed by a third party.

Content

Scanner excerpt · SKILL.md (reported line 93)May include surrounding context.

Direct API Call

javascript
const response = await fetch("https://api.comfydeploy.com/api/run/deployment/queue", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",

External Transmission

Medium
Category
Data Exfiltration
Confidence
88% confidence
Finding

The skill sends user-supplied script content, image references, and authorization-backed requests to an external service. This is dangerous if users are not clearly warned that potentially sensitive marketing assets, personal images, or confidential text will leave the local environment and be processed by a third party.

Content

Scanner excerpt · SKILL.md (reported line 93)May include surrounding context.

Direct API Call

javascript
const response = await fetch("https://api.comfydeploy.com/api/run/deployment/queue", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script clearly sends user-provided image content and dialogue text to third-party infrastructure for upload, processing, and output retrieval, but the usage/help text does not explicitly warn users that their media and script will leave the local environment. In a skill handling potentially sensitive faces, product imagery, and marketing copy, this creates a real privacy and informed-consent risk even if the behavior is functionally required.

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 · scripts/generate.py (reported line 28)May include surrounding context.

python
DEPLOYMENT_ID = "627c8fb5-1285-4074-a17c-ae54f8a5b5c6"
API_BASE = "https://api.comfydeploy.com/api"
DEFAULT_VOICE_ID = "PBi4M0xL4G7oVYxKgqww"

Static analysis

No suspicious patterns detected.