Managoat.McpAuth.UrlGuard (managoat_mcp_auth v0.1.0)

Copy Markdown View Source

The one rule for a URL fetched server-side: https, a real hostname, and nothing that resolves into the cluster. OAuth clients, MCP discovery and dynamic client registration all GET/POST tenant-supplied addresses from inside the platform, which is exactly the request an SSRF wants — so every one of them passes through check/1 first, and again on each use, since DNS can change between a save and a fetch.

Rejected: any scheme but https, an IP literal, localhost and the cluster-internal names, and a hostname that resolves only to loopback, private (RFC 1918), link-local, CGNAT or the metadata range. A hostname that does not resolve is also refused: nothing could be fetched from it, and a name that resolves later to something private is the trick.

:allow_private_hosts can be passed to check/2 to turn the resolution check off so a local stub can stand in for a provider. Its default comes from config :managoat_mcp_auth, allow_private_hosts: false.

Summary

Functions

:ok, or {:error, reason} naming the rule the URL broke.

Why a URL was refused, for a form or an API error.

Types

reason()

@type reason() ::
  :not_https
  | :no_host
  | :ip_literal
  | :internal_host
  | :private_address
  | :unresolvable

Functions

check(url, opts \\ [])

@spec check(
  String.t() | nil,
  keyword()
) :: :ok | {:error, reason()}

:ok, or {:error, reason} naming the rule the URL broke.

message(atom)

Why a URL was refused, for a form or an API error.