Back to skill

Security audit

HappyHorse Video Generation and Editting

Security checks for vulnerabilities and agentic risk

Overview

This skill is mostly coherent for HappyHorse video generation, but it needs Review because it can upload arbitrary local files to Alibaba DashScope without clearly documenting or limiting that behavior.

Review before installing. Only use this skill if you are comfortable sending prompts, media URLs, and any selected local media files to Alibaba DashScope. Ensure the agent passes only intended image/video files, not paths to private documents, .env files, keys, or credentials.

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/happyhorse-magic.py:32
Finding
Unrestricted Local File Encoding and Upload to an External API<![CDATA[ ## Vulnerability Details **File Location**: `scripts/happyhorse-magic.py`, lines 32–46; network transmission paths at lines 184, 249–250, and 349–353 **Vulnerability Type**: Arbitrary local file disclosure through insufficient input validation **Risk Level**: Medium ### Vulnerable Code ```python def encode_local_file(path): """If path is a local file, return a data URI (data:{mime};base64,...). Otherwise return the original string (assumed to be a URL).""" if path.startswith(("http://", "https://", "oss://", "data:")): return path abs_path = os.path.expanduser(path) if not os.path.isfile(abs_path): print(f"Error: file not found: {abs_path}", file=sys.stderr) sys.exit(1) mime, _ = mimetypes.guess_type(abs_path) if mime is None: mime = "application/octet-stream" with open(abs_path, "rb") as f: b64 = base64.b64encode(f.read()).decode("utf-8") print(f" [base64] Encoded local file: {abs_path} ({mime}, {len(b64)} chars)") return f"data:{mime};base64,{b64}" ``` The resulting data URI is placed into outbound request payloads through the following paths: ```python # image2video_gen media = [{"type": "first_frame", "url": encode_local_file(args.first_frame)}] ``` ```python # reference2video_gen for ref_image in args.reference_images: media.append({"type": "reference_image", "url": encode_local_file(ref_image)}) ``` ```python # video_edit media = [{"type": "video", "url": encode_local_file(args.video)}] if args.reference_images: for img in args.reference_images: media.append({"type": "reference_image", "url": encode_local_file(img)}) ``` These payloads are subsequently transmitted using `requests.post()` to the fixed external endpoint: ```python BASE_URL = "https://dashscope.aliyuncs.com" ``` ### Technical Analysis The documented interface describes the image and video arguments as media URLs. The implementation additionally treats any argument that does n ...[truncated 3070 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Require explicit local-file syntax** - Treat media arguments as URLs by default. - If local upload is necessary, expose a clearly named option such as `--local-first-frame`. - Require explicit user confirmation before transmitting a local file to an external service. 2. **Restrict permissible filesystem locations** - Resolve paths with `os.path.realpath()` or `pathlib.Path.resolve()`. - Permit files only under a configured workspace or approved media directory. - Reject traversal and symlink resolutions that escape the approved directory. 3. **Enforce media allowlists** - Allow only the formats documented for each command. - For images, restrict input to JPEG, PNG, and WEBP. - For videos, restrict input to supported MP4 or MOV content. - Reject `application/octet-stream` rather than using it as a fallback. 4. **Validate actual file content** - Do not rely solely on filename extensions or `mimetypes`. - Inspect magic bytes and parse the file using a trusted media library. - Verify that the detected content type matches the expected argument type. 5. **Enforce size limits before reading** - Use `os.path.getsize()` before opening the file. - Enforce the documented image and video limits. - Avoid unbounded `f.read()`; use bounded or streaming processing where supported. 6. **Reduce accidental secret disclosure** - Reject known sensitive filenames and directories as a defense-in-depth measure, including `.env`, `.ssh`, credential stores, and common cloud configuration paths. - Avoid printing full local paths where logs may be retained. - Clearly document that accepted local files are transmitted to Alibaba DashScope. 7. **Add security-focused tests** - Verify rejection of non-media files, oversized files, symlinks outside the workspace, path traversal attempts, spoofed extensions, and sensitive configuration files. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (21)

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

Critical
Category
Data Flow
Content
print(f"  Duration  : {args.duration}s")
    print(f"  Watermark : {args.watermark}")

    resp = requests.post(url, headers=headers, json=payload, timeout=60)
    result = resp.json()

    if "code" in result:
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 408, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
print(f"  Duration  : {args.duration}s")
    print(f"  Watermark : {args.watermark}")

    resp = requests.post(url, headers=headers, json=payload, timeout=60)
    result = resp.json()

    if "code" in result:
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 408, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
print(f"  Duration  : {args.duration}s")
    print(f"  Watermark : {args.watermark}")

    resp = requests.post(url, headers=headers, json=payload, timeout=60)
    result = resp.json()

    if "code" in result:
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 408, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
print(f"  Duration  : {args.duration}s")
    print(f"  Watermark : {args.watermark}")

    resp = requests.post(url, headers=headers, json=payload, timeout=60)
    result = resp.json()

    if "code" in result:
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 408, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
print(f"[text2video-get] Checking task: {args.task_id}")

    resp = requests.get(url, headers=headers, timeout=60)
    result = resp.json()

    if "code" in result:
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 408, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
print(f"[text2video-get] Checking task: {args.task_id}")

    resp = requests.get(url, headers=headers, timeout=60)
    result = resp.json()

    if "code" in result:
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 408, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
print(f"[text2video-get] Checking task: {args.task_id}")

    resp = requests.get(url, headers=headers, timeout=60)
    result = resp.json()

    if "code" in result:
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 408, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
print(f"[text2video-get] Checking task: {args.task_id}")

    resp = requests.get(url, headers=headers, timeout=60)
    result = resp.json()

    if "code" in result:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill uses environment variables and network access to call an external model service, but it does not declare an explicit tool scope such as permissions or allowed-tools. This weakens reviewability and containment because an agent may invoke capabilities broader than users or platform policy expect, especially when handling externally supplied URLs and API credentials.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The documentation instructs users to submit image and video URLs to a third-party video-generation service but does not warn that user-provided media, prompts, and possibly derived outputs will be sent off-platform for processing. This creates a real privacy and data-handling risk, particularly if users provide sensitive, copyrighted, biometric, or otherwise confidential media.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The documentation instructs users to send prompts and externally hosted reference-image URLs to a third-party video-generation API but does not warn that this transmits potentially sensitive content off-platform. In a media skill that encourages uploading character/object reference images, omission of a privacy/data-sharing warning can lead users to unknowingly expose personal images, copyrighted assets, or confidential prompts to an external service.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This markdown file documents a network API that transmits user-provided video and image URLs to a remote service and requires an API key in the Authorization header. The description does not include any warning about sending potentially sensitive media to a third-party service or protecting credentials, which fits the markdown-specific missing user warnings criterion.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The helper accepts local file paths, base64-encodes their full contents, and sends them to a third-party cloud API, while the CLI does not present any explicit privacy, consent, or data-handling warning. In a skill context, users may provide sensitive images or videos assuming local processing, making unintended external transfer a realistic confidentiality risk.

External Transmission

Medium
Category
Data Exfiltration
Content
print(f"  Duration  : {args.duration}s")
    print(f"  Watermark : {args.watermark}")

    resp = requests.post(url, headers=headers, json=payload, timeout=60)
    result = resp.json()

    if "code" in result:
Confidence
90% confidence
Finding
This submission transmits user prompts and potentially sensitive generated-content instructions to an external service. In this tool's context that behavior is functional and expected, but it is still security-relevant because users may not realize their inputs are leaving the local environment and could include confidential information.

External Transmission

Medium
Category
Data Exfiltration
Content
print(f"  Resolution : {args.resolution}")
    print(f"  Duration   : {args.duration}s")

    resp = requests.post(url, headers=headers, json=payload, timeout=60)
    result = resp.json()

    if "code" in result:
Confidence
95% confidence
Finding
This request can upload full local image content after converting it to a data URI, meaning sensitive local media is transmitted to a remote API. In a media-editing skill this is expected functionality, but it increases the danger because users may supply private photos without a clear notice that the content is being exfiltrated from the local machine to a third party.

External Transmission

Medium
Category
Data Exfiltration
Content
print(f"  Ratio      : {args.ratio}")
    print(f"  Duration   : {args.duration}s")

    resp = requests.post(url, headers=headers, json=payload, timeout=60)
    result = resp.json()

    if "code" in result:
Confidence
95% confidence
Finding
This call uploads one or more reference images, which may contain biometric, personal, or proprietary visual data, to an external cloud service. Because the skill supports multi-image character/reference fusion, the contextual likelihood of users providing sensitive identity-bearing images makes the confidentiality concern more significant.

External Transmission

Medium
Category
Data Exfiltration
Content
print(f"  Resolution : {args.resolution}")
    print(f"  Audio      : {args.audio_setting}")

    resp = requests.post(url, headers=headers, json=payload, timeout=60)
    result = resp.json()

    if "code" in result:
Confidence
96% confidence
Finding
This operation can transmit an entire local video plus optional reference images to a remote API, creating substantial risk of exposing sensitive audiovisual content. In the editing-skill context, users are especially likely to upload personal or confidential recordings, so silent external transfer is more dangerous than ordinary text-only API use.

Missing User Warnings

Low
Confidence
87% confidence
Finding
This markdown file documents remote HTTP API usage and shows that prompts and media URLs are sent to an external endpoint, but it does not include any user-facing warning about data leaving the local environment. For markdown files, SQP-2 applies when descriptions omit warnings about behaviors that could affect user data or privacy.

Natural-Language Policy Violations

Low
Confidence
78% confidence
Finding
The only prompt examples shown in both request and response use Chinese text, which can imply a fixed language expectation. Because the document does not state that the API is Chinese-only, region-specific, or that users may choose their own language, this may violate the language/locale policy criterion.

Natural-Language Policy Violations

Low
Confidence
79% confidence
Finding
The example request hard-codes a Chinese-language prompt, which may imply a language preference in the skill documentation without stating that other languages are acceptable. Because the file does not justify a locale-specific constraint or offer explicit user language choice, this can be interpreted as a mild language/locale policy issue.

Natural-Language Policy Violations

Low
Confidence
82% confidence
Finding
The documentation uses a Chinese-only example instruction in the request body and repeats it in the sample response, without offering an alternative language example or stating that the API is region- or language-specific. This can be read as implicitly forcing a specific language in the skill documentation without user opt-in.

Static analysis

No suspicious patterns detected.