Back to skill

Security audit

Images & videos generation with Gemini 3 Pro Image + Qwen Wan 2.6 (video) via one API key

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent media-generation client, but its API-key handling and video download path have enough network-safety gaps to require review before installation.

Install only if you trust AIsa with your prompts, reference image URLs, and AIsa API key. Prefer using AISA_API_KEY from the environment rather than passing keys on the command line, use a controlled output directory, and be cautious with video-wait --download until URL allowlisting, redirect checks, and file-size limits 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 (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/media_gen_client.py:43
Finding
Bearer Token Exposure Through Unrestricted HTTP Redirects<![CDATA[ ## Vulnerability Details **File Location**: `scripts/media_gen_client.py:43-62` **Vulnerability Type**: Authenticated request redirect trust-boundary violation **Risk Level**: Medium ### Vulnerable Code ```python def _http_request_json( *, method: str, url: str, api_key: str, headers: Optional[Dict[str, str]] = None, body: Optional[Dict[str, Any]] = None, timeout_s: int = 60, user_agent: str = "OpenClaw-Media-Gen/1.0", ) -> Dict[str, Any]: all_headers = { "Authorization": f"Bearer {api_key}", "Accept": "application/json", "User-Agent": user_agent, } if headers: all_headers.update(headers) data: Optional[bytes] = None if body is not None: data = json.dumps(body).encode("utf-8") all_headers.setdefault("Content-Type", "application/json") elif method.upper() in {"POST", "PUT", "PATCH"}: data = b"{}" all_headers.setdefault("Content-Type", "application/json") req = urllib.request.Request(url, data=data, headers=all_headers, method=method.upper()) try: with urllib.request.urlopen(req, timeout=timeout_s) as resp: ``` ### Technical Analysis The client places the AIsa API key in the `Authorization` header and submits the request through `urllib.request.urlopen`. Python's default URL opener automatically processes HTTP redirects. The code neither disables redirects nor verifies that a redirect remains on the expected HTTPS origin. Consequently, the authenticated request can cross from the trusted `api.aisa.one` origin to another origin without an application-level authorization check. Redirect processing may propagate sensitive request headers, including the bearer token. This behavior exceeds the minimum privilege needed for the declared functionality: the credential only needs to be disclosed to the documented AIsa API origin. Exploitation requires control over, or compromise of, an endpoint in the authenticated reque ...[truncated 1179 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable automatic redirects for requests carrying credentials, or use a custom `HTTPRedirectHandler`. 2. Permit authenticated requests only to an explicit allowlist containing the documented AIsa HTTPS origin. 3. If redirects are operationally required: - Resolve the new URL against the current URL. - Require the `https` scheme. - Compare the normalized hostname and effective port with the original origin. - Reject user-info components and unexpected ports. - Strip `Authorization` whenever the origin changes. - Apply a strict redirect-count limit. 4. Do not resend POST request bodies across cross-origin redirects. 5. Add tests for same-origin redirects, cross-origin redirects, HTTPS-to-HTTP downgrades, malformed destinations, and redirect loops. 6. Prefer environment-based secret handling over `--api-key`, because command-line arguments may be exposed through process listings and shell history. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/media_gen_client.py:79
Finding
Unrestricted Fetch of an API-Controlled Video URL<![CDATA[ ## Vulnerability Details **File Location**: `scripts/media_gen_client.py:79-99` and `scripts/media_gen_client.py:257-261` **Vulnerability Type**: Server-side request forgery and unbounded remote file download **Risk Level**: Medium ### Vulnerable Code ```python def _download_to_file(url: str, out_path: str, timeout_s: int = 300) -> Dict[str, Any]: """ Download a (possibly signed) URL to local file. Designed for OSS signed URLs returned by video generation tasks. """ os.makedirs(os.path.dirname(out_path) or ".", exist_ok=True) req = urllib.request.Request(url, headers={"User-Agent": "OpenClaw-Media-Gen/1.0"}) try: with urllib.request.urlopen(req, timeout=timeout_s) as resp, open(out_path, "wb") as f: total = 0 while True: chunk = resp.read(1024 * 1024) # 1MB if not chunk: break f.write(chunk) total += len(chunk) return {"success": True, "saved_to": out_path, "bytes": total} except Exception as e: return {"success": False, "error": str(e), "url": url, "saved_to": out_path} ``` The remote value reaches the download function here: ```python if status in {"SUCCEEDED", "FAILED", "CANCELED"}: if status == "SUCCEEDED" and getattr(args, "download", False): video_url = (resp.get("output") or {}).get("video_url") or (resp.get("output") or {}).get("videoUrl") if video_url: out_path = args.out or _safe_filename("mp4") dl = _download_to_file(video_url, out_path) resp = {**resp, "download": dl} ``` ### Technical Analysis The `video_url` is obtained from the remote task-status response and passed directly to `urllib.request.urlopen`. The client does not validate: - The URL scheme. - The destination hostname or port. - Whether DNS resolves to loopback, private, link-local, multicast, or reserved addresses. - Redirect destinations. - The respon ...[truncated 2254 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Accept only `https` download URLs. 2. Maintain an explicit allowlist of approved AIsa media-storage domains. If storage hosts vary, derive and document a constrained suffix allowlist while preventing suffix-confusion attacks. 3. Normalize and validate the hostname and port before connecting. 4. Resolve all destination addresses and reject loopback, private, link-local, multicast, unspecified, and reserved ranges for both IPv4 and IPv6. 5. Use a redirect handler that repeats all scheme, hostname, port, and resolved-address checks for every redirect target. 6. Set a maximum redirect count. 7. Enforce a maximum file size using both `Content-Length`, when available, and a running byte counter while streaming. 8. Validate that the response content type is an expected video type, such as `video/mp4`. 9. Write to a securely created temporary file in the destination directory, verify the completed download, and atomically replace the requested output only after success. 10. Delete partial files when an exception, timeout, type mismatch, or size-limit violation occurs. 11. Apply connection and read timeouts rather than relying only on a single broad timeout value. 12. Avoid including full signed URLs in error output because query parameters may contain temporary access credentials. ]]>
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
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (17)

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

Critical
Category
Data Flow
Content
req = urllib.request.Request(url, data=data, headers=all_headers, method=method.upper())
    try:
        with urllib.request.urlopen(req, timeout=timeout_s) as resp:
            raw = resp.read().decode("utf-8")
            return json.loads(raw) if raw else {}
    except urllib.error.HTTPError as e:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

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

Critical
Category
Data Flow
Content
os.makedirs(os.path.dirname(out_path) or ".", exist_ok=True)
    req = urllib.request.Request(url, headers={"User-Agent": "OpenClaw-Media-Gen/1.0"})
    try:
        with urllib.request.urlopen(req, timeout=timeout_s) as resp, open(out_path, "wb") as f:
            total = 0
            while True:
                chunk = resp.read(1024 * 1024)  # 1MB
Confidence
90% confidence
Finding
The downloader fetches a URL returned by the remote task API and writes the response to disk without validating the destination host, scheme, or response size. If the upstream service is compromised or returns attacker-controlled signed URLs, this can enable SSRF-like access from the client environment to arbitrary endpoints and potentially cause resource exhaustion by downloading very large files.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill requires environment access for an API key and performs outbound network requests, but it does not declare an explicit tool scope such as permissions or allowed-tools. This creates an authorization gap where consumers may not understand or constrain what the skill can access, increasing the risk of unintended secret use or network egress.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The file mixes English headings with core instructional content in Chinese, including the description of capabilities and usage notes. Under the policy rule, forcing a specific language without user opt-in or justification is a natural-language policy concern.

External Transmission

Medium
Category
Data Exfiltration
Content
文档:`google-gemini-chat`(GenerateContent)见 `https://aisa.mintlify.app/api-reference/chat/chat-api/google-gemini-chat.md`。

### curl 示例(返回 inline_data 时为图片)

```bash
curl -X POST "https://api.aisa.one/v1/models/gemini-3-pro-image-preview:generateContent" \
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
OpenClaw Media Gen - AIsa API Client

Image:
  - Gemini GenerateContent: POST https://api.aisa.one/v1/models/{model}:generateContent

Video:
  - Wan 2.6 async task:
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
OpenClaw Media Gen - AIsa API Client

Image:
  - Gemini GenerateContent: POST https://api.aisa.one/v1/models/{model}:generateContent

Video:
  - Wan 2.6 async task:
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
OpenClaw Media Gen - AIsa API Client

Image:
  - Gemini GenerateContent: POST https://api.aisa.one/v1/models/{model}:generateContent

Video:
  - Wan 2.6 async task:
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
OpenClaw Media Gen - AIsa API Client

Image:
  - Gemini GenerateContent: POST https://api.aisa.one/v1/models/{model}:generateContent

Video:
  - Wan 2.6 async task:
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
OpenClaw Media Gen - AIsa API Client

Image:
  - Gemini GenerateContent: POST https://api.aisa.one/v1/models/{model}:generateContent

Video:
  - Wan 2.6 async task:
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
OpenClaw Media Gen - AIsa API Client

Image:
  - Gemini GenerateContent: POST https://api.aisa.one/v1/models/{model}:generateContent

Video:
  - Wan 2.6 async task:
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
OpenClaw Media Gen - AIsa API Client

Image:
  - Gemini GenerateContent: POST https://api.aisa.one/v1/models/{model}:generateContent

Video:
  - Wan 2.6 async task:
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
OpenClaw Media Gen - AIsa API Client

Image:
  - Gemini GenerateContent: POST https://api.aisa.one/v1/models/{model}:generateContent

Video:
  - Wan 2.6 async task:
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
OpenClaw Media Gen - AIsa API Client

Image:
  - Gemini GenerateContent: POST https://api.aisa.one/v1/models/{model}:generateContent

Video:
  - Wan 2.6 async task:
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
OpenClaw Media Gen - AIsa API Client

Image:
  - Gemini GenerateContent: POST https://api.aisa.one/v1/models/{model}:generateContent

Video:
  - Wan 2.6 async task:
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Low
Confidence
96% confidence
Finding
The skill description is written in Chinese starting at L03, and the file provides no indication that users can choose another language or that the skill is intentionally region-specific. This can violate a language/locale policy when a skill imposes one language by default without opt-in.

Missing User Warnings

Low
Confidence
90% confidence
Finding
The documentation states that client scripts automatically parse returned media and save files locally, and later describes automatic MP4 download, without an explicit warning about local file writes. Silent or unexpected writes can overwrite user files, consume disk space, or store unreviewed content on the host.

Static analysis

No suspicious patterns detected.