T09 · Insecure Skill Coding Practices
Warning
- Location
- core.mjs:242
- Finding
- Unrestricted Local File Read Exposed Through the MCP Image-Editing Tool## Vulnerability Details **File Location**: `core.mjs:242-273`; reachable through `mcp-server.mjs:68-76` and `mcp-server.mjs:136-138` **Vulnerability Type**: Unrestricted filesystem access and resource-exhaustion risk **Risk Level**: Medium ### Vulnerable Code `core.mjs:242-273`: ```javascript if (!prompt) throw new Error('prompt is required'); if (!image) throw new Error('image is required (URL or base64 data URI)'); const images = Array.isArray(image) ? image : [image]; if (images.length > 3) throw new Error('Maximum 3 source images'); // Build input with image(s) + text prompt const content = []; for (const img of images) { // If it looks like a file path, read and base64 encode let imageUrl = img; if (!img.startsWith('http') && !img.startsWith('data:')) { const data = readFileSync(img); const ext = img.split('.').pop().toLowerCase(); const mime = ext === 'png' ? 'image/png' : ext === 'webp' ? 'image/webp' : 'image/jpeg'; imageUrl = `data:${mime};base64,${data.toString('base64')}`; } content.push({ type: 'image_url', image_url: { url: imageUrl } }); } content.push({ type: 'text', text: prompt }); const response = await fetch(`${API_BASE}/images/edits`, { method: 'POST', headers: headers(), body: JSON.stringify({ model, image: images[0], prompt, n, response_format, }), }); ``` `mcp-server.mjs:68-76`: ```javascript { name: 'grok_edit_image', description: 'Edit images using natural language with Grok Imagine. Provide source image URL and edit instruction.', inputSchema: { type: 'object', properties: { prompt: { type: 'string', description: 'Edit instruction' }, image: { type: 'string', description: 'Source image URL or base64 data URI' }, }, required: ['prompt', 'image'], }, }, ``` `mcp-server.mjs:136-138`: ```javascript case 'grok_edit_image': result = ...[truncated 3204 chars]
- Remediation
- ## Remediation Suggestions 1. Enforce the documented MCP contract by accepting only explicitly validated HTTPS URLs and approved image data URIs: - Parse URLs with the platform URL parser. - Require the `https:` protocol. - Validate data-URI media types against an image allowlist. - Reject all other strings instead of treating them as paths. 2. If local-file editing is intentionally supported, expose it as an explicit, separately documented capability: - Require user confirmation before reading a local file. - Resolve the supplied path with `realpath`. - Restrict resolved paths to a dedicated user-approved media directory. - Reject path traversal, symbolic-link escapes, devices, sockets, FIFOs, and other non-regular files. - Verify file signatures rather than trusting filename extensions. - Enforce strict byte-size limits before reading. - Use asynchronous file operations to avoid blocking the MCP event loop. 3. Correct the outbound request construction. Send the validated or encoded image value rather than the original path: ```javascript body: JSON.stringify({ model, image: validatedImageUrl, prompt, n, response_format, }); ``` 4. Remove the unused `content` construction if the xAI endpoint does not require it. Avoid reading any file unless its bytes will be used for the requested and authorized operation. 5. Add tests covering arbitrary absolute paths, traversal paths, symbolic links, oversized files, special files, malformed data URIs, unsupported URL schemes, and MCP schema bypass attempts.
