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.
