/** * Validates a redirect URL to prevent open redirect vulnerabilities. * * Only allows same-origin redirects (paths starting with `/` but not `//`). * External URLs, protocol-relative URLs, and invalid input are rejected. * * @param redirect - The redirect URL to validate (typically from query parameters) * @param fallback - The fallback path to use if validation fails (default: '/dashboard') * @returns A validated same-origin path or the fallback path * * @example * ```typescript * // Valid same-origin paths * validateRedirect('/dashboard') // returns '/dashboard' * validateRedirect('/projects/123') // returns '/projects/123' * * // Rejected external URLs (returns fallback) * validateRedirect('https://evil.com') // returns '/dashboard' * validateRedirect('//evil.com') // returns '/dashboard' * * // Invalid input (returns fallback) * validateRedirect(null) // returns '/dashboard' * validateRedirect(undefined) // returns '/dashboard' * * // Custom fallback * validateRedirect('https://evil.com', '/login') // returns '/login' * ``` */ export const validateRedirect = (redirect: string | unknown, fallback = '/dashboard'): string => { if (typeof redirect !== 'string') { return fallback; } if (redirect.startsWith('/') && !redirect.startsWith('//')) { return redirect; } return fallback; }