Skip to content

Validate gateway base URL scheme and host in build_gateway_client #323

Description

@ckrough

Follow-up from #93 (shared gateway client package). Raised by the security review on PR #321 as a medium advisory, accepted for that PR because the diff was a no-behavior-change extraction.

build_gateway_client passes settings.llm_gateway_base_url straight into AsyncOpenAI(base_url=...) and attaches the real gateway bearer token via default_headers. It validates neither the scheme nor the host. A misconfigured or attacker-influenced LLM_GATEWAY_URL therefore sends the credential to whatever origin the value names, and every chat, embedding, and moderation call routes there.

Two things changed the shape of this risk in #93, without making it reachable today:

  1. The builder moved into a shared package, so any future service can call it, not just retriever.
  2. GatewaySettings is a structural typing.Protocol, so any object exposing the four members type-checks where retriever.config.Settings was previously required. The type system no longer constrains what reaches base_url.

Nothing reachable exploits this now: llm_gateway_base_url is resolved in retriever.config from operator-set env vars, not from user input. This is hardening ahead of a second consumer, and the right place for the check is the builder, so every consumer inherits it.

  • Reject a base_url whose scheme is not https (allow http only for loopback, so local dev against a stub gateway still works).
  • Enforce a host allowlist, configurable, defaulting to the Cloudflare AI Gateway host plus any explicitly configured LLM_GATEWAY_URL host.
  • Raise a clear ValueError at client construction, matching how llm_gateway_base_url already fails fast when no gateway is configured.
  • Decide whether the check belongs in build_gateway_client or in a validator on the settings side, and note the reasoning in packages/llm/README.md.

Acceptance Criteria

  • build_gateway_client raises on a plain-http non-loopback base URL.
  • build_gateway_client raises on a host outside the allowlist.
  • A loopback http URL still builds, so local dev is unaffected.
  • Tests cover all three cases in packages/llm/tests/.
  • Retriever's existing gateway behavior is unchanged for a correctly configured gateway.

Metadata

Metadata

Assignees

No one assigned

    Labels

    high-riskTouches data, auth, or LLM surface; warrants extra reviewmodule:platformCross-cutting platform / foundationsecuritySecurity review, hardening, and threat-model work

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions