ruvnet/ruflo · error · Error
SSRF guard: invalid URL — ${rawUrl}
Error message
SSRF guard: invalid URL — ${rawUrl} What it means
Identical SSRF guard to error 4, duplicated in ruflo/src/ruvocal/mcp-bridge/index.js:743. assertSafeUrl() runs new URL(rawUrl) in try/catch and rethrows when the constructor rejects, catching non-parseable URL strings before scheme/host checks. Both copies exist because the ruvocal (chat UI) package vendors its own mcp-bridge rather than importing the shared one.
Source
Thrown at ruflo/src/ruvocal/mcp-bridge/index.js:743
};
} catch (err) {
if (err.name === "AbortError" || err.name === "TimeoutError") return { error: "Search timed out" };
return { error: err.message };
}
}
// =============================================================================
// 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 relevant ruvocal env var (e.g. OPENAI_BASE_URL or MCP backend URL) to a full 'https://...' value.
- Validate the URL with new URL() at app startup so misconfig fails loud at boot.
- When building URLs from user input, construct via URL/URLSearchParams and check .origin before passing to callCloudFunction.
- Keep the two mcp-bridge copies in sync if you patch the guard — the duplication means a fix in one file does not protect the other.
Example fix
// before
const target = process.env.RUVOCAL_CF_URL; // undefined
await callCloudFunction(target, payload);
// after
const target = process.env.RUVOCAL_CF_URL;
if (!target) throw new Error('RUVOCAL_CF_URL missing');
new URL(target);
await callCloudFunction(target, 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.RUVOCAL_CF_URL ?? '');
if (!u) throw new Error('RUVOCAL_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(`Ruvocal backend URL invalid: ${url}`, { cause: e }); throw e; } Prevention
- Validate ruvocal backend URLs at app startup.
- Keep this mcp-bridge copy in sync with the shared one when patching.
- Ensure MCP_SERVERS and base URLs are set to full https:// values.
When it happens
Trigger: A chat-UI/MCP code path calls its local assertSafeUrl/callCloudFunction with a malformed URL: empty string, undefined, 'undefined', a bare hostname without scheme, or a string with illegal characters.
Common situations: A ruvocal MCP_SERVERS or backend base-url env var unset or blank; a template-built URL that drops the scheme; config loaded from .env.local with a quoting error; user-supplied tool endpoint that is not a valid URL.
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/1ebf251b0af49b7f.
Report an issue: GitHub.