Files
slate/app/utils/url.ts
Stijnus 2e254ac19a feat: add web URL content fetcher for chat context
Add ability to fetch and inject web page content into chat as context.
Includes SSRF protection (blocks private IPs, localhost), content
extraction (strips scripts/styles/nav), and a clean popover UI.

Reimplements the concept from PR #1703 without the issues (duplicated
ChatBox, dual API routes, SSRF vulnerability, window.prompt UX).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 15:36:22 +01:00

43 lines
1.0 KiB
TypeScript

/**
* URL validation utilities with SSRF protection.
*/
const PRIVATE_IP_PATTERNS = [
/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/, // Loopback
/^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$/, // Class A private
/^172\.(1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}$/, // Class B private
/^192\.168\.\d{1,3}\.\d{1,3}$/, // Class C private
/^169\.254\.\d{1,3}\.\d{1,3}$/, // Link-local
/^0\.0\.0\.0$/, // Unspecified
];
const BLOCKED_HOSTNAMES = new Set(['localhost', '[::1]', '0.0.0.0']);
export function isValidUrl(input: string): boolean {
try {
const url = new URL(input);
return url.protocol === 'http:' || url.protocol === 'https:';
} catch {
return false;
}
}
export function isAllowedUrl(input: string): boolean {
if (!isValidUrl(input)) {
return false;
}
const url = new URL(input);
const hostname = url.hostname.toLowerCase();
if (BLOCKED_HOSTNAMES.has(hostname)) {
return false;
}
if (PRIVATE_IP_PATTERNS.some((pattern) => pattern.test(hostname))) {
return false;
}
return true;
}