Back to skill

Security audit

Qwen Image Gen

Security checks for vulnerabilities and agentic risk

Overview

This image-generation skill is mostly purpose-aligned, but it handles API credentials and downloads in ways that are too broadly scoped for automatic trust.

Install only if you trust the publisher and will keep the base URL pointed at the official DashScope endpoint you intend to use. Prefer DASHSCOPE_API_KEY from the environment instead of config.json, do not allow untrusted projects or agents to edit config.json or DASHSCOPE_BASE_URL, and review generated output files before reusing them.

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/qwen-image-gen.js:691
Finding
DashScope API credentials can be forwarded to an arbitrary configured endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/qwen-image-gen.js:691-692`, with credential-bearing network sinks at `scripts/qwen-image-gen.js:508-514`, `526-531`, and `535-541` **Vulnerability Type**: Unvalidated authenticated endpoint configuration **Risk Level**: High ### Vulnerable Code ```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(/\/$/, ''); ``` The API key is subsequently attached to requests sent to the selected base URL: ```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) }); const taskId = result && result.output && result.output.task_id; if (!taskId) { throw new Error(`创建任务失败,响应中缺少 task_id: ${JSON.stringify(result)}`); } return result; } async function fetchTask(baseUrl, apiKey, taskId) { return requestJson(`${baseUrl}/api/v1/tasks/${encodeURIComponent(taskId)}`, { method: 'GET', headers: { Authorization: `Bearer ${apiKey}` } }); } async function callSyncGeneration(baseUrl, apiKey, request) { return requestJson(`${baseUrl}${request.endpoint}`, { method: 'POST', headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify(request.body) }); } ``` ### Technical Analysis The script allows `DASHSCOPE_BASE_URL` or `config.baseUrl` to replace the default Alibaba Cloud endpoint. The value is converted to a string and has a trailing slash removed, but it is not otherwise validated. There is no enforcement of: - The HTTPS protocol - An official DashScope hostname - An approved destination port - The absenc ...[truncated 2416 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse the configured endpoint using `new URL()` and reject malformed URLs. 2. Require `url.protocol === "https:"`. 3. Allowlist the exact official DashScope hostnames required for supported regions. Avoid substring-based hostname checks. 4. Reject embedded usernames, passwords, fragments, and unexpected ports. 5. Do not forward the DashScope API key to custom endpoints. If custom gateways are required, require explicit opt-in and use a separate credential. 6. Disable automatic redirects for authenticated API requests where supported, or manually validate every redirect destination before resending the `Authorization` header. 7. Prefer `DASHSCOPE_API_KEY` or a secret manager over `config.json`. 8. If file-based key storage remains supported, document restrictive file permissions and ensure `config.json` is excluded from source control. 9. Add automated tests confirming that HTTP URLs, unapproved domains, deceptive subdomains, embedded credentials, and unexpected ports are rejected. A safe validation pattern should use exact hostname comparisons: ```js const ALLOWED_HOSTS = new Set([ 'dashscope.aliyuncs.com', 'dashscope-intl.aliyuncs.com' ]); function validateBaseUrl(value) { const url = new URL(value); if (url.protocol !== 'https:') { throw new Error('DashScope base URL must use HTTPS'); } if (!ALLOWED_HOSTS.has(url.hostname)) { throw new Error(`Unapproved DashScope hostname: ${url.hostname}`); } if (url.username || url.password || url.port || url.hash) { throw new Error('DashScope base URL contains prohibited components'); } return url.origin; } ``` The allowlist must be verified against current official Alibaba Cloud documentation before deployment. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/qwen-image-gen.js:545
Finding
Untrusted API response URLs are fetched without destination or response validation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/qwen-image-gen.js:545-566` and `589-596` **Vulnerability Type**: Server-side request forgery and unbounded download **Risk Level**: Medium ### Vulnerable Code Image locations are accepted directly from API response fields: ```js function extractImageUrls(payload) { const urls = []; const output = payload && payload.output ? payload.output : {}; if (Array.isArray(output.results)) { for (const item of output.results) { if (item && item.url) urls.push(item.url); } } if (Array.isArray(output.choices)) { for (const choice of output.choices) { const content = choice && choice.message && Array.isArray(choice.message.content) ? choice.message.content : []; for (const item of content) { if (item && item.image) urls.push(item.image); } } } return [...new Set(urls)]; } ``` The extracted locations are fetched and loaded entirely into memory without validation: ```js async function downloadFile(url, filePath) { const response = await fetch(url); if (!response.ok) { throw new Error(`下载失败: ${response.status} ${response.statusText}`); } const arrayBuffer = await response.arrayBuffer(); fs.writeFileSync(filePath, Buffer.from(arrayBuffer)); } ``` ### Technical Analysis The image downloader trusts URLs returned in `output.results[].url` and `output.choices[].message.content[].image`. It does not verify: - The URL scheme - The destination hostname - Whether the resolved address is loopback, private, link-local, or otherwise internal - Redirect destinations - Response size - Request duration - Response `Content-Type` - PNG file signatures or other image magic bytes Consequently, a compromised API service or an attacker-controlled service selected through the configurable base URL can direct the process to make requests from the Agent's network context. This can reach services unavailable to an external attacker, including localhost, ...[truncated 2245 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse every returned location with `new URL()` and permit only `https:`. 2. Restrict downloads to exact, documented Alibaba Cloud or DashScope result-storage hostnames. 3. Resolve destination hostnames and reject loopback, unspecified, private, link-local, multicast, and reserved IP ranges for both IPv4 and IPv6. 4. Revalidate the hostname and resolved address after every redirect, or disable redirects and process them manually. 5. Add an `AbortController` timeout for connection and total download duration. 6. Stream responses to disk instead of buffering the complete body in memory. 7. Enforce a maximum download size before and during streaming. 8. Require an expected image `Content-Type`, such as `image/png`, while treating the header as only one validation layer. 9. Validate PNG magic bytes before retaining the file. 10. Delete partially written files when validation or downloading fails. 11. Use exclusive file creation where appropriate to avoid unintentionally overwriting existing files. 12. Add tests covering localhost, private IPv4 ranges, IPv6 loopback, DNS rebinding scenarios, redirects to internal addresses, oversized files, incorrect content types, and invalid image data. If official result URLs use expiring signed storage domains, maintain a narrowly scoped allowlist based on official documentation rather than accepting arbitrary external hosts. ]]>
Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • 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 (9)

Ae1

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

Ae1

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

Ae1

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

Ae1

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

Ae1

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

Ae1

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

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill requires access to an API key and makes outbound network requests, but the manifest does not declare an explicit tool scope such as permissions or allowed-tools. This creates an overbroad trust boundary: a host agent may invoke the skill without clear policy gating on secret access and networking, increasing the chance of unintended credential exposure or unreviewed external communication.

Natural-Language Policy Violations

Low
Confidence
91% confidence
Finding
This markdown file presents all substantive guidance in Chinese, including headings, usage notes, and pricing, with no indication that the language choice is optional or that the document 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 natural-language policy violation.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The script emits user-facing messages in Chinese, for example the error text at L192, and this pattern continues throughout usage, validation, and status output. Because there is no opt-in, locale switch, or documented region-specific justification in the file, this is a natural-language locale policy concern.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/qwen-image-gen.js:313