From 6fdbce84420aa0a38898c3cdb094cec1ede09144 Mon Sep 17 00:00:00 2001 From: SandObserver <260779319+SandObserver@users.noreply.github.com> Date: Sun, 9 Aug 2026 15:07:53 -0400 Subject: [PATCH] Docs: name the SSRF guard's real mechanism architecture.md attributed the outbound guard to PRIVATE_IP_RE, which exists only as an independent comparison regex in a test file. The guard is isPrivateAddress, built from the BLOCKED_IPV4 CIDR table and BLOCKED_IPV6_RE. The category list was short as well: it named private, loopback and link-local but not carrier-grade NAT, multicast, reserved and broadcast, benchmarking or IETF protocol assignments, nor that localhost is refused by name and IPv4-mapped IPv6 forms are unwrapped before the check. --- docs/architecture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/architecture.md b/docs/architecture.md index 49b5f0ff..d2ae8619 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -78,7 +78,7 @@ Do not "fix" the second group by adding the guard: that breaks normal installs. Each of the four owns the whole pipeline: **rewrite, then guard the rewritten URL, then connect to it.** A ping therefore reports on the same target the matching fetch would use. Callers never handle the intermediate URL, so the URL that is checked cannot drift away from the URL that is connected to. Keep the guard downstream of every URL transformation: if a rewrite step is ever added, it goes above the guard. -The guard resolves the target host and rejects private, loopback, and link-local addresses via `PRIVATE_IP_RE` (unless `ALLOW_PRIVATE_IPS` is set), then pins the resolved IP so the connection cannot be re-pointed after the check. A blocked request throws `SsrfBlockedError`, which carries `status: 403` so a route's `catch` can forward it. `fetchJSON` itself disables redirect following, enforces a 4 MB response cap, applies per-request timeouts, and decides TLS verification through `shouldSkipTls`. `rewriteUrl` maps the container host IP back to a container name so links that work in the browser also work from inside the network. The `parse-prometheus.js` and `parse-xml.js` modules let widget handlers consume non-JSON upstreams; they hold no network logic, so they live outside this boundary and are tested on their own. +The guard resolves the target host and rejects it with `isPrivateAddress`, which tests the address against the `BLOCKED_IPV4` CIDR table (private, loopback, link-local including cloud metadata, carrier-grade NAT, multicast, reserved and broadcast, benchmarking, and IETF protocol assignments) and `BLOCKED_IPV6_RE` (loopback, unspecified, unique local, link-local, multicast), unwrapping IPv4-mapped IPv6 forms first; `localhost` is refused by name. All of it is skipped when `ALLOW_PRIVATE_IPS` is set. The resolved IP is then pinned so the connection cannot be re-pointed after the check. A blocked request throws `SsrfBlockedError`, which carries `status: 403` so a route's `catch` can forward it. `fetchJSON` itself disables redirect following, enforces a 4 MB response cap, applies per-request timeouts, and decides TLS verification through `shouldSkipTls`. `rewriteUrl` maps the container host IP back to a container name so links that work in the browser also work from inside the network. The `parse-prometheus.js` and `parse-xml.js` modules let widget handlers consume non-JSON upstreams; they hold no network logic, so they live outside this boundary and are tested on their own. ## Summary