{"record":{"id":"0d68f30a14aa785f","repo":"ruvnet/ruflo","slug":"private-address","errorCode":"PRIVATE_ADDRESS","errorMessage":"host ${host} is loopback/private/link-local; set CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE=1 to override","messagePattern":"host (.+?) is loopback/private/link-local; set CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE=1 to override","errorType":"validation","errorClass":"HttpFetchValidationError","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-tools/http-fetch-tools.ts","lineNumber":63,"sourceCode":" */\nexport function validateUrl(rawUrl: string): URL {\n  let parsed: URL;\n  try {\n    parsed = new URL(rawUrl);\n  } catch {\n    throw new HttpFetchValidationError(`invalid URL: ${rawUrl}`, 'INVALID_URL');\n  }\n  const proto = parsed.protocol.toLowerCase();\n  if (proto !== 'http:' && proto !== 'https:') {\n    throw new HttpFetchValidationError(\n      `protocol ${parsed.protocol} not allowed (only http: and https:)`,\n      'FORBIDDEN_PROTOCOL',\n    );\n  }\n  const host = parsed.hostname.toLowerCase();\n  const allowPrivate = process.env.CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE === '1';\n  if (!allowPrivate && isPrivateOrLoopback(host)) {\n    throw new HttpFetchValidationError(\n      `host ${host} is loopback/private/link-local; set CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE=1 to override`,\n      'PRIVATE_ADDRESS',\n    );\n  }\n  return parsed;\n}\n\nfunction isPrivateOrLoopback(host: string): boolean {\n  if (host === 'localhost' || host === 'localhost.localdomain') return true;\n  // IPv6 loopback\n  if (host === '::1' || host === '[::1]') return true;\n  // IPv4 numeric checks\n  const m = host.match(/^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/);\n  if (m) {\n    const a = Number(m[1]);\n    const b = Number(m[2]);\n    if (a === 0) return true;          // 0.0.0.0/8\n    if (a === 127) return true;        // loopback","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/mcp-tools/http-fetch-tools.ts#L45-L81","documentation":"Thrown by validateUrl as an SSRF guard: the parsed hostname resolves to loopback, RFC-1918 private space, link-local, CGNAT, or IPv6 ULA/loopback, and the env override CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE is not set to '1'. This secure-by-default posture stops the fetch tool from reaching internal services. The error carries code PRIVATE_ADDRESS.","triggerScenarios":"URLs targeting localhost, 127.0.0.1, ::1, 10.x, 172.16-31.x, 192.168.x, 169.254.x, 100.64-127.x (CGNAT), fc/fd (IPv6 ULA), fe80: (link-local), or 0.0.0.0 — without CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE=1 in the environment.","commonSituations":"Fetching a local dev server (http://localhost:3000); hitting an internal service on a private subnet; cloud metadata endpoint 169.254.169.254; testing against a Docker bridge IP; CI environments where the target is on a private network.","solutions":["Set CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE=1 in the environment when fetching local/internal targets is intentional.","Route the fetch through a public-facing endpoint (tunnel, ingress) instead of the private address.","Confirm you actually need a private target — the default is a security control, not a bug.","For one-off local testing: `CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE=1 npx @claude-flow/cli ...`."],"exampleFix":"// before\nhttp_fetch({ url: 'http://localhost:3000/health' })  // throws PRIVATE_ADDRESS\n// after\n// set env: export CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE=1\nhttp_fetch({ url: 'http://localhost:3000/health' })","handlingStrategy":"validation","validationCode":"function assertPublicUrl(raw, allowPrivate = process.env.CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE === '1') {\n  const u = new URL(raw);\n  const host = u.hostname.toLowerCase();\n  if (!allowPrivate && isPrivate(host)) {\n    throw new Error(`private host ${host}; set CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE=1`);\n  }\n  return u;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Set CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE=1 only for trusted local/internal targets.","Use a public ingress/tunnel instead of the private address when possible.","Treat the default as a security control, not a nuisance."],"tags":["http-fetch","security","ssrf","network","validation"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}