{"record":{"id":"45bc221a2df830e5","repo":"ruvnet/ruflo","slug":"ssrf-guard-only-https-urls-are-permitted-got-p-45bc22","errorCode":null,"errorMessage":"SSRF guard: only HTTPS URLs are permitted, got ${parsed.protocol}","messagePattern":"SSRF guard: only HTTPS URLs are permitted, got (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ruflo/src/ruvocal/mcp-bridge/index.js","lineNumber":746,"sourceCode":"    return { error: err.message };\n  }\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();\n  const timer = setTimeout(() => controller.abort(), timeoutMs);\n  try {\n    const resp = await fetch(url, {","sourceCodeStart":728,"sourceCodeEnd":764,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/ruflo/src/ruvocal/mcp-bridge/index.js#L728-L764","documentation":"SONA profiles are kept in an in-memory Map seeded at singleton construction with exactly four built-ins: 'default', 'fast', 'accurate', and 'memory-efficient'. handleProfileGet resolves profileId from input or falls back to state.activeProfileId ('default'), then throws 'Profile <id> not found' when the Map has no such key. Any other ID — typo, custom profile name never created, or a profile from a previous process — fails.","triggerScenarios":"sona_profile_get with profileId=\"fastt\" or \"FAST\" (IDs are case-sensitive lowercase); requesting a custom profile id that was never created via the profile-create tool in this process; referencing a profile by its display name ('Fast') instead of its id ('fast'); after activeProfileId was set to a profile that disappeared on restart.","commonSituations":"Docs examples using human names rather than ids; scripts hardcoding profile ids that were renamed; assuming profiles persist across restarts like config files (they do not — only the four built-ins are re-seeded).","solutions":["Use one of the built-in ids exactly: 'default', 'fast', 'accurate', 'memory-efficient'","List profiles first via the sona profile list tool and pass an id exactly as returned","For custom profiles, create them in the same process before getting them, and re-create after every server restart","Check casing — the Map keys are lowercase; 'Default' with a capital D will not match"],"exampleFix":"// before\nawait client.callTool('sona_profile_get', { profileId: 'Fast' }); // capital F -> undefined in Map -> throws [1132]\n\n// after\nawait client.callTool('sona_profile_get', { profileId: 'fast' });","handlingStrategy":"validation","validationCode":"const BUILT_IN_PROFILES = new Set(['default', 'fast', 'accurate', 'memory-efficient']);\nfunction isKnownProfileId(id: string): boolean {\n  return BUILT_IN_PROFILES.has(id); // extend with ids returned by sona_profile_list in this process\n}","typeGuard":"function isBuiltInProfile(id: string): id is 'default' | 'fast' | 'accurate' | 'memory-efficient' {\n  return id === 'default' || id === 'fast' || id === 'accurate' || id === 'memory-efficient';\n}","tryCatchPattern":null,"preventionTips":["List profiles via the profile list tool and copy the id verbatim (lowercase)","Use profile ids, not display names ('fast' not 'Fast')","Re-create custom profiles after every server restart — only the four built-ins are re-seeded"],"tags":["mcp","sona","profile","not-found","config"],"backgroundTag":"profile-not-found","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}