ruvnet/ruflo · error · HttpFetchValidationError

FORBIDDEN_PROTOCOL

FORBIDDEN_PROTOCOL

Error message

protocol ${parsed.protocol} not allowed (only http: and https:)

What it means

Thrown by validateUrl when the parsed URL's protocol is neither http: nor https:. The http_fetch tool only permits those two schemes to block file://, ftp:, data:, and other transports that could exfiltrate local files or bypass network policy. The error carries code FORBIDDEN_PROTOCOL.

Source

Thrown at v3/@claude-flow/cli/src/mcp-tools/http-fetch-tools.ts:55

    this.name = 'HttpFetchValidationError';
  }
}

/**
 * Decide whether the URL is permitted under the default secure-by-default
 * allowlist. Block file://, ftp://, RFC-1918 private addresses, loopback,
 * link-local — unless CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE=1 is set.
 */
export function validateUrl(rawUrl: string): URL {
  let parsed: URL;
  try {
    parsed = new URL(rawUrl);
  } catch {
    throw new HttpFetchValidationError(`invalid URL: ${rawUrl}`, 'INVALID_URL');
  }
  const proto = parsed.protocol.toLowerCase();
  if (proto !== 'http:' && proto !== 'https:') {
    throw new HttpFetchValidationError(
      `protocol ${parsed.protocol} not allowed (only http: and https:)`,
      'FORBIDDEN_PROTOCOL',
    );
  }
  const host = parsed.hostname.toLowerCase();
  const allowPrivate = process.env.CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE === '1';
  if (!allowPrivate && isPrivateOrLoopback(host)) {
    throw new HttpFetchValidationError(
      `host ${host} is loopback/private/link-local; set CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE=1 to override`,
      'PRIVATE_ADDRESS',
    );
  }
  return parsed;
}

function isPrivateOrLoopback(host: string): boolean {
  if (host === 'localhost' || host === 'localhost.localdomain') return true;
  // IPv6 loopback

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Use only http:// or https:// URLs with http_fetch.
  2. For local file content, read the file directly with fs instead of routing through http_fetch.
  3. If you genuinely need ftp or another scheme, use a dedicated client — http_fetch will not allow it.
  4. Check that the scheme was not mangled by URL construction (e.g. 'http//example.com' missing the colon).

Example fix

// before
http_fetch({ url: 'file:///etc/hosts' })
// after
readFileSync('/etc/hosts', 'utf-8')  // use fs for local files
Defensive patterns

Strategy: validation

Validate before calling

function assertHttpUrl(raw) {
  const u = new URL(raw);
  if (u.protocol !== 'http:' && u.protocol !== 'https:') {
    throw new Error(`protocol ${u.protocol} not allowed`);
  }
  return u;
}

Type guard

function isHttpUrl(s: unknown): s is string {
  if (typeof s !== 'string') return false;
  try { const u = new URL(s); return u.protocol === 'http:' || u.protocol === 'https:'; }
  catch { return false; }
}

Prevention

When it happens

Trigger: Any URL whose scheme is not http/https: file:///etc/passwd, ftp://host, data:text/html,..., javascript:, blob:, gopher:, etc. The check reads parsed.protocol after a successful URL parse.

Common situations: Pointing http_fetch at a local file via file://; a misconfigured base URL with a trailing colon producing an unexpected scheme; copy-pasted data: URLs; test fixtures using non-http schemes.

Related errors


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/523b891fab58a0bd. Report an issue: GitHub.