Back to skill

Security audit

phoenixclaw image gen

Security checks for vulnerabilities and agentic risk

Overview

The skill largely does what it claims, but its image-to-image option can upload any readable local file path to OpenRouter if an agent or user selects it.

Review before installing if agents may invoke skills automatically. Only pass non-sensitive image files to --input-image, avoid secrets or private documents in prompts or image paths, and consider adding file type/size checks plus explicit upload confirmation before using it in shared or sensitive workspaces.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate.js:321
Finding
Arbitrary Local File Disclosure Through Unrestricted Input Image Path## Vulnerability Details **File Location**: `scripts/generate.js:321-325`, with the network transmission sink in `scripts/adapters/openrouter.js:62-65` and `scripts/adapters/openrouter.js:262-270` **Vulnerability Type**: Unrestricted local file read and external transmission **Risk Level**: Medium ### Vulnerable Code `scripts/generate.js:321-325` reads the user-supplied path without validating its location, type, size, or resolved target: ```javascript if (inputImagePath) { try { const imageBuffer = fs.readFileSync(inputImagePath); inputImageBase64 = imageBuffer.toString('base64'); ``` `scripts/generate.js:515-522` passes the CLI-controlled `--input-image` value into that operation: ```javascript const result = await generateImageWithRetry( apiKey, argMap['i2i-model'], argMap.model, argMap.prompt, argMap.size, argMap.aspect, argMap['input-image'] ); ``` `scripts/adapters/openrouter.js:62-65` embeds the resulting bytes in an outbound request while unconditionally labeling them as PNG: ```javascript content: inputImageBase64 ? [ { type: 'text', text: prompt }, { type: 'image_url', image_url: { url: `data:image/png;base64,${inputImageBase64}` } } ] : prompt ``` `scripts/adapters/openrouter.js:262-270` transmits the payload to OpenRouter: ```javascript const response = await fetch(OPENROUTER_API_URL, { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', 'HTTP-Referer': 'https://github.com/ohmyopencodelabs/ai-workers', 'X-Title': 'AI Workers Image Generation' }, body: JSON.stringify(payload) }); ``` ### Technical Analysis The `--input-image` argument is treated as a trusted local path. The implementation calls `fs.readFileSync()` directly and does not verify that: - The resolved path is inside an approved workspace ...[truncated 2032 chars]
Remediation
## Remediation Suggestions 1. **Restrict input files to an approved root** - Resolve the configured upload or workspace root with `fs.realpathSync()`. - Resolve the requested file in the same manner. - Use `path.relative()` to verify that the final target remains beneath the approved root. - Reject absolute paths unless explicitly permitted by policy. 2. **Reject symlinks and non-regular files** - Use `fs.lstatSync()` before reading. - Reject symbolic links, device files, FIFOs, sockets, and directories. - Revalidate the resolved file immediately before opening it to reduce time-of-check/time-of-use risk. 3. **Validate the actual image format** - Inspect file signatures rather than trusting the extension. - Allowlist only required formats, such as PNG, JPEG, and WebP. - Set the transmitted data URL MIME type from the verified format instead of always using `image/png`. 4. **Enforce resource limits** - Check file size before reading and reject files above a conservative configured maximum. - Prefer bounded asynchronous reads over unbounded synchronous loading. - Apply request-size and timeout limits. 5. **Require informed authorization** - Clearly document that prompts and source images are transmitted to OpenRouter. - Require explicit user approval before uploading a local file, especially when an agent selected the path. - Include the normalized path and destination in the confirmation prompt. 6. **Apply least-privilege runtime controls** - Run the Skill with access only to the required workspace and upload directories. - Avoid exposing unrelated credential directories or host files to the process.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (7)

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill advertises use of environment variables and outbound network access to OpenRouter, but the manifest does not declare an explicit tool/permission scope such as allowed-tools or permissions. In an agent ecosystem, missing scope declarations reduce transparency and weaken least-privilege controls, making it easier for a skill to access sensitive capabilities without clear user review.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The description and operating instructions are presented entirely in Chinese, which effectively forces a specific language for users reading the skill documentation. There is no indication that this is a region-specific skill or that alternative language support is available by user choice.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The documentation states that prompts and images are sent to OpenRouter, but it does not clearly warn users that potentially sensitive text, images, or metadata will leave the local environment and be processed by a third party. This can lead to unintended disclosure of confidential data, especially in an image-generation skill where users may submit proprietary artwork, personal photos, or internal prompts.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The CLI contract documents parameters for prompts and input images but does not warn that these inputs may be transmitted to OpenRouter and downstream third-party model providers. In a skill specifically designed for external image generation, that omission can mislead users into submitting sensitive text or images under the false assumption that processing is local, creating a real privacy and data-handling risk.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
When --input-image is provided, the script reads the local image, base64-encodes it, and sends it to the external OpenRouter service with no explicit user-facing confirmation or warning in that execution path. In a CLI skill that may be invoked by another agent or automation, this can cause unintended exfiltration of sensitive local images if the caller or end user does not realize that local file contents are being uploaded off-host.

Natural-Language Policy Violations

Low
Confidence
83% confidence
Finding
该文档在自然语言中明确声明 v1 仅支持 OpenRouter,属于对工具/服务选择的强约束,但未说明这是用户可选项,也未给出区域合规或组织政策上的必要理由。根据规则,强制特定语言/locale 之外的自然语言组织政策约束中,类似“无用户 opt-in 的强制约束”可构成政策问题。

Intent-Code Divergence

Low
Confidence
98% confidence
Finding
The function documentation says it "Tests OpenRouter connectivity without generating an image," which implies some validation of reachability or service access. In implementation, it only reads and validates the API key from input/environment and returns metadata without contacting OpenRouter at all, so the comment overstates what the code does.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access, suspicious.exposed_secret_literal

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/cli/openrouter.js:121

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/adapters/openrouter.js:11

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/cli/openrouter.js:23

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
SKILL.md:64