{"record":{"id":"b24caf7f2cbc1c5c","repo":"ruvnet/ruflo","slug":"peer-url-must-be-http-or-https","errorCode":null,"errorMessage":"peer url must be http or https","messagePattern":"peer url must be http or https","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-tools/agentbbs-federation.ts","lineNumber":254,"sourceCode":"  }\n}\n\nfunction writePeers(basePath: string, peers: FederationPeer[]): void {\n  ensureDir(basePath);\n  writeFileSync(peersPath(basePath), JSON.stringify(peers, null, 2) + '\\n');\n}\n\n/**\n * Reject anything that is not a plain http(s) URL to a host.\n *\n * Blocks credentials-in-URL (they would be logged), and non-http schemes such\n * as `file:` which would turn a peer entry into a local file read.\n */\nexport function validatePeerUrl(raw: string): string {\n  let u: URL;\n  try { u = new URL(raw); } catch { throw new Error('peer url is not a valid URL'); }\n  if (u.protocol !== 'http:' && u.protocol !== 'https:') {\n    throw new Error('peer url must be http or https');\n  }\n  if (u.username || u.password) throw new Error('peer url must not embed credentials');\n  return u.origin;\n}\n\nexport function addPeer(\n  basePath: string,\n  input: { nodeId: string; url: string; publicKey: string; label?: string },\n): FederationPeer {\n  if (!NODE_ID_RE.test(input.nodeId ?? '')) throw new Error('nodeId must be 16 lowercase hex chars');\n  if (!HEX64_RE.test(input.publicKey ?? '')) throw new Error('publicKey must be 64 lowercase hex chars');\n  const url = validatePeerUrl(String(input.url));\n\n  const peers = readPeers(basePath);\n  if (peers.length >= MAX_PEERS) throw new Error(`peer registry is full (${MAX_PEERS})`);\n\n  const existing = peers.find(p => p.nodeId === input.nodeId);\n  if (existing) {","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/ruvnet/ruflo/blob/2602b642d92234c710ffbe96bfb33007d481ceab/v3/@claude-flow/cli/src/mcp-tools/agentbbs-federation.ts#L236-L272","documentation":"validatePeerUrl allows only http: and https: protocols. Other schemes (file:, ftp:, ws:, data:, etc.) are rejected, explicitly to prevent a peer entry from becoming a local file read via file:. The check runs after the URL successfully parses.","triggerScenarios":"Calling validatePeerUrl(raw) or addPeer(...) with a parseable URL whose u.protocol is not 'http:' or 'https:' — e.g. 'file:///etc/passwd', 'ftp://host/x', 'ws://host:9000'.","commonSituations":"Reusing an internal WebSocket/FTP endpoint string as a peer URL; pointing a peer at a local file path for testing; copy-pasting a ws:// service URL from another config.","solutions":["Use an http:// or https:// URL for the peer endpoint","If the peer serves another protocol, expose/proxy it behind an HTTP(S) endpoint registered in the federation","Remove any file:// paths from peer configuration and supply a real network address","If this was an internal test, run an HTTP server locally and register http://127.0.0.1:port instead"],"exampleFix":"// before\nvalidatePeerUrl('file:///var/lib/peer.json');\n// after\nvalidatePeerUrl('https://peer.example.com');","handlingStrategy":"validation","validationCode":"const u = new URL(peerUrl);\nif (u.protocol !== 'http:' && u.protocol !== 'https:') {\n  throw new Error(`peer url scheme must be http/https, got '${u.protocol}'`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const origin = validatePeerUrl(rawUrl);\n} catch (e) {\n  if (e.message === 'peer url must be http or https') {\n    console.error(`Refusing non-HTTP peer endpoint '${rawUrl}' — use http(s) or an HTTP proxy`);\n  } else throw e;\n}","preventionTips":["Never use file:// or ws:// strings in peer URL fields","Proxy non-HTTP services behind an HTTPS endpoint before registering them","Validate scheme at config-load time, before any addPeer call","Treat file:-scheme peer entries as a security smell — the library blocks them deliberately"],"tags":["url","security","validation"],"backgroundTag":"invalid-url","analyzedSha":"2602b642d92234c710ffbe96bfb33007d481ceab","analyzedAt":"2026-09-15T22:58:14.805Z","contentChangedAt":"2026-09-15T22:58:14.805Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}