T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/lib/providers.mjs:23
- Finding
- Unrestricted URL Fetching Enables Server-Side Request Forgery and Potential Data Exfiltration<![CDATA[ ## Vulnerability Details **File Location**: `scripts/lib/providers.mjs:23-43`, `scripts/lib/providers.mjs:147-153`, and `scripts/gen.mjs:218-235` **Vulnerability Type**: Server-Side Request Forgery, unrestricted network access, and unbounded response processing **Risk Level**: High ### Vulnerable Code ```js const isUrl = (s) => /^https?:\/\//i.test(s) const MIME = { '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.png': 'image/png', '.webp': 'image/webp', '.gif': 'image/gif', } const mimeOf = (p) => MIME[path.extname(p).toLowerCase()] || 'image/jpeg' async function asBase64(p) { if (isUrl(p)) { const r = await fetch(p) if (!r.ok) throw new Error(`拉取参考图失败 ${r.status}: ${p}`) return Buffer.from(await r.arrayBuffer()).toString('base64') } return (await readFile(p)).toString('base64') } const asDataUri = async (p) => isUrl(p) ? p : `data:${mimeOf(p)};base64,${await asBase64(p)}` ``` The OpenAI provider independently performs the same unrestricted fetch: ```js for (const p of req.images) { const buf = isUrl(p) ? Buffer.from(await (await fetch(p)).arrayBuffer()) : await readFile(p) fd.append('image[]', new Blob([buf], { type: mimeOf(p) }), path.basename(p)) } ``` Provider-returned asset URLs are also fetched without validation: ```js async function persist(files, savePath, req) { if (!files?.length) return [] const out = [] for (const [i, f] of files.entries()) { let target if (savePath) { const ext = path.extname(savePath) || f.ext || '.jpg' const base = savePath.slice(0, savePath.length - path.extname(savePath).length) target = files.length > 1 ? `${base}-${i + 1}${ext}` : `${base}${ext}` } else { target = path.join('output', `${Date.now()}-${i + 1}${f.ext || '.jpg'}`) } await mkdir(path.dirname(target), { recursive: true }) const buf = f.buffer || Buffer.from(await (await fetch(f.url)).arrayBuffer()) await writeFile(target, buf) out.push(target) } ...[truncated 2652 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Permit HTTPS URLs only unless HTTP access is explicitly required. 2. Resolve the hostname before connecting and reject destinations in: - Loopback ranges - RFC 1918 private ranges - Link-local ranges - Carrier-grade NAT ranges - Multicast and reserved ranges - IPv6 loopback, unique-local, and link-local ranges - Known cloud metadata addresses 3. Repeat destination validation after every redirect to prevent redirect-based bypasses. 4. Prefer an allowlist of trusted image-hosting domains where operationally possible. 5. Require a valid image `Content-Type` and verify the downloaded file signature rather than relying on the URL extension. 6. Enforce strict response-size and download-time limits. Stream responses and abort once the configured maximum is exceeded. 7. Apply the same validation to provider-returned asset URLs in `persist()`. 8. Where possible, configure providers to return image bytes directly rather than arbitrary download URLs. 9. Clearly distinguish local paths from remote URLs and require explicit user consent before fetching remote inputs. ]]>
