T09 · Insecure Skill Coding Practices
- Location
scripts/generate.js:196- Finding
Unvalidated Server-Controlled URLs Enable Client-Side SSRF and Unbounded Downloads
- Content
View full analysis
setTimeout(r, POLL_INTERVAL)); const statusRes = await fetch(fullStatusUrl); const statusData = await statusRes.json(); ``` The same trust issue affects result downloads: ```javascript const downloads = [ { url: result.glbUrl, ext: '.glb', label: 'GLB' }, { url: result.vrmUrl, ext: '.vrm', label: 'VRM' }, { url: result.processedImageUrl, ext: '.png', label: 'PNG' }, ]; console.log('📥 Downloading...'); for (const { url, ext, label } of downloads) { if (!url) continue; try { const res = await fetch(url); if (res.ok) { const buffer = Buffer.from(await res.arrayBuffer()); const filePath = join(OUTPUT_DIR, `${baseName}${ext}`); writeFileSync(filePath, buffer); console.log(` ✅ ${label}: ${filePath}`); } } catch (e) { console.log(` ⚠️ ${label}: ${e.message}`); } } ``` ### Technical Analysis The generation API controls `statusUrl`, `glbUrl`, `vrmUrl`, and `processedImageUrl`. The script performs requests to these values without validating: - URL scheme - Destination hostname - Destination port - Resolved IP address - Redirect destinations - Response content type - Response or download size An absolute `statusUrl` is accepted whenever it begins with `http`, including plaintext HTTP and arbitrary external or internal hosts. Result URLs are accepted with no validation at all. Node.js `fetch` also follows redirects by default, so validating only an initial URL would not be sufficie ...[truncated 2277 chars]- Remediation
View remediation
