fix(ssrf): let .local/.internal hostnames pass to IP-level checks

The pre-DNS hostname block was redundant: any .local/.internal host
that resolves to a private IP is already gated by isPrivateNetwork +
ALLOW_INTERNAL_NETWORK, and any that resolves to loopback/link-local
is caught by isAlwaysBlocked unconditionally.

Dropping the hostname pre-check means Docker/LAN deployments can reach
services on .local hostnames (e.g. immich.local) with
ALLOW_INTERNAL_NETWORK=true, while loopback and link-local IPs
(including 169.254.169.254) remain hard-blocked with no override.

Reverts the isAlwaysBlocked guard loosening from 9a08368.
This commit is contained in:
jubnl
2026-05-03 16:43:39 +02:00
parent 3a57cec992
commit e655a7a4e4
2 changed files with 12 additions and 11 deletions
+1 -5
View File
@@ -66,10 +66,6 @@ export async function checkSsrf(rawUrl: string, bypassInternalIpAllowed: boolean
const hostname = url.hostname.toLowerCase();
if (isInternalHostname(hostname) && hostname !== 'localhost' && !ALLOW_INTERNAL_NETWORK) {
return { allowed: false, isPrivate: false, error: 'Requests to .local/.internal domains are not allowed' };
}
// Resolve hostname to IP
let resolvedIp: string;
try {
@@ -79,7 +75,7 @@ export async function checkSsrf(rawUrl: string, bypassInternalIpAllowed: boolean
return { allowed: false, isPrivate: false, error: 'Could not resolve hostname' };
}
if (isAlwaysBlocked(resolvedIp) && !ALLOW_INTERNAL_NETWORK) {
if (isAlwaysBlocked(resolvedIp)) {
return {
allowed: false,
isPrivate: true,