{"record":{"id":"db9d4a0cb063076c","repo":"decolua/9router","slug":"blocked-url-private-ip","errorCode":null,"errorMessage":"Blocked URL: private IP","messagePattern":"Blocked URL: private IP","errorType":"validation","errorClass":"Error","httpStatus":400,"severity":"error","filePath":"src/shared/utils/ssrfGuard.js","lineNumber":54,"sourceCode":"  });\n}\n\nfunction isBlockedIpv6(host) {\n  const h = host.replace(/^\\[|\\]$/g, \"\").toLowerCase();\n  const v4Mapped = h.match(/^::ffff:(\\d+\\.\\d+\\.\\d+\\.\\d+)$/);\n  if (v4Mapped) return isBlockedIpv4(v4Mapped[1]);\n  if (h === \"::1\" || h === \"::\") return true;\n  return h.startsWith(\"fe80:\") || h.startsWith(\"fc\") || h.startsWith(\"fd\");\n}\n\n// Throw if URL targets a non-public host. Caller should map to 400.\nexport function assertPublicUrl(rawUrl) {\n  const parsed = new URL(rawUrl);\n  const host = parsed.hostname.toLowerCase();\n\n  if (BLOCKED_HOSTNAMES.has(host)) throw new Error(\"Blocked URL: internal host\");\n  if (BLOCKED_SUFFIXES.some((s) => host.endsWith(s))) throw new Error(\"Blocked URL: internal host\");\n  if (isBlockedIpv4(host)) throw new Error(\"Blocked URL: private IP\");\n  if (host.includes(\":\") && isBlockedIpv6(host)) throw new Error(\"Blocked URL: private IP\");\n}\n","sourceCodeStart":36,"sourceCodeEnd":57,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/shared/utils/ssrfGuard.js#L36-L57","documentation":"assertPublicUrl line 54 fires when the hostname is a dotted IPv4 literal falling inside a private/reserved range (0.0.0.0/8, 10/8, 127/8, 169.254/16, 172.16/12, 192.168/16). The message is 'Blocked URL: private IP' — distinct from the hostname-based 'internal host' errors — signalling the target is a private network address rather than a public one.","triggerScenarios":"Calling resolveBaseUrl / POST / handleFetch with a raw IPv4 literal in a reserved range, e.g. `http://10.0.0.5`, `http://192.168.1.1`, `http://172.16.0.1`, `http://127.0.0.1`, or a link-local `169.254.x.x` cloud-metadata address.","commonSituations":"Pointing the proxy at a home/office LAN device; testing against a local dev server bound to 127.0.0.1; accidentally using the cloud metadata IP 169.254.169.254 (a classic SSRF payload); Docker-internal 172.x addresses.","solutions":["Use the target service's public IP or public hostname","Expose the private service through a public reverse proxy/tunnel and use that URL","If this is a metadata-style probe in testing, remove it — the guard intentionally blocks it"],"exampleFix":"// before\nawait fetchViaProxy(\"http://192.168.1.10:3000/api\");\n// throws: Blocked URL: private IP\n\n// after\nawait fetchViaProxy(\"https://my-service.example.com/api\");","handlingStrategy":"validation","validationCode":"function isPrivateIpv4(host) {\n  const m = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(host);\n  if (!m) return false;\n  const [a, b] = m.slice(1).map(Number);\n  return a === 0 || a === 10 || a === 127 || (a === 169 && b === 254) || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168);\n}\nconst host = new URL(rawUrl).hostname;\nif (isPrivateIpv4(host)) console.warn(\"Blocked: private IPv4 target\", host);","typeGuard":"function isPublicIpv4Url(v) {\n  if (typeof v !== \"string\") return false;\n  try {\n    const host = new URL(v).hostname;\n    return /^\\d{1,3}(\\.\\d{1,3}){3}$/.test(host) ? !isPrivateIpv4(host) : true;\n  } catch { return false; }\n}","tryCatchPattern":"try {\n  await proxyFetch(url);\n} catch (e) {\n  if (e.message === \"Blocked URL: private IP\") {\n    throw new HttpError(400, \"Target resolves to a private/reserved IPv4 address\");\n  }\n  throw e;\n}","preventionTips":["Never target 169.254.169.254 or other metadata IPs through the proxy — this is exactly what the guard blocks","Use public hostnames instead of LAN IPs in any URL passed to server-side fetch endpoints","For Docker/LAN dev services, expose them via a tunnel with a public URL if proxying is required","Validate the target IP range client-side before submitting"],"tags":["ssrf","security","ipv4","network"],"backgroundTag":"ssrf-blocked-private-ip","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}