diff --git a/docker/Dockerfile.health_check b/docker/Dockerfile.health_check index 6c18201e688..e968bec340e 100644 --- a/docker/Dockerfile.health_check +++ b/docker/Dockerfile.health_check @@ -18,7 +18,7 @@ USER appuser # Health check HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ - CMD ["python", "-c", "import sys; sys.exit(0)"] + CMD ["python", "/app/health_check_client.py", "--help"] # Set entrypoint ENTRYPOINT ["python", "/app/health_check_client.py"] diff --git a/ui/litellm-dashboard/src/app/mcp/oauth/callback/page.tsx b/ui/litellm-dashboard/src/app/mcp/oauth/callback/page.tsx index 5c27a1d6150..0539d6d8f19 100644 --- a/ui/litellm-dashboard/src/app/mcp/oauth/callback/page.tsx +++ b/ui/litellm-dashboard/src/app/mcp/oauth/callback/page.tsx @@ -60,7 +60,17 @@ const McpOAuthCallbackContent = () => { } const returnUrl = getSecureItem(RETURN_URL_STORAGE_KEY); - const destination = returnUrl || resolveDefaultRedirect(); + let destination = resolveDefaultRedirect(); + if (returnUrl) { + try { + const parsed = new URL(returnUrl, window.location.origin); + if (parsed.origin === window.location.origin) { + destination = parsed.href; + } + } catch { + // invalid URL — fall through to default + } + } window.location.replace(destination); }, [payload]); diff --git a/ui/litellm-dashboard/src/utils/secureStorage.ts b/ui/litellm-dashboard/src/utils/secureStorage.ts index 6b9a9bc1013..6942368bcf2 100644 --- a/ui/litellm-dashboard/src/utils/secureStorage.ts +++ b/ui/litellm-dashboard/src/utils/secureStorage.ts @@ -1,10 +1,20 @@ function encode(value: string): string { // btoa cannot handle characters outside Latin-1, so we percent-encode first. - return btoa(unescape(encodeURIComponent(value))); + return btoa( + encodeURIComponent(value).replace( + /%([0-9A-F]{2})/g, + (_, p1) => String.fromCharCode(parseInt(p1, 16)) + ) + ); } function decode(encoded: string): string { - return decodeURIComponent(escape(atob(encoded))); + return decodeURIComponent( + atob(encoded) + .split("") + .map((c) => "%" + c.charCodeAt(0).toString(16).padStart(2, "0")) + .join("") + ); } export function setSecureItem(key: string, value: string): void { @@ -21,12 +31,8 @@ export function getSecureItem(key: string): string | null { if (raw === null) return null; return decode(raw); } catch { - // Corrupted or non-encoded legacy value — clear it. - try { - window.sessionStorage.removeItem(key); - } catch { - // ignore - } + // Corrupted or non-encoded legacy value — return null without deleting + // so that in-flight flows (e.g. OAuth) can time out naturally. return null; } }