T09 · Insecure Skill Coding Practices
- Location
scripts/bazaar_bridge.js:76- Finding
Bypassable URL Validation Enables Server-Side Request Forgery
- Content
View full analysis
2048) return { ok: false, error: 'Invalid URL length' }; let parsed; try { parsed = new URL(trimmed); } catch { return { ok: false, error: 'Malformed URL' }; } // Only allow https if (parsed.protocol !== 'https:') return { ok: false, error: 'Only https:// URLs are allowed' }; // Block internal/reserved IPs const hostname = parsed.hostname.toLowerCase(); if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '0.0.0.0' || hostname === '[::1]' || hostname.endsWith('.local') || hostname.endsWith('.internal')) { return { ok: false, error: 'Internal/host-local URLs are not allowed' }; } // Block private IP ranges (10.x.x.x, 172.16-31.x.x, 192.168.x.x) const ipMatch = hostname.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/); if (ipMatch) { const [_, a, b] = ipMatch.map(Number); if (a === 10 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168)) { return { ok: false, error: 'Private IP ranges are not allowed' }; } } // Block link-local/multicast (169.254.x.x, 224-239.x.x.x) if (ipMatch) { const a = Number(ipMatch[1]); if (a === 169 || (a >= 224 && a <= 239)) { return { ok: false, error: 'Link-local and multicast IPs are not allowed' }; } } return { ok: true, url: trimmed }; } ``` The validated value is subsequently passed to `fetch`: ```js res = await fetch(urlVal.url, { signal: AbortSignal.timeout(REQUEST_TIMEOUT) }); ``` Discovery-controlled URLs can also reach the same sink automatically: ```js const result = await fetchEndpoint(best.url, conf ...[truncated 2360 chars]- Remediation
View remediation
