url-safety hook
The url-safety hook decides whether a server-side fetch destination is safe from SSRF (a request that can be aimed at internal services, cloud metadata, or private hosts). It is the deterministic engine the ssrf sub-skill uses, and you can run it yourself.
Run it
Pass the URL or host as a single quoted argument. The hook prints one word, read in the table below.
node ./.bluespec/hooks/url-safety.mjs 'http://example.com/'
# => safe
node ./.bluespec/hooks/url-safety.mjs 'http://0x7f000001/'
# => private-target
It resolves the host the way the network does, so encoded forms of an internal address do not slip past. Hex, octal, dotless decimal, shorthand, IPv6, and IPv4-mapped forms all read private-target.
node ./.bluespec/hooks/url-safety.mjs 'http://[::1]/'
# => private-target
Where a sloppy validator and the real fetcher would read different hosts, the verdict is parser-divergent.
node ./.bluespec/hooks/url-safety.mjs 'http://allowed.com@evil.example/'
# => parser-divergent
How to read the verdict
| Verdict | Meaning |
|---|---|
safe | The destination connects to a public host, and a sloppy validator would read the same host. |
private-target | The host resolves to a private, loopback, link-local, unspecified, or cloud-metadata address. |
parser-divergent | A sloppy text validator would read a different real host than the one the fetcher connects to. |
invalid url | The string does not parse as a URL, so there is nothing to fetch. |
Only safe is an allow. Treat private-target, parser-divergent, and invalid url all as a block.
The destination is passed as a positional argument, never interpolated into the command. A value with quotes or backticks stays inert and cannot inject into the shell. Always wrap it in single quotes so your shell does not expand it first.
The hook is pure and never resolves DNS, so two runtime gaps are out of scope: DNS rebinding (a name that resolves public when checked, internal when fetched) and redirect chains (a first hop that passes, then redirects inward). Close those by resolving once and connecting to the validated address, and by refusing redirects. It pins the reserved loopback names (localhost and the .localhost family) to internal with no lookup, but any other DNS name reads safe, because asserting it resolves inward would require resolution: the allowlist guards those, and is what this checker validates.
This is the same check the ssrf sub-skill runs to prove a destination filter holds. Point it at each host in an allowlist or denylist, one call per host.