( originalUrl: string, redirectUrl: string, )
| 210 | * - Or both of the above |
| 211 | */ |
| 212 | export function isPermittedRedirect( |
| 213 | originalUrl: string, |
| 214 | redirectUrl: string, |
| 215 | ): boolean { |
| 216 | try { |
| 217 | const parsedOriginal = new URL(originalUrl) |
| 218 | const parsedRedirect = new URL(redirectUrl) |
| 219 | |
| 220 | if (parsedRedirect.protocol !== parsedOriginal.protocol) { |
| 221 | return false |
| 222 | } |
| 223 | |
| 224 | if (parsedRedirect.port !== parsedOriginal.port) { |
| 225 | return false |
| 226 | } |
| 227 | |
| 228 | if (parsedRedirect.username || parsedRedirect.password) { |
| 229 | return false |
| 230 | } |
| 231 | |
| 232 | // Now check hostname conditions |
| 233 | // 1. Adding www. is allowed: example.com -> www.example.com |
| 234 | // 2. Removing www. is allowed: www.example.com -> example.com |
| 235 | // 3. Same host (with or without www.) is allowed: paths can change |
| 236 | const stripWww = (hostname: string) => hostname.replace(/^www\./, '') |
| 237 | const originalHostWithoutWww = stripWww(parsedOriginal.hostname) |
| 238 | const redirectHostWithoutWww = stripWww(parsedRedirect.hostname) |
| 239 | return originalHostWithoutWww === redirectHostWithoutWww |
| 240 | } catch (_error) { |
| 241 | return false |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | /** |
| 246 | * Helper function to handle fetching URLs with custom redirect handling |
nothing calls this directly
no test coverage detected