T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- src/server.mjs:131
- Finding
- Source resolution permits SSRF through incomplete IP validation and DNS rebinding<![CDATA[ ## Vulnerability Details **File Location**: `src/server.mjs:131-229`, reachable through `src/server.mjs:612-625` **Vulnerability Type**: Server-Side Request Forgery **Risk Level**: High ### Vulnerable Code ```js function isPrivateOrSpecialIp(ip) { if (!ip) return true; if (ip.includes(':')) { const n = ip.toLowerCase(); return n === '::1' || n.startsWith('fc') || n.startsWith('fd') || n.startsWith('fe80:') || n.startsWith('::ffff:127.'); } const p = ip.split('.').map(Number); if (p.length !== 4 || p.some((x) => Number.isNaN(x) || x < 0 || x > 255)) return true; const [a, b] = p; return ( a === 0 || a === 10 || a === 127 || (a === 169 && b === 254) || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168) || a >= 224 ); } async function assertSafeFetchUrl(rawUrl) { const u = new URL(rawUrl); if (!['http:', 'https:'].includes(u.protocol)) throw new Error('invalid url scheme'); const host = u.hostname; if (host === 'localhost' || host.endsWith('.localhost')) throw new Error('blocked host'); if (isIP(host) && isPrivateOrSpecialIp(host)) throw new Error('blocked host'); const resolved = await lookup(host, { all: true }); if (!resolved.length || resolved.some((r) => isPrivateOrSpecialIp(r.address))) { throw new Error('blocked host'); } } async function httpFetch(url, timeout = 5000, redirectsLeft = 3) { await assertSafeFetchUrl(url); return new Promise((resolve, reject) => { const mod = url.startsWith('https') ? https : http; const r = mod.get(url, { headers: { 'User-Agent': 'AI-Digest/1.0', 'Accept': 'text/html,application/xhtml+xml,application/xml,application/json,*/*' } }, async (resp) => { try { if (resp.statusCode >= 300 && resp.statusCode < 400 && resp.headers.location) { clearTimeout(timer); if (redirectsLeft <= 0) return reject(new Error('too many redirects')); const nextUrl = ...[truncated 3562 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse addresses with a well-tested IP-range library supporting IPv4, IPv6, and IPv4-mapped IPv6 normalization. 2. Reject all loopback, private, link-local, multicast, unspecified, reserved, documentation, carrier-grade NAT, and non-global addresses. 3. Normalize IPv4-mapped IPv6 addresses and apply the complete IPv4 policy to the embedded address. 4. Resolve the hostname once, validate every returned address, and connect directly to a validated address. 5. Preserve the original hostname only for the HTTP `Host` header and TLS SNI validation. 6. Revalidate every redirect target and enforce a small redirect limit. 7. Consider an allowlist of approved feed domains or route source retrieval through a restricted egress proxy. 8. Add tests covering mapped IPv6, mixed public/private DNS answers, DNS rebinding, alternate numeric address forms, and cloud metadata addresses. ]]>
