Runlight.Safefetch (runlight v0.3.2)

Copy Markdown View Source

Fetches from addresses that other people's input names, such as the icon links on a site's home page or a link domain, and only from the public internet (the SDK's safefetch.ts). Only https is fetched, never a private, loopback, link-local, or metadata address, and redirects are followed by hand under the same rules.

The default fetcher (Runlight.Fetch.httpc/2) resolves the name itself, checks every address, and connects to one it checked, so a name that answers differently a moment later gets nowhere. The name is also checked just before each request, for a fetcher of the app's own that cannot choose the address it connects to.

Summary

Functions

Whether an address is link-local (169.254.0.0/16, fe80::/10) or a cloud's metadata service (fd00:ec2::254, Alibaba's 100.100.100.200), in any of its IPv6 spellings. A model's API or a mail server is never there, though it may well be on this computer or a private network, so those stay allowed where this is checked.

Whether a host, by its name, its address, or any address its name resolves to now, is link-local or a metadata service. A name that does not resolve here is let through, since the connection then goes nowhere such a check could have stopped.

Whether an IP address, v4 or v6, is on the public internet. Anything that is not an address is not.

The public addresses a name resolves to, for setting up DNS records. None where it does not resolve.

Fetches an https URL on the public internet (a GET unless method: and body: say otherwise), following up to redirects: redirects that stay on it, within timeout: milliseconds in all. Only a GET follows redirects; anything else comes back with the redirect as it is. Answers {:error, :private} for an address off it and {:error, :timeout} when time runs out. A redirect past the last one comes back as it is.

Whether a name resolves to an address off the public internet. False when it does not resolve.

Fetches from a service someone set up, such as an AI model's API or a mail webhook, which may well be on this computer or a private network, but is never at a link-local or cloud metadata address. fetch is the fetcher (fn url, opts -> ... end) and opts its options. The address is checked, and so is every redirect's, up to redirects: of them (default 5), each followed as fetch follows it: a 303, or a 301 or 302 answering a POST, goes on as a GET with no body. Only a redirect on the same origin is followed: the request carries a key, in whatever header the service names it, and a 307 or 308 would carry its body too, an email or a conversation, so a redirect to another origin, https to http among them, is refused before anything is sent there. A redirect past the last one comes back as it is. timeout: is for all of them together. Answers {:error, {:address_refused, host}} for an address it refuses, and {:error, {:redirect_refused, host, to}} for a redirect it will not follow, before anything is asked there. The default fetcher is also told (service: true) to connect only to an address it checked, with no proxy between.

Functions

metadata_address?(ip)

@spec metadata_address?(String.t()) :: boolean()

Whether an address is link-local (169.254.0.0/16, fe80::/10) or a cloud's metadata service (fd00:ec2::254, Alibaba's 100.100.100.200), in any of its IPv6 spellings. A model's API or a mail server is never there, though it may well be on this computer or a private network, so those stay allowed where this is checked.

metadata_host?(host)

@spec metadata_host?(String.t()) :: boolean()

Whether a host, by its name, its address, or any address its name resolves to now, is link-local or a metadata service. A name that does not resolve here is let through, since the connection then goes nowhere such a check could have stopped.

public_address?(ip)

@spec public_address?(String.t()) :: boolean()

Whether an IP address, v4 or v6, is on the public internet. Anything that is not an address is not.

public_addresses(name)

@spec public_addresses(String.t()) :: [String.t()]

The public addresses a name resolves to, for setting up DNS records. None where it does not resolve.

public_fetch(rl, target, opts)

@spec public_fetch(Runlight.t(), String.t(), keyword()) ::
  {:ok, Runlight.Http.Response.t()} | {:error, term()}

Fetches an https URL on the public internet (a GET unless method: and body: say otherwise), following up to redirects: redirects that stay on it, within timeout: milliseconds in all. Only a GET follows redirects; anything else comes back with the redirect as it is. Answers {:error, :private} for an address off it and {:error, :timeout} when time runs out. A redirect past the last one comes back as it is.

resolves_privately?(name)

@spec resolves_privately?(String.t()) :: boolean()

Whether a name resolves to an address off the public internet. False when it does not resolve.

service_fetch(fetch, target, opts)

@spec service_fetch(
  (String.t(), keyword() ->
     {:ok, Runlight.Http.Response.t()} | {:error, term()}),
  String.t(),
  keyword()
) :: {:ok, Runlight.Http.Response.t()} | {:error, term()}

Fetches from a service someone set up, such as an AI model's API or a mail webhook, which may well be on this computer or a private network, but is never at a link-local or cloud metadata address. fetch is the fetcher (fn url, opts -> ... end) and opts its options. The address is checked, and so is every redirect's, up to redirects: of them (default 5), each followed as fetch follows it: a 303, or a 301 or 302 answering a POST, goes on as a GET with no body. Only a redirect on the same origin is followed: the request carries a key, in whatever header the service names it, and a 307 or 308 would carry its body too, an email or a conversation, so a redirect to another origin, https to http among them, is refused before anything is sent there. A redirect past the last one comes back as it is. timeout: is for all of them together. Answers {:error, {:address_refused, host}} for an address it refuses, and {:error, {:redirect_refused, host, to}} for a redirect it will not follow, before anything is asked there. The default fetcher is also told (service: true) to connect only to an address it checked, with no proxy between.