Back to skill

Security audit

Free Media Gen(免费生图生视频)

Security checks across malware telemetry and agentic risk

Overview

The skill is a real third-party media generator, but it uses stored API keys and external downloads with weak scoping safeguards, so users should review it before installing.

Install only if you are comfortable sending prompts, request metadata, and generated media requests to the listed third-party providers using API keys from your WorkBuddy models.json. Avoid sensitive or regulated prompts, review config.json endpoints before use, restrict who can edit models.json and the skill config, and treat the audit option as a state-changing operation that may consume provider quota and update local files.

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

Error
Location
scripts/media_auditor.py:49
Finding
Bearer credentials can be redirected to attacker-controlled endpoints<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/_common.py:74-99` - `scripts/media_auditor.py:49-72` - `scripts/agnes_image.py:53-61` - `scripts/agnes_video.py:98-138` - `scripts/kolors_image.py:40-54` - `scripts/sensenova_image.py:43-56` **Vulnerability Type**: Improper URL and hostname validation leading to credential disclosure **Risk Level**: High ### Vulnerable Code The API-key resolver identifies providers using substring matching: ```python def resolve_api_key(ref, provider=None): """Resolve an API key from a config reference or provider name.""" models = load_models_json().get("models", []) if isinstance(load_models_json(), dict) else load_models_json() if isinstance(models, dict): models = models.get("models", []) if ref and ref.startswith("models.json:"): target_id = ref.split(":", 1)[1] for e in models: if e.get("id") == target_id: return e.get("apiKey") host = PROVIDER_HOSTS.get(provider) if host: for e in models: url = e.get("url", "") if host in url: return e.get("apiKey") if ref in PROVIDER_HOSTS: host = PROVIDER_HOSTS[ref] for e in models: if host in e.get("url", ""): return e.get("apiKey") raise KeyError("Unable to resolve API key: ref=%s, provider=%s" % (ref, provider)) ``` The auditor applies the same substring test and derives the credential-bearing request base from the matched URL: ```python def base_for(provider, models): host = C.PROVIDER_HOSTS.get(provider) if not host: return None for e in models: if host in e.get("url", ""): u = e["url"].rstrip("/") for suffix in ("/chat/completions", "/completions", "/v1beta/openai", "/v1beta", "/v4"): if u.endswith(suffix): u = u[: -len(suffix)] break retu ...[truncated 3539 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse every URL with `urllib.parse.urlsplit()` before using it. 2. Require: - `scheme == "https"` - Exact, case-normalized hostname equality with the selected provider. - No embedded username or password. - No fragments. - Only explicitly approved ports. 3. Replace substring checks with exact provider-origin validation: ```python from urllib.parse import urlsplit APPROVED_HOSTS = { "agnes": {"api.agnes-ai.cn"}, "sensenova": {"token.sensenova.cn"}, "siliconflow": {"api.siliconflow.cn"}, } def validate_provider_url(provider, url): parsed = urlsplit(url) allowed = APPROVED_HOSTS.get(provider, set()) if parsed.scheme.lower() != "https": raise ValueError("Only HTTPS endpoints are allowed") if parsed.username is not None or parsed.password is not None: raise ValueError("Credentials in endpoint URLs are prohibited") if (parsed.hostname or "").lower() not in allowed: raise ValueError("Endpoint hostname is not approved for this provider") if parsed.port not in (None, 443): raise ValueError("Endpoint port is not approved") return parsed ``` 4. Do not infer credential-bearing catalog endpoints from arbitrary `models.json` URLs. Store immutable provider API origins in code and append only known paths. 5. Validate each `config.json` endpoint against the model’s declared provider before resolving or attaching its API key. 6. Disable automatic cross-origin redirects for credential-bearing requests, or revalidate every redirect target and strip authorization headers whenever the origin changes. 7. Use a structured provider-to-key mapping instead of selecting keys based on model URL substrings. 8. Restrict write permissions on `config.json` and `models.json`, and treat configuration changes as security-sensitive. 9. Add regression tests for malicious values such as: - `https://api.agnes-ai.cn.attacker.example/` - `https://attacker.example/api.ag ...[truncated 157 chars]

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/_common.py:132
Finding
Unrestricted provider-supplied media downloads permit blind SSRF and resource exhaustion<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/_common.py:132-146` - `scripts/agnes_image.py:70-79` - `scripts/agnes_video.py:204-214` - `scripts/kolors_image.py:64-73` - `scripts/sensenova_image.py:66-75` **Vulnerability Type**: Server-side request forgery and unbounded resource consumption **Risk Level**: Medium ### Vulnerable Code The shared download function accepts an arbitrary URL, follows redirects through `urllib`, and reads the complete response into memory without a size limit: ```python def download(url, path, retries=3, timeout=120): """Download a binary URL to the specified path.""" os.makedirs(os.path.dirname(path) or ".", exist_ok=True) last_err = None for attempt in range(retries): try: req = urllib.request.Request( url, headers={"User-Agent": "free-media-gen/1.0"}, ) with urllib.request.urlopen(req, timeout=timeout) as resp, open(path, "wb") as out: out.write(resp.read()) return True except Exception as e: last_err = e time.sleep(2 ** attempt) sys.stderr.write("Download failed: %s -> %s\n" % (url, last_err)) return False ``` Provider-returned URLs are passed directly into this function: ```python video_url = None if vid: q = urllib.parse.urlencode({ "video_id": vid, "model_name": args.model, }) st4, b4 = get(root + "/agnesapi?" + q, headers) try: j4 = json.loads(b4) video_url = (j4.get("metadata") or {}).get("url") or dig_url(j4) except Exception: pass if not video_url: print(json.dumps({ "ok": False, "stage": "completed_no_url", "task_id": tid, "video_id": vid, "note": "The task completed but no media URL was returned", })) sys.exit(1) out_path = os.path.join( out_dir, "agnes_video_%s.mp4" % str(tid), ) ok = C.download(video_url, out_path, ret ...[truncated 3449 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Permit only HTTPS media URLs. 2. Maintain an explicit allowlist of approved media and CDN hostnames for each provider. 3. Validate every redirect destination rather than only the initial URL. 4. Resolve destination hostnames and reject all loopback, private, link-local, multicast, unspecified, and reserved IP addresses for both IPv4 and IPv6. 5. Re-resolve and revalidate at connection time to reduce DNS-rebinding exposure. 6. Stream responses in bounded chunks instead of calling `resp.read()` without a limit: ```python MAX_MEDIA_BYTES = 100 * 1024 * 1024 CHUNK_SIZE = 64 * 1024 written = 0 with opener.open(req, timeout=timeout) as resp, open(path, "wb") as out: while True: chunk = resp.read(CHUNK_SIZE) if not chunk: break written += len(chunk) if written > MAX_MEDIA_BYTES: raise ValueError("Media response exceeds the configured size limit") out.write(chunk) ``` 7. Check `Content-Length` when present, while still enforcing a streamed byte limit because the header may be absent or false. 8. Require expected media content types, such as approved `image/*` formats or `video/mp4`. 9. Validate file signatures after download and delete partial or invalid files. 10. Apply strict maximum lengths before decoding base64 data. Estimate decoded size from the encoded length before calling `base64.b64decode()`. 11. Use temporary files followed by an atomic rename only after validation succeeds. 12. Apply restrictive network egress controls at the runtime or container level so the Skill cannot reach cloud metadata endpoints, localhost services, or private networks. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (21)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill declares no permissions while its documented behavior clearly requires reading local config files, writing output and audit reports, accessing environment variables, and making network requests to third-party providers. This is dangerous because users and the platform cannot accurately assess or gate the skill's effective access, and the hidden capability set includes both local data exposure and outbound transmission of prompts and possibly provider-linked secrets.

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding
The public description frames the skill as a simple free media generator, but the instructions also include local path discovery, inspection of WorkBuddy configuration, remote model-catalog auditing, live generation tests, mutation of config.json, and report generation in the workspace. This mismatch is risky because users may invoke the skill expecting only generation while it performs broader local inspection, outbound calls, and state changes they were not clearly warned about.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
The helper resolves API keys from a global models.json and, if an exact ID is not found, falls back to matching by provider hostname. That broadens credential access beyond the skill's explicitly configured media model entry and can cause the skill to silently reuse unrelated account credentials for third-party services, which is especially risky here because the skill is designed to bypass official model paths and call external providers directly.

Description-Behavior Mismatch

Medium
Confidence
90% confidence
Finding
The script writes newly discovered models into config.json, changing persistent skill behavior and model inventory rather than merely auditing or generating media. Because the skill is described as a media-generation capability, silently mutating configuration extends scope and can introduce unreviewed third-party endpoints or models into future runs.

Context-Inappropriate Capability

Medium
Confidence
83% confidence
Finding
The script enumerates provider catalogs using configured API keys and performs live verification requests against third-party services. This exceeds simple media generation and can consume credits, probe external inventories, and expand the set of reachable models without clear user intent or least-privilege controls.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The README explicitly promotes direct calls to third-party media APIs and reuse of API keys from `models.json`, but it does not warn users that prompts, generated media, and associated metadata will be transmitted to external providers under those providers' policies. In a skill that intentionally bypasses the platform's built-in path, omitting disclosure about credential use and data-sharing materially increases privacy and trust risk for users.

Vague Triggers

Medium
Confidence
84% confidence
Finding
The trigger phrases include broad natural-language terms such as requests about free image/video generation or avoiding a watermark, which can cause the skill to activate during ordinary conversation without a strong explicit invocation boundary. Because activation leads to third-party API usage and local config inspection, accidental triggering can expose user prompts to external providers and cause unintended file or network activity.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill does not prominently warn that user prompts, media requests, and associated metadata will be sent to external providers including Agnes, SenseNova, and SiliconFlow. This is a meaningful privacy and compliance issue because users may supply sensitive text or images under the assumption processing stays within the local assistant or first-party tooling.

Vague Triggers

Medium
Confidence
83% confidence
Finding
The manifest description advertises broad capabilities such as directly calling third-party free models, bypassing a first-party ImageGen path, and producing unwatermarked output without defining trigger constraints, safety boundaries, or authorization expectations. In a skill-routing ecosystem, this can cause the agent or user to invoke a powerful external-content-generation workflow in unintended contexts, increasing the chance of policy bypass, data sharing with third parties, or misuse of unreviewed generation endpoints.

Missing User Warnings

Low
Confidence
81% confidence
Finding
The code reads API keys from models.json automatically at the utility layer with no user-visible disclosure or confirmation at the point of use. In this skill's context, that means a user may believe they are invoking a free generation feature while the skill silently consumes stored credentials for external third-party platforms.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script sends the raw user-provided prompt to a third-party image generation API in the JSON payload without any visible consent, warning, or data-minimization control. Prompts can contain sensitive personal, proprietary, or policy-restricted content, and this skill’s purpose explicitly emphasizes routing requests to external free providers and bypassing the platform’s native generator, which increases privacy and governance risk.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script deletes prior audit report files matching a date pattern in the workspace without confirmation, backup, or a dedicated subdirectory. This destructive cleanup can remove user data unexpectedly and is especially risky if the workspace contains reports needed for audit history or if naming collisions occur.

External Transmission

Medium
Category
Data Exfiltration
Content
"id": "agnes-image-2.1-flash",
      "provider": "agnes",
      "modality": "image",
      "endpoint": "https://api.agnes-ai.cn/v1/images/generations",
      "api_key_ref": "models.json:agnes-2.5-flash",
      "free": true,
      "needs_vpn": false,
Confidence
92% confidence
Finding
This configuration routes user prompts and generated media requests to a third-party endpoint at api.agnes-ai.cn, which creates a real data exfiltration and privacy boundary crossing risk. In context, the skill explicitly advertises bypassing the platform's native ImageGen flow and using external free providers, increasing the chance that users may not expect their prompts or media to be sent off-platform.

External Transmission

Medium
Category
Data Exfiltration
Content
"id": "agnes-video-v2.0",
      "provider": "agnes",
      "modality": "video",
      "endpoint": "https://api.agnes-ai.cn/v1/videos",
      "api_key_ref": "models.json:agnes-2.5-flash",
      "free": true,
      "needs_vpn": false,
Confidence
92% confidence
Finding
This video-generation endpoint sends potentially sensitive text, images, and generation parameters to an external Agnes service. Because the skill is centered on direct third-party media generation and bypass behavior, the external transmission is materially more dangerous than a generic API call: users may unknowingly expose proprietary prompts, uploaded reference images, or other private content.

External Transmission

Medium
Category
Data Exfiltration
Content
"id": "agnes-video-2.5-flash",
      "provider": "agnes",
      "modality": "video",
      "endpoint": "https://api.agnes-ai.cn/v1/videos",
      "api_key_ref": "models.json:agnes-2.5-flash",
      "free": true,
      "needs_vpn": false,
Confidence
92% confidence
Finding
This entry defines another external Agnes video endpoint, again creating a channel for off-platform transmission of user prompts and media. The risk is amplified by the skill's stated purpose of bypassing built-in media generation controls and watermarking expectations, which suggests intentional circumvention of safer/default product pathways.

External Transmission

Medium
Category
Data Exfiltration
Content
"default_size": "1280x720",
      "async_fetch": true,
      "submit": {
        "endpoint": "https://api.agnes-ai.cn/v1/videos",
        "required": [
          "model",
          "prompt",
Confidence
90% confidence
Finding
The nested submit endpoint explicitly instructs POSTing model, prompt, and other generation parameters to a third-party service, which is an external transmission of user-supplied data. Since prompts may contain sensitive business, personal, or regulated content, sending them to an undeclared third-party model provider without robust disclosure and consent is a genuine security/privacy concern.

External Transmission

Medium
Category
Data Exfiltration
Content
"id": "Kwai-Kolors/Kolors",
      "provider": "siliconflow",
      "modality": "image",
      "endpoint": "https://api.siliconflow.cn/v1/images/generations",
      "api_key_ref": "models.json:Qwen/Qwen3-8B",
      "free": true,
      "needs_vpn": false,
Confidence
89% confidence
Finding
This configuration sends image-generation requests to SiliconFlow, another external provider, creating the same prompt and metadata exposure risk. The surrounding skill description emphasizes use of third-party free models and bypass of native generation/watermark paths, which makes the outbound transfer less incidental and more security-relevant.

External Transmission

Medium
Category
Data Exfiltration
Content
"id": "agnes-image-2.5-flash",
      "provider": "agnes",
      "modality": "image",
      "endpoint": "https://api.agnes-ai.cn/v1/images/generations",
      "api_key_ref": "models.json:agnes-2.5-flash",
      "free": true,
      "needs_vpn": false,
Confidence
92% confidence
Finding
This Agnes image-generation endpoint is another confirmed off-platform transmission path for user prompts and possibly uploaded image inputs. In a skill explicitly designed to route around the standard media generation path, these outbound calls represent a meaningful privacy and policy bypass risk rather than a harmless implementation detail.

External Transmission

Medium
Category
Data Exfiltration
Content
GET {root}/agnesapi?video_id={video_id}&model_name={model}
  -> metadata.url 即 MP4 直链(**必须带 model_name**,否则该字段不返回)

base = https://api.agnes-ai.cn/v1 ;root = https://api.agnes-ai.cn
凭证经 _common 从 models.json 解析;不硬编码绝对路径。

用法:
Confidence
94% confidence
Finding
This script sends user prompts and authenticated requests to a third-party service at api.agnes-ai.cn and downloads returned media URLs, which is a real external data transmission risk. In the context of a skill explicitly designed to bypass the platform's native media generation and route content to external free providers, users may unknowingly expose prompts, metadata, and credentials to an untrusted service outside normal platform controls.

External Transmission

Medium
Category
Data Exfiltration
Content
# -*- coding: utf-8 -*-
"""硅基流动 SiliconFlow — Kolors 文生图 (free-media-gen)。

  POST https://api.siliconflow.cn/v1/images/generations
  {"model":"Kwai-Kolors/Kolors","prompt":..,"image_size":"1024x1024"}
  -> {"data":[{"url":..}]}   (部分版本返回 {"images":[{"url":..}]},脚本两者都处理)
Confidence
87% confidence
Finding
This script sends user-supplied prompts and an API bearer token to a third-party external service (api.siliconflow.cn) and then downloads returned image URLs to local storage. That is a real data-flow and trust-boundary crossing: prompt content may contain sensitive user data, and the skill explicitly advertises bypassing a first-party image service in favor of external providers, which increases privacy, compliance, and supply-chain risk.

Session Persistence

Medium
Category
Rogue Agent
Content
### Who it is for

Designed for WorkBuddy's custom-models registry (`models.json`). On other OpenClaw clients, write
each platform's API key into `~/.workbuddy/models.json` using the same shape (`id` / `url` / `apiKey`)
and the whole flow is reusable.
Confidence
84% confidence
Finding
The README instructs users to place API keys in a persistent local file at `~/.workbuddy/models.json`, which creates a session-persistence style secret storage risk if file permissions, encryption, or secret-management guidance are absent. Because the skill is designed to reuse those stored keys across clients and direct third-party calls, compromise of the file can expose reusable credentials and enable unauthorized use of external APIs.

VirusTotal

VirusTotal findings are pending for this skill version.

View on VirusTotal

Static analysis

No suspicious patterns detected.