Summary
There are multiple SSRF validation implementations with different behavior. The shared SSRF guard permits http: and has an incomplete non-global IP blocklist. OIDC provider validation uses that guard even though the error message says HTTPS is required. The outbound webhook handler has its own separate private-IP regex list, misses several non-global ranges, and validates DNS but still fetches the original hostname.
Evidence
- apps/web/src/lib/server/content/ssrf-guard.ts:23-33 allows both http: and https:.
- apps/web/src/lib/server/content/ssrf-guard.ts:117-129 blocks only a subset of IPv4 non-public ranges.
- apps/web/src/lib/server/domains/settings/identity-providers.service.ts:282-307 uses checkUrlSafety for OIDC URLs and reports “valid https:// URL” even though http: passes the scheme check.
- apps/web/src/lib/server/events/handlers/webhook.ts:30-47 defines a separate incomplete private-IP regex list.
- apps/web/src/lib/server/events/handlers/webhook.ts:111-116 validates the resolved IP, but - apps/web/src/lib/server/events/handlers/webhook.ts:146-152 fetches the original URL, leaving a DNS rebinding window.
Impact
Sensitive server-side fetches can be routed to non-HTTPS endpoints or non-public network addresses that are not covered by the current blocklists. Outbound webhooks also rely on a validation/fetch split instead of connecting to the validated IP.
Recommended fix
Create one centralized SSRF policy and use it across OIDC, unfurling, outbound webhooks, and integration callbacks. Sensitive endpoints such as OIDC discovery, token, userinfo, and JWKS should require HTTPS. The guard should block all non-global IP ranges, including documentation, benchmarking, multicast, reserved, link-local, loopback, private, carrier-grade NAT, and IPv4-mapped IPv6 forms. Fetches should connect to the pinned validated IP and should not follow redirects to unvalidated hosts.
Acceptance criteria
- OIDC provider URLs require https://.
- All server-side URL fetchers use the same SSRF guard or an explicit policy wrapper.
- The guard blocks all non-global IPv4 and IPv6 ranges.
- Outbound webhooks connect to a validated pinned IP or use an equivalent TOCTOU-safe fetch path.
- Tests cover 127.0.0.1, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 100.64.0.0/10, 169.254.169.254, 192.0.0.0/24, 198.18.0.0/15, multicast, reserved ranges, IPv6 loopback, IPv6 link-local, IPv6 ULA, IPv6 multicast, and IPv4-mapped IPv6.
Summary
There are multiple SSRF validation implementations with different behavior. The shared SSRF guard permits http: and has an incomplete non-global IP blocklist. OIDC provider validation uses that guard even though the error message says HTTPS is required. The outbound webhook handler has its own separate private-IP regex list, misses several non-global ranges, and validates DNS but still fetches the original hostname.
Evidence
Impact
Sensitive server-side fetches can be routed to non-HTTPS endpoints or non-public network addresses that are not covered by the current blocklists. Outbound webhooks also rely on a validation/fetch split instead of connecting to the validated IP.
Recommended fix
Create one centralized SSRF policy and use it across OIDC, unfurling, outbound webhooks, and integration callbacks. Sensitive endpoints such as OIDC discovery, token, userinfo, and JWKS should require HTTPS. The guard should block all non-global IP ranges, including documentation, benchmarking, multicast, reserved, link-local, loopback, private, carrier-grade NAT, and IPv4-mapped IPv6 forms. Fetches should connect to the pinned validated IP and should not follow redirects to unvalidated hosts.
Acceptance criteria