{"record":{"id":"0f98a5d3ea0f15d0","repo":"ruvnet/ruflo","slug":"ssrf-guard-invalid-url-rawurl","errorCode":null,"errorMessage":"SSRF guard: invalid URL — ${rawUrl}","messagePattern":"SSRF guard: invalid URL — (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ruflo/src/mcp-bridge/index.js","lineNumber":654,"sourceCode":"    }\n    return { guidance: groupGuides[topic], topic };\n  }\n\n  return { guidance: `Unknown topic '${topic}'. Use 'overview', 'groups', or a specific group name.`, topic };\n}\n\n// =============================================================================\n// SSRF GUARD — Reject requests to private/loopback ranges (CWE-918)\n// =============================================================================\n\nconst PRIVATE_IP_RE = /^(?:10\\.|172\\.(?:1[6-9]|2\\d|3[01])\\.|192\\.168\\.|127\\.|0\\.|::1|fc|fd)/i;\n\nfunction assertSafeUrl(rawUrl) {\n  let parsed;\n  try {\n    parsed = new URL(rawUrl);\n  } catch {\n    throw new Error(`SSRF guard: invalid URL — ${rawUrl}`);\n  }\n  if (parsed.protocol !== \"https:\") {\n    throw new Error(`SSRF guard: only HTTPS URLs are permitted, got ${parsed.protocol}`);\n  }\n  const host = parsed.hostname;\n  if (PRIVATE_IP_RE.test(host) || host === \"localhost\" || host.endsWith(\".local\")) {\n    throw new Error(`SSRF guard: private/loopback host rejected — ${host}`);\n  }\n}\n\n// =============================================================================\n// HELPER — Call a backend Cloud Function / API\n// =============================================================================\n\nasync function callCloudFunction(url, payload, timeoutMs = 25000) {\n  // Validate the URL before making any network request.\n  assertSafeUrl(url);\n  const controller = new AbortController();","sourceCodeStart":636,"sourceCodeEnd":672,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/ruflo/src/mcp-bridge/index.js#L636-L672","documentation":"First of three sequential checks in assertSafeUrl() (ruflo/src/mcp-bridge/index.js): it runs new URL(rawUrl) inside try/catch and rethrows as this SSRF-guard error when the constructor rejects. This catches strings that are not parseable URLs at all before the protocol/host checks run. The guard exists to prevent server-side request forgery (CWE-918).","triggerScenarios":"Passing a malformed URL to callCloudFunction or any caller of assertSafeUrl: empty string, undefined coerced to 'undefined', 'not a url', 'ftp://', a bare hostname like 'example.com' without a scheme, or a string with illegal characters.","commonSituations":"An MCP/cloud-function base URL env var is unset (resolves to undefined → 'undefined') or blank; a config typo omits the scheme; a template string concatenates user input that produces an invalid URL; the value was loaded from a YAML/JSON config that quoted it incorrectly.","solutions":["Set the base URL env var to a complete 'https://...' string and verify it is defined at startup.","Coerce and validate the URL with new URL() at config-load time so the failure is loud at boot, not at first request.","Default unsafe/empty config to undefined and fail fast with a clear config error instead of passing garbage to assertSafeUrl.","Ensure template strings that build URLs include the scheme and encode path segments."],"exampleFix":"// before\nawait callCloudFunction(process.env.CF_URL, payload); // CF_URL unset\n\n// after\nconst base = process.env.CF_URL;\nif (!base) throw new Error('CF_URL not configured');\nnew URL(base); // validate at startup\nawait callCloudFunction(base, payload);","handlingStrategy":"validation","validationCode":"function safeUrl(raw: string): URL | null { try { return new URL(raw); } catch { return null; } }\nconst u = safeUrl(process.env.CF_URL ?? '');\nif (!u) throw new Error('CF_URL is not a valid URL');","typeGuard":"function isParseableUrl(raw: string): boolean { try { new URL(raw); return true; } catch { return false; } }","tryCatchPattern":"try { await callCloudFunction(url, payload); } catch (e) { if (e instanceof Error && e.message.startsWith('SSRF guard: invalid URL')) throw new Error(`Misconfigured backend URL: ${url}`, { cause: e }); throw e; }","preventionTips":["Validate base URLs with new URL() at startup.","Fail fast when a required URL env var is missing.","Treat undefined/empty URL values as config errors at boot."],"tags":["security","ssrf","url-validation","mcp-bridge"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}