T09 · Insecure Skill Coding Practices
- Location
scripts/image-generator.js:98- Finding
Unrestricted Image Sources Permit SSRF and Arbitrary Local File Disclosure to OpenAI
- Content
View full analysis
{ client.get(source, (response) => { const chunks = []; response.on('data', chunk => chunks.push(chunk)); response.on('end', () => { const buffer = Buffer.concat(chunks); resolve(buffer.toString('base64')); }); response.on('error', reject); }).on('error', reject); }); } ``` The resulting content is included in a request to OpenAI: ```javascript const base64 = await toBase64(imagePath); const mimeType = imagePath.toLowerCase().endsWith('.png') ? 'image/png' : 'image/jpeg'; const userContent = [ { type: 'image_url', image_url: { url: `data:${mimeType};base64,${base64}` } }, { type: 'text', text: `Edit this image: ${prompt}` } ]; if (maskPath) { const maskBase64 = await toBase64(maskPath); const maskMime = maskPath.toLowerCase().endsWith('.png') ? 'image/png' : 'image/jpeg'; userContent.unshift({ type: 'image_url', image_url: { url: `data:${maskMime};base64,${maskBase64}` } }); } const response = await client.chat.completions.create({ model: 'gpt-4o', messages: [{ role: 'user', content: userContent }] }); ``` ### Technical Analysis Both `imagePath` and `maskPath` reach `toBase64()` without a path boundary, URL allowlist, protocol restriction, address-range check, content-type check, or size limit. If the supplied string names any file readable by the Node.js process, `fs.readFileSync()` reads the entire file. Its contents are base64 ...[truncated 2263 chars]- Remediation
View remediation
