Files
documenso/packages/lib/server-only/webhooks/is-private-url.ts
T
ephraimduncan 138d663c25 chore: merge main, resolve biome formatting conflicts
Merge origin/main into feat/external-2fa-codes. Resolve formatting
conflicts caused by biome rollout; preserve both feature streams:
PR's external 2FA token + signing-session 2FA proof additions plus
main's RateLimit/RecipientExpired/signingReminders/date-auto-insert.

In complete-document-with-token.ts, drop the duplicate early
field-fetching block introduced when main moved that logic later
with date auto-insert support; keep the EXTERNAL_TWO_FACTOR_AUTH
check using derivedRecipientActionAuth.
2026-05-12 11:46:11 +00:00

83 lines
1.8 KiB
TypeScript

import { z } from 'zod';
const ZIpSchema = z.string().ip();
/**
* Check whether a URL points to a known private/loopback address.
*
* Performs a synchronous check against known private hostnames and IP ranges.
* Works regardless of the URL protocol.
*/
export const isPrivateUrl = (url: string): boolean => {
try {
const parsed = new URL(url);
const hostname = parsed.hostname.toLowerCase();
// Strip IPv6 brackets.
const bare = hostname.startsWith('[') ? hostname.slice(1, -1) : hostname;
const normalizedHost = bare.replace(/\.+$/, '');
if (normalizedHost === 'localhost') {
return true;
}
const parsedIp = ZIpSchema.safeParse(normalizedHost);
if (!parsedIp.success) {
return false;
}
if (normalizedHost === '::1' || normalizedHost === '::') {
return true;
}
if (normalizedHost === '0.0.0.0') {
return true;
}
if (normalizedHost.startsWith('127.')) {
return true;
}
if (normalizedHost.startsWith('10.')) {
return true;
}
if (normalizedHost.startsWith('192.168.')) {
return true;
}
if (normalizedHost.startsWith('169.254.')) {
return true;
}
if (normalizedHost.startsWith('fe80:')) {
return true;
}
if (normalizedHost.startsWith('fc') || normalizedHost.startsWith('fd')) {
return true;
}
// 172.16.0.0/12
if (normalizedHost.startsWith('172.')) {
const second = parseInt(normalizedHost.split('.')[1], 10);
if (second >= 16 && second <= 31) {
return true;
}
}
// IPv4-mapped IPv6 (e.g. ::ffff:127.0.0.1)
const v4Mapped = normalizedHost.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/i);
if (v4Mapped) {
return isPrivateUrl(`http://${v4Mapped[1]}`);
}
return false;
} catch {
return false;
}
};