fix: add SSRF protection to validate-model endpoint (#357)

Block private IPs, localhost, cloud metadata endpoints (169.254.169.254),
and internal hostnames in custom baseUrl parameter to prevent server-side
request forgery attacks.
This commit is contained in:
Dayuan Jiang
2025-12-23 00:26:01 +09:00
committed by GitHub
parent 84959637db
commit 6446454cd7

View File

@@ -11,6 +11,66 @@ import { createOllama } from "ollama-ai-provider-v2"
export const runtime = "nodejs" export const runtime = "nodejs"
/**
* SECURITY: Check if URL points to private/internal network (SSRF protection)
* Blocks: localhost, private IPs, link-local, AWS metadata service
*/
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)
// 10.0.0.0/8
if (a === 10) return true
// 172.16.0.0/12
if (a === 172 && b >= 16 && b <= 31) return true
// 192.168.0.0/16
if (a === 192 && b === 168) return true
// 169.254.0.0/16 (link-local)
if (a === 169 && b === 254) return true
// 127.0.0.0/8 (loopback)
if (a === 127) return true
}
// Block common internal hostnames
if (
hostname.endsWith(".local") ||
hostname.endsWith(".internal") ||
hostname.endsWith(".localhost")
) {
return true
}
return false
} catch {
// Invalid URL - block it
return true
}
}
interface ValidateRequest { interface ValidateRequest {
provider: string provider: string
apiKey: string apiKey: string
@@ -42,6 +102,14 @@ export async function POST(req: Request) {
) )
} }
// SECURITY: Block SSRF attacks via custom baseUrl
if (baseUrl && isPrivateUrl(baseUrl)) {
return NextResponse.json(
{ valid: false, error: "Invalid base URL" },
{ status: 400 },
)
}
// Validate credentials based on provider // Validate credentials based on provider
if (provider === "bedrock") { if (provider === "bedrock") {
if (!awsAccessKeyId || !awsSecretAccessKey || !awsRegion) { if (!awsAccessKeyId || !awsSecretAccessKey || !awsRegion) {