Back to skill

Security audit

青萍 AI 平台

Security checks for vulnerabilities and agentic risk

Overview

This image-generation skill is mostly purpose-aligned, but it needs Review because the remote API can control downloaded URLs and filenames, which may write outside the advertised folder.

Review before installing. Use a limited/rotatable Qingping API key and avoid committing shell profile files containing it. Be aware that prompts are sent to Qingping's external API and that the current downloader trusts the service's returned URLs and filenames; run it only in a workspace where unintended .png writes or large downloads would not damage important 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

Warning
Location
scripts/generate_image.py:284
Finding
Path Traversal Through Server-Controlled Image Filename## Vulnerability Details **File Location**: `scripts/generate_image.py`, lines 218–229 and 284–290 **Vulnerability Type**: Path traversal and arbitrary file placement **Risk Level**: Medium ### Vulnerable Code ```python def download_image(url: str, filename: str, output_dir: Path) -> Path: """Download an image to the specified directory.""" output_path = output_dir / filename print(f"\n📥 Downloading image...") print(f" URL: {url}") print(f" Saving to: {output_path}") try: output_dir.mkdir(parents=True, exist_ok=True) request.urlretrieve(url, output_path) ``` The filename passed to this function is derived directly from a remote API response: ```python for img_data in generated_urls: url = img_data.get("url") name = img_data.get("name") if not url or not name: print(f"⚠️ Skipping invalid data: {img_data}") continue filename = f"{name}.png" path = download_image(url, filename, output_dir) ``` ### Technical Analysis The `name` field is controlled by the remote image-generation service and is concatenated with `.png` without validation. The resulting value is joined to `output_dir` using: ```python output_path = output_dir / filename ``` Python's `pathlib` does not automatically restrict the resulting path to the intended directory. A name containing parent-directory components, such as `../../target`, can resolve outside `qingping-ai/`. An absolute name can also cause the original output directory to be discarded. The `.png` suffix limits the destination to a filename ending in `.png`, but it does not prevent overwriting existing image files or placing attacker-controlled content at arbitrary writable paths with that suffix. This behavior exceeds the minimum filesystem privileges required by the Skill. Generated files only need to be written beneath the documented `qingping-ai/` directory. ### Attack ...[truncated 1206 chars]
Remediation
## Remediation Suggestions Treat every filename returned by the API as untrusted. 1. Prefer generating filenames locally with a UUID or other cryptographically random identifier. 2. If the remote name must be retained, reject absolute paths, parent-directory components, path separators, null bytes, and platform-specific reserved names. 3. Resolve both the output directory and destination path and verify containment before writing. 4. Open new files using exclusive creation where overwriting is not required. 5. Apply a conservative filename-length limit. Example hardening: ```python import re import uuid output_root = output_dir.resolve() safe_name = re.sub(r"[^A-Za-z0-9._-]", "_", Path(name).name) safe_name = safe_name[:100] or uuid.uuid4().hex output_path = (output_root / f"{safe_name}.png").resolve() if output_root not in output_path.parents: raise ValueError("Invalid output filename") ``` For stronger isolation, ignore the remote name entirely: ```python output_path = output_dir.resolve() / f"{uuid.uuid4().hex}.png" ```

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate_image.py:283
Finding
Unrestricted Download of API-Controlled URLs## Vulnerability Details **File Location**: `scripts/generate_image.py`, lines 218–229 and 283–290 **Vulnerability Type**: Client-side SSRF and unbounded remote file download **Risk Level**: Medium ### Vulnerable Code ```python def download_image(url: str, filename: str, output_dir: Path) -> Path: """Download an image to the specified directory.""" output_path = output_dir / filename print(f"\n📥 Downloading image...") print(f" URL: {url}") print(f" Saving to: {output_path}") try: output_dir.mkdir(parents=True, exist_ok=True) request.urlretrieve(url, output_path) print(f"✅ Download complete!") return output_path ``` The URL originates from the remote API: ```python for img_data in generated_urls: url = img_data.get("url") name = img_data.get("name") if not url or not name: print(f"⚠️ Skipping invalid data: {img_data}") continue filename = f"{name}.png" path = download_image(url, filename, output_dir) downloaded_paths.append(path) ``` ### Technical Analysis The script automatically retrieves any URL supplied in the API's `generated_image_urls` response. It does not validate: - The URL scheme. - The destination hostname. - Whether the hostname resolves to a loopback, private, link-local, or otherwise restricted address. - Redirect destinations. - The response `Content-Type`. - The response size. - Whether the downloaded content is a valid image. Consequently, a malicious or compromised API could direct the client to internal network services or attacker-selected resources. Because `urlretrieve()` writes the response without an explicit maximum size, the same behavior can also be used to consume disk space. The `.png` extension does not establish that the retrieved content is an image. Downloading the generated image is necessary for the declared functionality, but accepting ...[truncated 1569 chars]
Remediation
## Remediation Suggestions 1. Require the `https` scheme. 2. Allowlist the documented image CDN hostname or a narrowly defined set of trusted CDN hosts. 3. Resolve the hostname and reject loopback, private, link-local, multicast, reserved, and unspecified IP addresses. 4. Validate every redirect target using the same policy, or disable automatic redirects and process them manually. 5. Stream the response in bounded chunks rather than using `urlretrieve()`. 6. Enforce a maximum download size appropriate to the selected image resolution. 7. Require an expected image `Content-Type` and verify the downloaded file's actual format. 8. Use connection and read timeouts and delete partial files after failures. 9. Write to a temporary file within the output directory, validate it, and atomically rename it only after successful verification. A secure implementation should validate the parsed URL before opening the connection, validate the connected address to reduce DNS-rebinding risk, and stop reading immediately when the configured byte limit is exceeded.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (4)

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The README instructs users to place the API key in shell startup files like `~/.zshrc` without warning that these files may be broadly readable to the user’s tooling, backups, shell history workflows, or accidentally committed/shared. Persisting long-lived secrets in profile files increases the chance of credential exposure and reuse across sessions, especially in developer environments where dotfiles are synced or published.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The skill description includes very broad trigger phrases such as generic requests to '生成图片' and 'AI生图', which can cause the agent to invoke this third-party skill for many ordinary image-generation requests unrelated to a narrowly scoped Qingping integration. That overbroad routing increases the chance of unnecessary external API calls, unintended disclosure of user prompts to the service, and surprising behavior when a safer or more appropriate built-in tool should have been used.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The file-level docstring and subsequent CLI messages are written entirely in Chinese, and the script does not provide any opt-in or fallback for other languages. The policy explicitly calls out forced language or locale behavior as a natural-language policy violation unless the constraint is optional or clearly justified.

Missing User Warnings

Low
Confidence
91% confidence
Finding
The markdown states that images are 'automatically downloaded to local' and saved into a local directory, which is a file-writing behavior affecting user storage. While the behavior is described, there is no explicit warning or cautionary note about automatic disk writes, overwrite/storage implications, or how to control the save location.

Static analysis

No suspicious patterns detected.