Fix the URL screen for a link preview endpoint
Your task
An AI built a link preview endpoint that fetched any URL a user sent. An attacker could send http://169.254.169.254/latest/meta-data/ to read the cloud instance's credentials, or reach internal services on localhost. This is server-side request forgery (SSRF). Your team now screens every preview URL before the server fetches it. A production guard also checks the address the host name resolves to and refuses redirects; this check is the part that needs no network.
An assistant wrote screen_preview_urls(urls, allowed_hosts). It is in the editor. It has a bug. Find it and fix it.
Input and output:
urls: the URLs users sent.allowed_hosts: the domains previews may come from.- Return one verdict per URL, in order:
"allow","block:scheme","block:private_ip"or"block:host_not_allowed".
Rules the tests check:
- Trim whitespace from each URL and each allowed host, and lowercase the allowed hosts. Ignore empty allowed hosts.
- The scheme is the text before the first
://, lowercased. With no://, or with a scheme other thanhttps:"block:scheme". - Find the host in the text after that
://, in this order:- Cut it at the first
/,?or#. What is left is the authority. - If the authority contains
@, keep only what comes after its last@. The part before it is a user name and password, not the host. - Cut at the first
:to drop the port, then lowercase.
- Cut it at the first
"block:private_ip": the host islocalhost, ends with.localhost,.internalor.local, or is a private IPv4 address. That is four parts separated by., each 1 to 3 digits and at most 255, where the first part is 0, 10 or 127, or the first two parts are 172 and 16 to 31, or 192 and 168, or 169 and 254.- Otherwise
"allow"when the host equals an allowed host or ends with.followed by one. Anything else:"block:host_not_allowed".
Examples
screen_preview_urls(urls: list[str], allowed_hosts: list[str]) -> list[str]Example 1
Input
screen_preview_urls(["https://example.com/blog/post"], ["example.com"])Output
["allow"]Example 2
Input
screen_preview_urls(["http://example.com/", "https://169.254.169.254/latest/meta-data/iam/"], ["example.com"])Output
["block:scheme", "block:private_ip"]Example 3
Input
screen_preview_urls(["https://example.com@169.254.169.254/latest/meta-data/", "https://cdn.example.com/a.png"], ["example.com"])Output
["block:private_ip", "allow"]