ruvnet/ruflo · error · HttpFetchValidationError

FORBIDDEN_HEADER

FORBIDDEN_HEADER

Error message

header "${key}" is not allowed without CLAUDE_FLOW_HTTP_FETCH_ALLOW_AUTH=1

What it means

Thrown by validateHeaders in the http_fetch tool when a header name exactly matches the auth-bearing blocklist FORBIDDEN_HEADERS_EXACT (authorization, cookie, set-cookie, proxy-authorization) and CLAUDE_FLOW_HTTP_FETCH_ALLOW_AUTH is not '1'. This default prevents credential pass-through to potentially untrusted endpoints. The error carries code FORBIDDEN_HEADER.

Source

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

    if (a === 169 && b === 254) return true;          // link-local
    if (a === 100 && b >= 64 && b <= 127) return true; // CGNAT
    return false;
  }
  // IPv6 bracketed addresses and other special forms — be conservative and
  // accept only public-looking literals. Reject obvious private/local forms.
  if (host.startsWith('fc') || host.startsWith('fd')) return true;  // fc00::/7 ULA
  if (host.startsWith('fe80:')) return true;                        // link-local
  return false;
}

export function validateHeaders(headers: Record<string, string>): Record<string, string> {
  const allowAuth = process.env.CLAUDE_FLOW_HTTP_FETCH_ALLOW_AUTH === '1';
  const out: Record<string, string> = {};
  for (const [key, value] of Object.entries(headers)) {
    const lower = key.toLowerCase();
    if (!allowAuth) {
      if ((FORBIDDEN_HEADERS_EXACT as readonly string[]).includes(lower)) {
        throw new HttpFetchValidationError(
          `header "${key}" is not allowed without CLAUDE_FLOW_HTTP_FETCH_ALLOW_AUTH=1`,
          'FORBIDDEN_HEADER',
        );
      }
      if (FORBIDDEN_HEADER_PREFIXES.some((p) => lower.startsWith(p))) {
        throw new HttpFetchValidationError(
          `header "${key}" is not allowed without CLAUDE_FLOW_HTTP_FETCH_ALLOW_AUTH=1`,
          'FORBIDDEN_HEADER',
        );
      }
    }
    if (typeof value !== 'string') {
      throw new HttpFetchValidationError(
        `header "${key}" must be a string`,
        'INVALID_HEADER_VALUE',
      );
    }
    out[key] = value;

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Set CLAUDE_FLOW_HTTP_FETCH_ALLOW_AUTH=1 in the environment when credential headers are intentional and the target is trusted.
  2. Prefer a server-side proxy that injects credentials rather than passing them through the fetch tool.
  3. Confirm the target endpoint truly requires the header; some APIs accept query-param or mTLS auth instead.
  4. Never set ALLOW_AUTH=1 together with untrusted/external URLs — it leaks credentials.

Example fix

// before
http_fetch({ url, headers: { authorization: `Bearer ${token}` } })  // throws
// after
// env: export CLAUDE_FLOW_HTTP_FETCH_ALLOW_AUTH=1
http_fetch({ url, headers: { authorization: `Bearer ${token}` } })
Defensive patterns

Strategy: validation

Validate before calling

const EXACT = new Set(['authorization','cookie','set-cookie','proxy-authorization']);
function assertHeadersAllowed(headers, allowAuth = process.env.CLAUDE_FLOW_HTTP_FETCH_ALLOW_AUTH === '1') {
  if (!allowAuth) for (const k of Object.keys(headers)) {
    if (EXACT.has(k.toLowerCase())) throw new Error(`header ${k} requires CLAUDE_FLOW_HTTP_FETCH_ALLOW_AUTH=1`);
  }
}

Prevention

When it happens

Trigger: Passing headers containing (case-insensitive) 'authorization', 'cookie', 'set-cookie', or 'proxy-authorization' without setting CLAUDE_FLOW_HTTP_FETCH_ALLOW_AUTH=1. Header names are lowercased before the exact-match check.

Common situations: Adding an Authorization: Bearer <token> for an authenticated API; forwarding session cookies; using a proxy that requires proxy-authorization; calling an internal API that mandates an auth header.

Related errors


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