ExAthena.Tools.WebFetch (ExAthena v0.18.0)

Copy Markdown View Source

HTTP GET via Req, returning the body as text.

Arguments:

  • url (required) — http:// or https:// only; other schemes rejected.
  • timeout_ms (optional, default 10_000).

Response body capped at 1 MB. Redirects followed up to 5 hops.

SSRF guard

Every fetch target — the initial URL and every redirect hop — is validated before a request is made: non-http(s) schemes are rejected, and hosts that are (or resolve to) loopback, private, link-local, CGNAT, or otherwise non-public addresses are refused (see ExAthena.Net.blocked_host?/1). This applies regardless of confinement, so the default library configuration cannot be steered to http://169.254.169.254/ (cloud metadata), localhost, or internal services — including via a public URL that 302s to one. Redirects are followed manually (Req auto-redirect is disabled) so each Location target is re-validated before it is fetched.

Hosts that legitimately need local fetching (docs served from a dev server, a localhost API) opt out with allow_local_hosts: true on ExAthena.run/2 (threaded into ctx.assigns[:allow_local_hosts]), which disables the host block for that run; the scheme check still applies.

Known limit (documented rather than over-engineered): hostname validation resolves DNS at check time, but the HTTP client resolves again when connecting, so a resolver flipping records between the two lookups (DNS-rebinding TOCTOU) can still slip through. Deployments that must defend against that should pin egress at the network layer.

This is deliberately minimal — it's here so agents can fetch documentation pages, not to replace a full HTTP client. For richer access (auth headers, POST bodies, etc.), implement a custom tool that wraps Req directly.