Back to skill

Security audit

Image & Video Generation

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it says, but it has review-worthy data-sharing and local-network/download risks users should understand before installing.

Install only if you are comfortable sending prompts and reference images/videos to AI Artist, and do not use private media unless you understand the provider's retention and sharing behavior. Avoid setting FEISHU_WEBHOOK_URL in shared environments unless you intend every prompt and result link to be posted there. Do not print your full AI_ARTIST_TOKEN; rotate it if it has appeared in logs. Use --download cautiously because the skill does not validate returned media URLs or download size.

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 (3)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate_image.py:35
Finding
Predictable Shared Temporary Cache Permits Cache Poisoning and Symlink Attacks<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_image.py:35-38, 166-191` **Vulnerability Type**: Predictable temporary file with insufficient ownership and symlink protections **Risk Level**: Medium ### Vulnerable Code ```python _MODEL_LIST_CACHE = {"rows": None, "expires_at": 0.0} _MODEL_LIST_TTL = 300 # 5 minutes import tempfile as _tempfile _MODEL_LIST_DISK_CACHE = os.path.join(_tempfile.gettempdir(), "deepsop_model_list.json") ``` ```python def _load_disk_cache(): """Load the disk cache file if present and still fresh; return rows or None.""" import time try: if not os.path.exists(_MODEL_LIST_DISK_CACHE): return None with open(_MODEL_LIST_DISK_CACHE, "r", encoding="utf-8") as f: blob = json.load(f) if not isinstance(blob, dict) or "rows" not in blob: return None if blob.get("expires_at", 0) < time.time(): return None return blob["rows"] except Exception: return None def _save_disk_cache(rows, expires_at): try: with open(_MODEL_LIST_DISK_CACHE, "w", encoding="utf-8") as f: json.dump({"rows": rows, "expires_at": expires_at}, f, ensure_ascii=False) except Exception: pass # best-effort ``` ### Technical Analysis The model list cache uses the fixed filename `deepsop_model_list.json` in the system-wide temporary directory. The code reads and writes this path using ordinary `open()` calls without: - Verifying that the file is a regular file. - Verifying that it is owned by the current user. - Rejecting symbolic links. - Creating it with exclusive and restrictive permissions. - Using an atomic write-and-replace operation. - Separating cache files by user identity. On multi-user systems, another local user may be able to create or modify the predictable path before the Skill runs. A forged cache can influence model availability decisions for up to the cache lifetime. More seriously, if t ...[truncated 1721 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store the cache in a private, per-user cache directory such as: - `$XDG_CACHE_HOME/ai-image-generator/` - `~/.cache/ai-image-generator/` - A platform-specific directory returned by a trusted cache-directory library. 2. Create the directory with permissions restricted to the current user, such as mode `0700` on POSIX systems. 3. Before reading an existing cache: - Use `os.lstat()` rather than following links. - Reject symbolic links and non-regular files. - Verify that the file owner matches the current user. - Reject files with unsafe group or world permissions. 4. Create new cache files with restrictive mode `0600`. 5. On supported POSIX platforms, open files using `os.open()` with `O_NOFOLLOW`, `O_CREAT`, and appropriate exclusive-creation controls. 6. Write updates to a securely created temporary file in the same private directory, flush and optionally `fsync()` it, then use `os.replace()` for atomic replacement. 7. Treat cached rows as untrusted input and validate their types, permitted source categories, method identifiers, and `hiddenState` values. 8. If cross-process caching is not operationally necessary, remove the disk cache and retain only the in-process cache. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate_image.py:434
Finding
Unvalidated Server-Provided Media URL Enables Client-Side SSRF and Resource Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_image.py:434-458` **Vulnerability Type**: Server-side request forgery in the client environment and unbounded download **Risk Level**: High ### Vulnerable Code ```python def download_image(url, output_path=None): """ Download image from URL. Args: url: Image URL output_path: Optional path to save the image Returns: bytes: Image data, or None if failed """ try: response = requests.get(url, timeout=60) response.raise_for_status() image_data = response.content # Save to file if path provided if output_path: Path(output_path).parent.mkdir(parents=True, exist_ok=True) with open(output_path, 'wb') as f: f.write(image_data) _progress(f"图片已保存:{output_path}") return image_data except Exception as e: print(f"下载图片失败:{e}", file=sys.stderr) return None ``` The function is reached with the URL supplied by the generation service: ```python if download and result.get("url"): if not output_dir: output_dir = os.path.join(os.path.expanduser("~"), ".openclaw", "workspace", "images") safe_prompt = "".join(c if c.isalnum() or c in (' ', '-', '_') else '_' for c in prompt) safe_prompt = safe_prompt[:50].strip().replace(' ', '_') filename = f"{safe_prompt}_{int(time.time())}.png" output_path = os.path.join(output_dir, filename) image_data = download_image(result["url"], output_path) ``` ### Technical Analysis When `--download` is enabled, the Skill fetches the result URL returned by the external AI API. The URL is passed directly to `requests.get()` without validating: - The scheme. - The destination hostname. - The resolved IP address. - Redirect destinations. - Whether the destination is loopback, link-local, private, reserved, or otherwise internal. - The response `Conte ...[truncated 2676 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require `https` and reject unsupported URL schemes, embedded credentials, malformed hosts, and explicit nonstandard ports unless operationally required. 2. Maintain an allowlist of expected generation-media domains controlled by the declared provider. 3. Resolve the hostname before connecting and reject loopback, private, link-local, multicast, unspecified, reserved, and other non-public IP ranges for both IPv4 and IPv6. 4. Revalidate every redirect target. Prefer disabling automatic redirects and handling a small, fixed number manually. 5. Protect against DNS rebinding by ensuring the validated address is the address actually used for the connection. 6. Use `stream=True` and enforce a conservative maximum download size using both `Content-Length` and incremental byte counting. 7. Abort downloads that exceed the limit before buffering or writing the complete body. 8. Validate the declared `Content-Type` against an image allowlist and verify the downloaded file signature using an image parser. 9. Avoid simultaneously retaining raw bytes, a disk copy, and a Base64 copy unless the caller explicitly requests each representation. 10. Apply filesystem quota checks and clean up partial output files after failed validation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:563
Finding
Credential Verification Instructions Print the Complete API Token<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:563-572` **Vulnerability Type**: Plaintext secret exposure through terminal and log output **Risk Level**: Medium ### Vulnerable Code ```bash # Linux/macOS/Git Bash echo $AI_ARTIST_TOKEN # Windows PowerShell echo $env:AI_ARTIST_TOKEN # Windows CMD echo %AI_ARTIST_TOKEN% ``` ### Technical Analysis The Skill documentation recommends verifying configuration by printing the full `AI_ARTIST_TOKEN`. This unnecessarily discloses a bearer-style API credential to standard output. Terminal output may be captured by: - CI or automation logs. - Agent command transcripts. - Shell session recording. - Screen sharing. - Remote support software. - Terminal scrollback. - Copy-and-paste history. The stated verification goal only requires checking whether the variable is present and plausibly formatted. Revealing the complete token exceeds the minimum access and disclosure necessary for that purpose. The script itself sends the token to the declared API in the `X-Api-Key` header, which is required functionality. The vulnerability is specifically the documentation's recommendation to expose the complete token during local verification. ### Attack Path 1. A user follows the documented API-key setup procedure. 2. The user runs the recommended `echo` command in a terminal, CI job, Agent shell, or recorded support session. 3. The full token is stored in output logs, terminal history infrastructure, or an orchestration transcript. 4. Another user, administrator, support participant, or attacker with access to those records retrieves the token. 5. The attacker submits authenticated requests to the AI Artist API using the victim's credential. 6. The attacker may consume the victim's generation balance and access any API capabilities granted to that token. ### Impact Assessment Exposure of the token can permit actions authorized by the affected API key, including: - Unauthorized image or video generation. - Cons ...[truncated 285 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove all instructions that print the full environment variable. 2. Replace them with a presence-only check that never reveals the value. 3. If format confirmation is useful, display only a masked suffix, such as `configured: sk-****abcd`. 4. Ensure masking handles short and malformed values without accidentally revealing them. 5. Recommend rotating the token immediately if it has been printed into a shared or retained log. 6. Add explicit guidance that credentials must not be posted in chat, command output, screenshots, issue reports, or CI logs. 7. Prefer a dedicated configuration-check command that returns only: - Whether the token is configured. - Whether its prefix or length appears valid. - Whether an authenticated health check succeeded. 8. Ensure authenticated health-check errors do not include request headers or the token. ]]>
Vulnerability Patterns
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (68)

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

Critical
Category
Data Flow
Content
with open(file_path, 'rb') as f:
            files = {'file': (os.path.basename(file_path), f)}
            headers = {'X-Api-Key': API_KEY}
            response = requests.post(FILE_UPLOAD_URL, headers=headers, files=files, timeout=120)
            response.raise_for_status()
            result = response.json()
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

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

Critical
Category
Data Flow
Content
}
            }
        
        response = requests.post(
            FEISHU_WEBHOOK_URL,
            json=content,
            headers={"Content-Type": "application/json"},
Confidence
98% confidence
Finding
The script posts prompt content and result URLs to an arbitrary webhook URL taken directly from the FEISHU_WEBHOOK_URL environment variable. This creates an exfiltration path to any attacker-controlled endpoint if the environment is poisoned or misconfigured, and it occurs automatically whenever the variable is set.

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
查看当前服务端激活的模型请运行:`python3 scripts/generate_image.py --list-models`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
tests/test_generate_image.py:36