T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/render-gotchi-bypass.mjs:250
- Finding
- Unrestricted Renderer-Controlled Artifact URLs Enable Blind SSRF and Resource Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/render-gotchi-bypass.mjs`, lines 250-257, 335-341, and 353-359 **Vulnerability Type**: Server-Side Request Forgery (SSRF) and unbounded resource consumption **Risk Level**: Medium ### Vulnerable Code ```js async function downloadFile(url, filePath) { const response = await fetch(url); if (!response.ok) { throw new Error(`Failed to download ${url} (${response.status})`); } const bytes = Buffer.from(await response.arrayBuffer()); fs.writeFileSync(filePath, bytes); return filePath; } ``` ```js if (proxyUrls.PNG_Full && finalAvailability?.PNG_Full?.exists === true) { const fullUrl = proxyUrls.PNG_Full.startsWith("http") ? proxyUrls.PNG_Full : `${DAPP_BASE}${proxyUrls.PNG_Full}`; artifacts.fullPngPath = path.join(options.outDir, `gotchi-${tokenId}-full.png`); await downloadFile(fullUrl, artifacts.fullPngPath); artifacts.fullPngUrl = fullUrl; } ``` ```js if (proxyUrls.PNG_Headshot && finalAvailability?.PNG_Headshot?.exists === true) { const headshotUrl = proxyUrls.PNG_Headshot.startsWith("http") ? proxyUrls.PNG_Headshot : `${DAPP_BASE}${proxyUrls.PNG_Headshot}`; artifacts.headshotPngPath = path.join(options.outDir, `gotchi-${tokenId}-headshot.png`); await downloadFile(headshotUrl, artifacts.headshotPngPath); artifacts.headshotPngUrl = headshotUrl; } ``` ### Technical Analysis The `proxyUrls.PNG_Full` and `proxyUrls.PNG_Headshot` values originate from the remote renderer API response. When either value begins with `"http"`, the script passes it directly to `fetch()` without validating its protocol, hostname, resolved address, port, credentials, or redirect destination. A compromised or malicious renderer response can therefore instruct the script to send requests to arbitrary HTTP resources reachable from the execution environment. Potential targets include loopback services, private network hosts, link-local services, and cloud metadata endpoints. Red ...[truncated 3032 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse every artifact URL using `new URL()` rather than accepting values based on `startsWith("http")`. 2. Require HTTPS and reject URLs containing embedded credentials, unexpected ports, unsupported schemes, or malformed hostnames. 3. Maintain an explicit allowlist of expected Aavegotchi and renderer CDN hostnames. Treat relative URLs as relative to `DAPP_BASE`, but still validate the resulting URL. 4. Resolve destination hostnames and reject loopback, private, link-local, multicast, reserved, and cloud metadata address ranges for both IPv4 and IPv6. 5. Disable automatic redirects where possible. If redirects are required, validate the scheme, hostname, port, and resolved address at every redirect hop. 6. Add request timeouts with `AbortController`. 7. Check `Content-Type` against expected image media types before saving PNG artifacts. 8. Enforce a conservative maximum response size using `Content-Length` where available and a byte-counting streaming limit regardless of that header. 9. Stream responses to a temporary file rather than buffering the entire response in memory. Atomically rename the file only after validation succeeds, and delete partial files on failure. 10. Apply output-directory quotas or confirm sufficient available storage before downloading. 11. Consider verifying PNG signatures and image structure before treating downloaded files as valid artifacts. ]]>
