52 lines
1.3 KiB
TypeScript
52 lines
1.3 KiB
TypeScript
/**
|
|
* Safe navigation utility to prevent open redirect vulnerabilities.
|
|
* Validates that a URL is same-origin or in an allowed list before navigating.
|
|
*
|
|
* @see S0.1 of the UI Remediation Plan
|
|
*/
|
|
|
|
import { logger } from './logger';
|
|
|
|
const ALLOWED_ORIGINS: string[] = [
|
|
// Add trusted external origins here if needed
|
|
];
|
|
|
|
/**
|
|
* Validates that a URL is safe to navigate to (same-origin or allowlisted).
|
|
*/
|
|
export function isSafeUrl(url: string): boolean {
|
|
try {
|
|
const parsed = new URL(url, window.location.origin);
|
|
|
|
// Allow same-origin URLs
|
|
if (parsed.origin === window.location.origin) {
|
|
return true;
|
|
}
|
|
|
|
// Allow explicitly allowlisted origins
|
|
if (ALLOWED_ORIGINS.includes(parsed.origin)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
} catch {
|
|
// If URL parsing fails, it's not safe
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Navigate to a URL only if it passes the same-origin or allowlist check.
|
|
* Falls back to the app root if the URL is not safe.
|
|
*
|
|
* @param url - The URL to navigate to
|
|
* @param fallback - Fallback path if URL is unsafe (default: '/')
|
|
*/
|
|
export function safeNavigate(url: string, fallback = '/'): void {
|
|
if (isSafeUrl(url)) {
|
|
window.location.href = url;
|
|
} else {
|
|
logger.warn(`[safeNavigate] Blocked navigation to untrusted URL: ${url}`);
|
|
window.location.href = fallback;
|
|
}
|
|
}
|