Back to skill

Security audit

wan-image-gen

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent image-generation tool, but it can send the user's DashScope API key to an unrestricted configured endpoint and downloads unvalidated remote files.

Install only if you trust the skill source and will keep baseUrl pointed at the legitimate DashScope endpoint. Prefer DASHSCOPE_API_KEY from the environment instead of config.json, do not let untrusted projects set DASHSCOPE_BASE_URL or config.json, and run it in a network- and disk-limited workspace if possible.

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/wan-image-gen.js:581
Finding
API Credential Disclosure Through an Unrestricted Endpoint Override<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wan-image-gen.js:467-490, 581-585` **Vulnerability Type**: Unrestricted credential-bearing network destination **Risk Level**: High ### Vulnerable Code ```js async function createTask(baseUrl, apiKey, request) { const result = await requestJson(`${baseUrl}${request.endpoint}`, { method: 'POST', headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json', 'X-DashScope-Async': 'enable' }, body: JSON.stringify(request.body) }); ``` ```js async function fetchTask(baseUrl, apiKey, taskId) { return requestJson(`${baseUrl}/api/v1/tasks/${encodeURIComponent(taskId)}`, { method: 'GET', headers: { Authorization: `Bearer ${apiKey}` } }); } ``` ```js const apiKey = firstNonEmpty(process.env.DASHSCOPE_API_KEY, config.apiKey); const baseUrl = String( firstNonEmpty(process.env.DASHSCOPE_BASE_URL, config.baseUrl, DEFAULT_BASE_URL) ).replace(/\/$/, ''); ``` ### Technical Analysis Sending the API key and image prompt to the official DashScope service is necessary for the declared image-generation functionality. However, the destination receiving these sensitive values is not restricted to the official service. The script accepts `baseUrl` from either `DASHSCOPE_BASE_URL` or `config.json` and performs no URL parsing, HTTPS enforcement, hostname allowlisting, port validation, or embedded-credential rejection. The same DashScope API key is then unconditionally placed in an `Authorization: Bearer` header for requests to that destination. This crosses the minimum required privilege boundary: endpoint configurability does not require granting every configured destination access to the DashScope credential. A malicious or mistakenly configured HTTP endpoint can receive both the API key and submitted prompt data. Plain HTTP also permits network interception. ### Attack Path 1. An attacker gains the ability to influence the process ...[truncated 1258 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse the configured endpoint using the standard `URL` class and reject malformed URLs. 2. Require `https:` for all credential-bearing requests. 3. Allowlist the documented DashScope hostnames and permitted regional endpoints. 4. Reject embedded usernames or passwords, fragments, unexpected ports, and hostnames that resolve to loopback, private, link-local, or reserved addresses. 5. If custom endpoints are operationally necessary, require an explicit unsafe opt-in rather than enabling them through ordinary configuration. 6. Use a separate credential specifically scoped to each custom endpoint; never forward the DashScope key to an unrelated destination. 7. Revalidate the destination immediately before sending the bearer token to reduce DNS rebinding risk. 8. Document that changing the endpoint changes the party receiving prompts and credentials. 9. Prefer reading the key from the environment or a protected secret provider instead of plaintext `config.json`. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/wan-image-gen.js:501
Finding
Unvalidated Service-Provided Image URL Fetching<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wan-image-gen.js:501-519, 544-562` **Vulnerability Type**: Server-side request forgery and unbounded download **Risk Level**: Medium ### Vulnerable Code ```js if (Array.isArray(output.results)) { output.results.forEach((item) => { if (item && item.url) { urls.push(item.url); } }); } if (Array.isArray(output.choices)) { output.choices.forEach((choice) => { const content = choice && choice.message && Array.isArray(choice.message.content) ? choice.message.content : []; content.forEach((item) => { if (item && item.image) { urls.push(item.image); } }); }); } ``` ```js async function downloadFile(url, filePath) { const response = await fetch(url); if (!response.ok) { throw new Error(`Download failed: ${response.status} ${response.statusText}`); } const arrayBuffer = await response.arrayBuffer(); fs.writeFileSync(filePath, Buffer.from(arrayBuffer)); } ``` ```js for (let i = 0; i < urls.length; i += 1) { const url = urls[i]; const filename = `${prefix}-${String(i + 1).padStart(2, '0')}.png`; const filePath = path.join(outputDir, filename); await downloadFile(url, filePath); saved.push(filePath); } ``` ### Technical Analysis Image URLs extracted from task responses are passed directly to `fetch`. The code does not validate the URL scheme, hostname, port, resolved IP address, or redirect destination. It therefore trusts the remote API response to select arbitrary resources accessible from the machine running the Skill. If the configured API endpoint is compromised, malicious, or redirected, it can return URLs targeting loopback services, private network hosts, or cloud metadata endpoints. Redirects can also move an initially acceptable URL to a prohibited destination unless every redirect is validated. The response is consumed through `arrayBuffer()`, causing the entire body to be buffered in memory before it i ...[truncated 1761 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse every returned image URL with `URL` and permit only `https:`. 2. Allowlist the documented DashScope or approved object-storage download hostnames. 3. Resolve destination hostnames and block loopback, private, link-local, multicast, and reserved addresses for both IPv4 and IPv6. 4. Disable redirects or manually process them, applying the same validation to every redirect destination. 5. Add connection, response-header, idle, and total download timeouts. 6. Stream the response to disk instead of buffering it with `arrayBuffer()`. 7. Enforce a strict maximum download size while streaming, independent of the reported `Content-Length`. 8. Require an expected image media type and validate file signatures before retaining the file. 9. Derive the output extension from an approved, validated image type rather than always using `.png`. 10. Delete partial files when validation, download, or size checks fail. ]]>
Vulnerability Patterns
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (13)

Ae1

High
Category
analysis-evasion
Content
主要脚本:`node scripts/wan-image-gen.js`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
主要脚本:`node scripts/wan-image-gen.js`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
主要脚本:`node scripts/wan-image-gen.js`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
主要脚本:`node scripts/wan-image-gen.js`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
主要脚本:`node scripts/wan-image-gen.js`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
主要脚本:`node scripts/wan-image-gen.js`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill requires environment access to read an API key and performs outbound network calls, but the manifest does not explicitly declare tool scope such as permissions or allowed-tools. This weakens least-privilege enforcement and makes it harder for a host agent or reviewer to understand and constrain what the skill is allowed to access.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
The natural-language instructions and usage guidance are entirely presented in Chinese, and the skill does not indicate that language selection is optional or that the skill is intended only for a Chinese-speaking or region-specific audience. Under the stated policy, forcing a specific language without user opt-in can be a locale-policy issue.

Missing User Warnings

Low
Confidence
80% confidence
Finding
The skill automatically downloads generated images to a local directory, but the top-level description does not clearly warn that execution writes files to disk. This can surprise users or calling agents, especially in environments where local writes are sensitive, audited, or space-limited.

Intent-Code Divergence

Low
Confidence
98% confidence
Finding
L080 声称“默认档位为 final”,而前面的 config 示例在 L039 与 L053 明确给出 defaultGoal=balanced、defaultTier=standard,对应默认模型并非 final。这个问题属于文档内部对实际默认行为的主动矛盾描述,会误导调用方对脚本默认行为的理解。

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
SQP-3 applies to all file types and covers language or locale policy violations. The file content is entirely Chinese and does not indicate user opt-in, multilingual support, or a justified region-specific language constraint, which may conflict with organizational language-choice expectations.

Missing User Warnings

Low
Confidence
89% confidence
Finding
This is a markdown file, so SQP-2 applies to user-affecting behaviors described in the documentation. Line L20 states that images are automatically downloaded locally, but the notes do not present this as a warning or caution even though it affects user data/storage and creates local files.

Natural-Language Policy Violations

Low
Confidence
85% confidence
Finding
The usage examples and many user-facing messages in the script are written only in Chinese, which effectively steers use toward a specific language/locale. The file does not indicate that Chinese is optional or that users may supply prompts and interact in another language.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/wan-image-gen.js:265