Back to skill

Security audit

ZenMux Media Gen

Security checks for vulnerabilities and agentic risk

Overview

This ZenMux media-generation skill mostly does what it says, but it needs review because it trusts API-returned download URLs and saves unbounded remote content locally.

Install only if you trust ZenMux and are comfortable sending prompts, optional reference image URLs, and your ZenMux API key to its API. Run it in an environment with restricted network egress and limited disk space if possible, and be cautious with automatic downloads until URL allowlisting, streaming size limits, and content validation are added.

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/zenmux_media_client.py:54
Finding

Unvalidated and Unbounded Downloads from API-Controlled URLs

Content
View full analysis

Vulnerability Details

File Location: scripts/zenmux_media_client.py, lines 54-70 and 155-166
Vulnerability Type: Server-Side Request Forgery-like behavior and resource exhaustion through unsafe media downloads
Risk Level: Medium

Vulnerable Code

python
if "url" in image_data:
    # Download from URL
    img_response = requests.get(image_data["url"], timeout=30)
    img_data = img_response.content
elif "base64" in image_data:
    img_data = base64.b64decode(image_data["base64"])
else:
    print(f"Unknown image format: {image_data}", file=sys.stderr)
    sys.exit(1)

if out:
    with open(out, "wb") as f:
        f.write(img_data)
    print(f"Image saved to: {out}")
    return out
python
video_url = status["output"]["url"]
print(f"Downloading from: {video_url}")

response = requests.get(video_url, timeout=300)
response.raise_for_status()

if out is None:
    out = f"{task_id}.mp4"

with open(out, "wb") as f:
    f.write(response.content)

Technical Analysis

The client trusts media URLs returned by the ZenMux API and passes them directly to requests.get() without validating their scheme, hostname, resolved IP address, port, or redirect destination. If the API response is compromised or manipulated, the client can be induced to contact loopback, link-local, private-network, or cloud metadata endpoints using the network privileges of the local process.

Both download paths also access .content, which buffers the entire response in memory before writing it. Neither path imposes a maximum response size. A malicious endpoint can therefore return an oversized or indefinitely streamed response, potentially exhausting memory and subsequently disk space. The image path additionally does not call raise_for_status(), allowing HTTP error bodies to be accepted and written as image output.

This network access is broader than the minimum privilege needed to downloa ...[truncated 1840 chars]

Remediation
View remediation

Remediation Suggestions

  1. Allow only https media URLs and reject URLs containing embedded credentials, unexpected ports, fragments, or unsupported schemes.
  2. Maintain an explicit allowlist of documented ZenMux media hostnames rather than accepting arbitrary response destinations.
  3. Resolve the destination hostname before connecting and reject loopback, private, link-local, multicast, reserved, and unspecified IPv4 and IPv6 addresses.
  4. Disable automatic redirects or validate the scheme, hostname, and resolved address again for every redirect target.
  5. Use streaming downloads with stream=True, process fixed-size chunks, and enforce strict maximum image and video byte limits.
  6. Check Content-Length when available, while still enforcing the limit during streaming because that header may be absent or dishonest.
  7. Call raise_for_status() in the image download path before accepting response content.
  8. Validate the returned media type using response headers and file signatures before saving it.
  9. Write downloads to a temporary file, abort and delete partial content when limits are exceeded, and atomically rename the file only after successful validation.
  10. Apply restricted network egress controls to the Skill process so it cannot access local, private, or metadata networks even if application-level validation is bypassed.
Vulnerability Patterns
  • 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
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (21)

Tainted flow: 'headers' from os.environ.get (line 135, credential/environment) → requests.post (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/zenmux_media_client.py (reported line 43)May include surrounding context.

python
print(f"Generating image with model: {model}")
    print(f"Prompt: {prompt}")
    
    response = requests.post(url, headers=headers, json=data, timeout=120)
    response.raise_for_status()
    
    result = response.json()

Tainted flow: 'headers' from os.environ.get (line 135, credential/environment) → requests.post (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/zenmux_media_client.py (reported line 113)May include surrounding context.

python
print(f"Prompt: {prompt}")
    print(f"Duration: {duration}s, Resolution: {resolution}")
    
    response = requests.post(url, headers=headers, json=data, timeout=60)
    response.raise_for_status()
    
    result = response.json()

Tainted flow: 'headers' from os.environ.get (line 135, credential/environment) → requests.get (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/zenmux_media_client.py (reported line 139)May include surrounding context.

python
"Authorization": f"Bearer {ZENMUX_API_KEY}"
    }
    
    response = requests.get(url, headers=headers, timeout=30)
    response.raise_for_status()
    
    return response.json()

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill declares environment and network capabilities via metadata and documented commands, but does not define an explicit tool scope such as permissions or allowed-tools. In an agent setting, that can cause overbroad execution authority, making it easier for the skill to access secrets and perform outbound requests beyond what users may expect.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill mixes English headings with substantial Chinese-only usage instructions and examples, which effectively imposes a language preference on users. The policy allows language constraints only when users are given a choice or when the locale limitation is clearly documented and justified, neither of which is present here.

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 · SKILL.md (reported line 50)May include surrounding context.

  • Base URL: https://api.zenmux.ai/v1
  • POST /chat/completions(OpenAI兼容格式)

curl 示例

bash
curl -X POST "https://api.zenmux.ai/v1/chat/completions" \

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 · SKILL.md (reported line 88)May include surrounding context.

  • POST /images/generations(Google Veo 使用 Image Generation API)
bash
curl -X POST "https://api.zenmux.ai/v1/images/generations" \
  -H "Authorization: Bearer $ZENMUX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{

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 · SKILL.md (reported line 47)May include surrounding context.

md
from pathlib import Path

ZENMUX_API_KEY = os.environ.get("ZENMUX_API_KEY")
ZENMUX_BASE_URL = "https://api.zenmux.ai/v1"

DEFAULT_IMAGE_MODEL = "google/gemini-3-pro-image-preview"
DEFAULT_VIDEO_MODEL = "google/veo-3.1-generate-001"

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 · SKILL.md (reported line 53)May include surrounding context.

md
from pathlib import Path

ZENMUX_API_KEY = os.environ.get("ZENMUX_API_KEY")
ZENMUX_BASE_URL = "https://api.zenmux.ai/v1"

DEFAULT_IMAGE_MODEL = "google/gemini-3-pro-image-preview"
DEFAULT_VIDEO_MODEL = "google/veo-3.1-generate-001"

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 · SKILL.md (reported line 84)May include surrounding context.

md
from pathlib import Path

ZENMUX_API_KEY = os.environ.get("ZENMUX_API_KEY")
ZENMUX_BASE_URL = "https://api.zenmux.ai/v1"

DEFAULT_IMAGE_MODEL = "google/gemini-3-pro-image-preview"
DEFAULT_VIDEO_MODEL = "google/veo-3.1-generate-001"

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 · SKILL.md (reported line 88)May include surrounding context.

md
from pathlib import Path

ZENMUX_API_KEY = os.environ.get("ZENMUX_API_KEY")
ZENMUX_BASE_URL = "https://api.zenmux.ai/v1"

DEFAULT_IMAGE_MODEL = "google/gemini-3-pro-image-preview"
DEFAULT_VIDEO_MODEL = "google/veo-3.1-generate-001"

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 · SKILL.md (reported line 104)May include surrounding context.

md
from pathlib import Path

ZENMUX_API_KEY = os.environ.get("ZENMUX_API_KEY")
ZENMUX_BASE_URL = "https://api.zenmux.ai/v1"

DEFAULT_IMAGE_MODEL = "google/gemini-3-pro-image-preview"
DEFAULT_VIDEO_MODEL = "google/veo-3.1-generate-001"

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/zenmux_media_client.py (reported line 17)May include surrounding context.

python
from pathlib import Path

ZENMUX_API_KEY = os.environ.get("ZENMUX_API_KEY")
ZENMUX_BASE_URL = "https://api.zenmux.ai/v1"

DEFAULT_IMAGE_MODEL = "google/gemini-3-pro-image-preview"
DEFAULT_VIDEO_MODEL = "google/veo-3.1-generate-001"

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This code constructs a request body containing the user-supplied prompt and transmits it to the ZenMux API via HTTPS. While the script logs the prompt locally, it does not clearly warn users that their input is being sent to a third-party service, which is a relevant privacy disclosure for a code file.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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/zenmux_media_client.py (reported line 43)May include surrounding context.

python
print(f"Generating image with model: {model}")
    print(f"Prompt: {prompt}")
    
    response = requests.post(url, headers=headers, json=data, timeout=120)
    response.raise_for_status()
    
    result = response.json()

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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/zenmux_media_client.py (reported line 113)May include surrounding context.

python
print(f"Generating image with model: {model}")
    print(f"Prompt: {prompt}")
    
    response = requests.post(url, headers=headers, json=data, timeout=120)
    response.raise_for_status()
    
    result = response.json()

Tainted flow: 'out' from requests.post (line 172, network input) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/zenmux_media_client.py (reported line 68)May include surrounding context.

python
sys.exit(1)
                        
                        if out:
                            with open(out, "wb") as f:
                                f.write(img_data)
                            print(f"Image saved to: {out}")
                            return out

Tainted flow: 'out' from requests.post (line 172, network input) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

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

python
sys.exit(1)
                        
                        if out:
                            with open(out, "wb") as f:
                                f.write(img_data)
                            print(f"Image saved to: {out}")
                            return out

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The docstrings and CLI help describe creating and checking video generation tasks, but the implementation uses /images/generations and /images/generations/{task_id} for those operations. This is an active contradiction between the documented intent and the concrete API behavior, not merely missing detail.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The request includes the user's prompt and may also include a reference image URL, both of which are transmitted to the remote ZenMux API. The script describes media generation generally, but it does not explicitly warn that user-provided content is sent to a third-party network service.

Content

No source excerpt is available for this finding.

Tainted flow: 'video_url' from requests.post (line 165, network input) → requests.get (network output)

Medium
Category
Data Flow
Confidence
84% confidence
Finding

The code downloads a URL returned by the remote API without validating its scheme, host, or content type. If the upstream service is compromised or returns attacker-controlled URLs, the client could be induced to make arbitrary outbound requests, creating an SSRF-like pivot or downloading untrusted content from unexpected locations.

Content

Scanner excerpt · scripts/zenmux_media_client.py (reported line 168)May include surrounding context.

python
video_url = status["output"]["url"]
                    print(f"Downloading from: {video_url}")
                    
                    response = requests.get(video_url, timeout=300)
                    response.raise_for_status()
                    
                    if out is None:

Static analysis

No suspicious patterns detected.