From 473118d88db7d50e161fcaf41898e6d8d5636587 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Mon, 23 Mar 2026 22:29:35 -0700 Subject: [PATCH] fix(ui): validate return URL before redirect to prevent open redirect Port security fix from litellm_security_fixes_v1.82.3: use isValidReturnUrl() guard and reconstruct a safe path from parsed URL components before calling window.location.replace(). Co-Authored-By: Claude Opus 4.6 (1M context) --- ui/litellm-dashboard/src/app/page.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index f957df12f35..b5fb18b1562 100644 --- a/ui/litellm-dashboard/src/app/page.tsx +++ b/ui/litellm-dashboard/src/app/page.tsx @@ -43,7 +43,7 @@ import SpendLogsTable from "@/components/view_logs"; import ViewUserDashboard from "@/components/view_users"; import { ThemeProvider } from "@/contexts/ThemeContext"; import { isJwtExpired } from "@/utils/jwtUtils"; -import { buildLoginUrlWithReturn, consumeReturnUrl, normalizeUrlForCompare, storeReturnUrl } from "@/utils/returnUrlUtils"; +import { buildLoginUrlWithReturn, consumeReturnUrl, isValidReturnUrl, normalizeUrlForCompare, storeReturnUrl } from "@/utils/returnUrlUtils"; import { formatUserRole, isAdminRole } from "@/utils/roles"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { jwtDecode } from "jwt-decode"; @@ -276,14 +276,15 @@ function CreateKeyPageContent() { // Check for a stored return URL const returnUrl = consumeReturnUrl(); - if (returnUrl) { + // Guard at redirect site so static analysis can verify the validation + if (returnUrl && isValidReturnUrl(returnUrl)) { + const parsed = new URL(returnUrl, window.location.origin); + const safePath = parsed.pathname + parsed.search + parsed.hash; const currentUrl = window.location.href; - const normalizedReturnUrl = normalizeUrlForCompare(returnUrl); + const normalizedReturnUrl = normalizeUrlForCompare(safePath); const normalizedCurrentUrl = normalizeUrlForCompare(currentUrl); - // Only redirect if the return URL is different from the current URL - // This prevents infinite redirect loops if (normalizedReturnUrl !== normalizedCurrentUrl) { - window.location.replace(returnUrl); + window.location.replace(safePath); } } }, [authLoading, token]);