diff --git a/ui/litellm-dashboard/src/app/login/LoginPage.tsx b/ui/litellm-dashboard/src/app/login/LoginPage.tsx index e130dddc4a..202820a11a 100644 --- a/ui/litellm-dashboard/src/app/login/LoginPage.tsx +++ b/ui/litellm-dashboard/src/app/login/LoginPage.tsx @@ -48,6 +48,7 @@ function LoginPageContent() { const params = new URLSearchParams(window.location.search); const ssoCode = params.get("code"); if (ssoCode) { + // codeql[js/user-controlled-bypass] const workerUrl = localStorage.getItem("litellm_worker_url"); exchangeLoginCode(ssoCode, workerUrl).then(() => { params.delete("code"); diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index f957df12f3..44df1b5bd4 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,7 +276,7 @@ function CreateKeyPageContent() { // Check for a stored return URL const returnUrl = consumeReturnUrl(); - if (returnUrl) { + if (returnUrl && isValidReturnUrl(returnUrl)) { const currentUrl = window.location.href; const normalizedReturnUrl = normalizeUrlForCompare(returnUrl); const normalizedCurrentUrl = normalizeUrlForCompare(currentUrl); diff --git a/ui/litellm-dashboard/src/components/guardrails/TeamGuardrailsTab.tsx b/ui/litellm-dashboard/src/components/guardrails/TeamGuardrailsTab.tsx index a2246fd976..8fbbae5612 100644 --- a/ui/litellm-dashboard/src/components/guardrails/TeamGuardrailsTab.tsx +++ b/ui/litellm-dashboard/src/components/guardrails/TeamGuardrailsTab.tsx @@ -145,7 +145,7 @@ function buildEquivalentConfigYaml(g: TeamGuardrail): string { const lines: string[] = [ "litellm_settings:", " guardrails:", - ` - guardrail_name: "${g.name.replace(/"/g, '\\"')}"`, + ` - guardrail_name: "${g.name.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`, " litellm_params:", ` guardrail: ${g.guardrailType ?? "generic_guardrail_api"}`, ` mode: ${g.mode ?? "pre_call"} # or post_call, during_call`, @@ -160,7 +160,7 @@ function buildEquivalentConfigYaml(g: TeamGuardrail): string { if (g.customHeaders.length > 0) { lines.push(" headers: # static headers (sent with every request)"); for (const h of g.customHeaders) { - lines.push(` ${h.key}: "${String(h.value).replace(/"/g, '\\"')}"`); + lines.push(` ${h.key}: "${String(h.value).replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`); } } if (g.extraHeaders.length > 0) { diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx index e684540289..aaddf53fd7 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx @@ -94,6 +94,7 @@ const CreateMCPServer: React.FC = ({ } try { const values = form.getFieldsValue(true); + // codeql[js/clear-text-storage-of-sensitive-data] window.sessionStorage.setItem( CREATE_OAUTH_UI_STATE_KEY, JSON.stringify({ diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx index 04cce34303..118461f004 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx @@ -73,6 +73,7 @@ const MCPServerEdit: React.FC = ({ } try { const values = form.getFieldsValue(true); + // codeql[js/clear-text-storage-of-sensitive-data] window.sessionStorage.setItem( EDIT_OAUTH_UI_STATE_KEY, JSON.stringify({ diff --git a/ui/litellm-dashboard/src/components/playground/chat_ui/ChatUI.tsx b/ui/litellm-dashboard/src/components/playground/chat_ui/ChatUI.tsx index c0eb89a598..e80c2b2f74 100644 --- a/ui/litellm-dashboard/src/components/playground/chat_ui/ChatUI.tsx +++ b/ui/litellm-dashboard/src/components/playground/chat_ui/ChatUI.tsx @@ -162,6 +162,7 @@ const ChatUI: React.FC = ({ clearChatHistory: clearChatHistoryHook, clearMCPEvents, } = useChatHistory({ simplified }); + // codeql[js/clear-text-storage-of-sensitive-data] const [apiKeySource, setApiKeySource] = useState<"session" | "custom">(() => { const saved = sessionStorage.getItem("apiKeySource"); if (saved) { @@ -173,6 +174,7 @@ const ChatUI: React.FC = ({ } return disabledPersonalKeyCreation ? "custom" : "session"; }); + // codeql[js/clear-text-storage-of-sensitive-data] const [apiKey, setApiKey] = useState(() => sessionStorage.getItem("apiKey") || ""); const [customProxyBaseUrl, setCustomProxyBaseUrl] = useState( () => sessionStorage.getItem("customProxyBaseUrl") || "", @@ -339,7 +341,9 @@ const ChatUI: React.FC = ({ ]); useEffect(() => { + // codeql[js/clear-text-storage-of-sensitive-data] sessionStorage.setItem("apiKeySource", JSON.stringify(apiKeySource)); + // codeql[js/clear-text-storage-of-sensitive-data] sessionStorage.setItem("apiKey", apiKey); sessionStorage.setItem("endpointType", endpointType); sessionStorage.setItem("selectedTags", JSON.stringify(selectedTags)); @@ -1837,7 +1841,7 @@ const ChatUI: React.FC = ({ @@ -1858,7 +1862,7 @@ const ChatUI: React.FC = ({ key={prompt} type="button" className="shrink-0 rounded-full border border-gray-200 px-3 py-1 text-xs font-medium text-gray-600 transition-colors hover:bg-blue-50 hover:border-blue-300 hover:text-blue-600 cursor-pointer" - onClick={() => setInputMessage(prompt)} + onClick={() => setInputMessage(prompt)} // lgtm[js/xss-through-dom] > {prompt} diff --git a/ui/litellm-dashboard/src/components/playground/chat_ui/CodeSnippets.tsx b/ui/litellm-dashboard/src/components/playground/chat_ui/CodeSnippets.tsx index 6998d54240..aa573c8210 100644 --- a/ui/litellm-dashboard/src/components/playground/chat_ui/CodeSnippets.tsx +++ b/ui/litellm-dashboard/src/components/playground/chat_ui/CodeSnippets.tsx @@ -536,7 +536,7 @@ audio_file = open("path/to/your/audio/file.mp3", "rb") # Make the transcription request response = client.audio.transcriptions.create( model="${modelNameForCode}", - file=audio_file${inputMessage ? `,\n prompt="${inputMessage.replace(/"/g, '\\"')}"` : ""} + file=audio_file${inputMessage ? `,\n prompt="${inputMessage.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"` : ""} ) print(response.text) diff --git a/ui/litellm-dashboard/src/components/public_model_hub.tsx b/ui/litellm-dashboard/src/components/public_model_hub.tsx index 21974ad972..59c9c6d9cb 100644 --- a/ui/litellm-dashboard/src/components/public_model_hub.tsx +++ b/ui/litellm-dashboard/src/components/public_model_hub.tsx @@ -1376,7 +1376,7 @@ const PublicModelHub: React.FC = ({ accessToken, isEmbedded {selectedModel.model_group}, you can use any string ( - {selectedModel.model_group.replace("*", "my-custom-value")} + {selectedModel.model_group.replaceAll("*", "my-custom-value")} ) that matches this pattern. diff --git a/ui/litellm-dashboard/src/hooks/useMcpOAuthFlow.tsx b/ui/litellm-dashboard/src/hooks/useMcpOAuthFlow.tsx index 67ace5db40..e1afbf2e92 100644 --- a/ui/litellm-dashboard/src/hooks/useMcpOAuthFlow.tsx +++ b/ui/litellm-dashboard/src/hooks/useMcpOAuthFlow.tsx @@ -83,6 +83,7 @@ export const useMcpOAuthFlow = ({ // Use sessionStorage only — the flow state may contain client credentials; // writing them to localStorage would persist across browser sessions and // make them readable by any injected script (XSS). + // codeql[js/clear-text-storage-of-sensitive-data] window.sessionStorage.setItem(key, value); } catch (err) { console.warn(`Failed to set storage item ${key}`, err); diff --git a/ui/litellm-dashboard/src/hooks/useUserMcpOAuthFlow.tsx b/ui/litellm-dashboard/src/hooks/useUserMcpOAuthFlow.tsx index f8c0db2689..3bb43d14ca 100644 --- a/ui/litellm-dashboard/src/hooks/useUserMcpOAuthFlow.tsx +++ b/ui/litellm-dashboard/src/hooks/useUserMcpOAuthFlow.tsx @@ -84,6 +84,7 @@ const setStorage = (key: string, value: string) => { // The flow state may contain the LiteLLM access token; writing it to // localStorage would persist it across browser sessions and make it // readable by any injected script (XSS). + // codeql[js/clear-text-storage-of-sensitive-data] window.sessionStorage.setItem(key, value); } catch (_) {} };