ruvnet/ruflo · error · Error
SSRF guard: invalid URL — ${rawUrl}
Error message
SSRF guard: invalid URL — ${rawUrl} What it means
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).
Source
Thrown at ruflo/src/mcp-bridge/index.js:654
}
return { guidance: groupGuides[topic], topic };
}
return { guidance: `Unknown topic '${topic}'. Use 'overview', 'groups', or a specific group name.`, topic };
}
// =============================================================================
// SSRF GUARD — Reject requests to private/loopback ranges (CWE-918)
// =============================================================================
const PRIVATE_IP_RE = /^(?:10\.|172\.(?:1[6-9]|2\d|3[01])\.|192\.168\.|127\.|0\.|::1|fc|fd)/i;
function assertSafeUrl(rawUrl) {
let parsed;
try {
parsed = new URL(rawUrl);
} catch {
throw new Error(`SSRF guard: invalid URL — ${rawUrl}`);
}
if (parsed.protocol !== "https:") {
throw new Error(`SSRF guard: only HTTPS URLs are permitted, got ${parsed.protocol}`);
}
const host = parsed.hostname;
if (PRIVATE_IP_RE.test(host) || host === "localhost" || host.endsWith(".local")) {
throw new Error(`SSRF guard: private/loopback host rejected — ${host}`);
}
}
// =============================================================================
// HELPER — Call a backend Cloud Function / API
// =============================================================================
async function callCloudFunction(url, payload, timeoutMs = 25000) {
// Validate the URL before making any network request.
assertSafeUrl(url);
const controller = new AbortController();View on GitHub (pinned to 6b01dc5a68)
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.
Example fix
// before
await callCloudFunction(process.env.CF_URL, payload); // CF_URL unset
// after
const base = process.env.CF_URL;
if (!base) throw new Error('CF_URL not configured');
new URL(base); // validate at startup
await callCloudFunction(base, payload); Defensive patterns
Strategy: validation
Validate before calling
function safeUrl(raw: string): URL | null { try { return new URL(raw); } catch { return null; } }
const u = safeUrl(process.env.CF_URL ?? '');
if (!u) throw new Error('CF_URL is not a valid URL'); Type guard
function isParseableUrl(raw: string): boolean { try { new URL(raw); return true; } catch { return false; } } Try / catch
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; } Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- SSRF guard: invalid URL — ${rawUrl}
- SSRF guard: only HTTPS URLs are permitted, got ${parsed.prot
- SSRF guard: private/loopback host rejected — ${host}
- SSRF guard: only HTTPS URLs are permitted, got ${parsed.prot
- SSRF guard: private/loopback host rejected — ${host}
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/0f98a5d3ea0f15d0.
Report an issue: GitHub.