Files
next-ai-draw-io/lib/ssrf-protection.ts
Dayuan Jiang 4ace31d412 fix: allow private URLs by default for reverse proxy setups (#600)
* fix: allow private URLs by default for reverse proxy setups

Fixes #588 - Users with reverse proxy setups (e.g., Antigravity tools)
were getting "Invalid base URL" errors due to SSRF protection blocking
private/internal URLs.

Changes:
- Add ALLOW_PRIVATE_URLS env var (defaults to true)
- Set to "false" to enable strict SSRF protection if needed

* refactor: extract isPrivateUrl to shared utility
2026-01-17 23:14:53 +09:00

64 lines
1.8 KiB
TypeScript

/**
* SSRF (Server-Side Request Forgery) protection utilities
*/
/**
* Check if URL points to private/internal network
* Blocks: localhost, private IPs, link-local, AWS metadata service
*/
export function isPrivateUrl(urlString: string): boolean {
try {
const url = new URL(urlString)
const hostname = url.hostname.toLowerCase()
// Block localhost
if (
hostname === "localhost" ||
hostname === "127.0.0.1" ||
hostname === "::1"
) {
return true
}
// Block AWS/cloud metadata endpoints
if (
hostname === "169.254.169.254" ||
hostname === "metadata.google.internal"
) {
return true
}
// Check for private IPv4 ranges
const ipv4Match = hostname.match(
/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/,
)
if (ipv4Match) {
const [, a, b] = ipv4Match.map(Number)
if (a === 10) return true // 10.0.0.0/8
if (a === 172 && b >= 16 && b <= 31) return true // 172.16.0.0/12
if (a === 192 && b === 168) return true // 192.168.0.0/16
if (a === 169 && b === 254) return true // 169.254.0.0/16 (link-local)
if (a === 127) return true // 127.0.0.0/8 (loopback)
}
// Block common internal hostnames
if (
hostname.endsWith(".local") ||
hostname.endsWith(".internal") ||
hostname.endsWith(".localhost")
) {
return true
}
return false
} catch {
return true // Invalid URL - block it
}
}
/**
* Whether private URLs are allowed (defaults to true)
* Set ALLOW_PRIVATE_URLS=false to block private URLs
*/
export const allowPrivateUrls = process.env.ALLOW_PRIVATE_URLS !== "false"