ruvnet/ruflo · error · Error
SSRF guard: only HTTPS URLs are permitted, got
Error message
SSRF guard: only HTTPS URLs are permitted, got ${parsed.protocol} What it means
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.
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
Example fix
// before
await client.callTool('sona_profile_get', { profileId: 'Fast' }); // capital F -> undefined in Map -> throws [1132]
// after
await client.callTool('sona_profile_get', { profileId: 'fast' }); Defensive patterns
Strategy: validation
Validate before calling
const BUILT_IN_PROFILES = new Set(['default', 'fast', 'accurate', 'memory-efficient']);
function isKnownProfileId(id: string): boolean {
return BUILT_IN_PROFILES.has(id); // extend with ids returned by sona_profile_list in this process
} Type guard
function isBuiltInProfile(id: string): id is 'default' | 'fast' | 'accurate' | 'memory-efficient' {
return id === 'default' || id === 'fast' || id === 'accurate' || id === 'memory-efficient';
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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).
Related errors
- SSRF guard: invalid URL
- SSRF guard: only HTTPS URLs are permitted, got
- SSRF guard: private/loopback host rejected
- Dangerous key segment rejected
- Failed to create share link
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/45bc221a2df830e5.
Report an issue: GitHub.
Appendix: source
Thrown at ruflo/src/ruvocal/mcp-bridge/index.js:746
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();
const timer = setTimeout(() => controller.abort(), timeoutMs);
try {
const resp = await fetch(url, {View on GitHub (pinned to fa13ee4ad6)