Documentation
¶
Overview ¶
Package httpguard provides SSRF-protection helpers for outbound HTTP clients that accept user-configured base URLs (e.g. OpenAI-compatible providers).
The package is intentionally self-contained so that any internal package can import it without risking a circular dependency. Do NOT import internal/discordbot or internal/llm from here.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func IsBlockedIP ¶
IsBlockedIP returns (true, reason) when ip falls in a blocked range. Nil ip returns (false, "") — safe to call with net.ParseIP output that may be nil.
func IsConstructorBlockedIP ¶
IsConstructorBlockedIP returns (true, reason) only for addresses that are definitely unsafe as user-configured base URLs at construction time.
Unlike IsBlockedIP (which is used by SafeDial and blocks all RFC-1918 ranges including loopback), this function is deliberately lenient about loopback and private LAN ranges so that legitimate Ollama / vLLM deployments on localhost or a private network are not rejected at startup. It ONLY blocks:
- 169.254.0.0/16 — link-local / cloud metadata (Railway SSRF vector)
- 0.0.0.0/32 — unspecified (invalid destination)
- ::/128 — IPv6 unspecified
- fe80::/10 — IPv6 link-local
func IsSafeURL ¶
IsSafeURL validates rawURL against SSRF block rules using the full IsBlockedIP check (all RFC-1918, link-local, loopback, unspecified). It performs DNS resolution and checks every returned IP. ctx is propagated to the DNS lookup so it can be cancelled by the caller's deadline.
func NewSafeHTTPClient ¶
NewSafeHTTPClient returns an *http.Client whose Transport uses SafeDial, enforcing SSRF protection at the TCP dial layer (DNS rebinding mitigation), with a 10-second connection timeout. Caller MUST set a meaningful Timeout after calling NewSafeHTTPClient (the default 10 s may not suit all providers). Caller MUST wrap resp.Body with io.LimitReader before reading.
func SafeDial ¶
SafeDial is a custom DialContext that re-validates the destination at dial time, defending against DNS rebinding between URL check and actual connection.
net/http.Transport passes the RAW HOSTNAME to a custom DialContext, not a resolved IP. We handle two cases:
- host is already an IP literal — check directly via IsBlockedIP and dial.
- host is a hostname — resolve DNS ourselves, validate every returned address with IsBlockedIP, and dial the first safe IP.
Always dialing the resolved numeric IP (rather than passing the hostname back to the OS resolver) ensures the IP we validated is the IP we actually connect to — the rebind window is closed.