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>
43 lines
1.0 KiB
TypeScript
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;
|
|
}
|