The /api/fetchRes proxy endpoint accepted an arbitrary URL from the
request body and passed it directly to fetch(), returning the response
to the caller. Any user with the site auth code could use it to reach
internal services (cloud metadata endpoints, RFC1918 hosts, loopback,
non-http(s) schemes) — a classic CWE-918 SSRF primitive.
The endpoint now:
- rejects non-http(s) schemes
- rejects URLs containing embedded credentials
- rejects hostnames resolving to loopback/private/link-local/CGNAT/
multicast/reserved ranges, IPv6 ULA/link-local, IPv4-mapped variants
and well-known cloud metadata hostnames
- follows redirects manually and re-validates each hop (max 5)