diff --git a/docker/Dockerfile.non_root b/docker/Dockerfile.non_root index f15d6cb7cab..f20ea6554f8 100644 --- a/docker/Dockerfile.non_root +++ b/docker/Dockerfile.non_root @@ -53,7 +53,7 @@ RUN mkdir -p /var/lib/litellm/ui && \ mkdir -p /var/lib/litellm/assets && \ cp /app/litellm/proxy/logo.jpg /var/lib/litellm/assets/logo.jpg && \ ( cd /var/lib/litellm/ui && \ - find . -name "*.html" ! -name "index.html" | while IFS= read -r html_file; do \ + find . -path './_next' -prune -o -name "*.html" ! -name "index.html" -print | while IFS= read -r html_file; do \ folder_name="${html_file%.html}" && \ mkdir -p "$folder_name" && \ mv "$html_file" "$folder_name/index.html"; \ diff --git a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py index 2b36577f2d9..a6e7425cb53 100644 --- a/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py @@ -507,6 +507,11 @@ def _build_mcp_oauth_callback_html(code: Optional[str], state: Optional[str]) -> window.localStorage.getItem(RETURN_URL_KEY); }} catch (e) {{}} + // Validate returnUrl: reject absolute URLs (open redirect protection) + if (returnUrl && (returnUrl.startsWith('//') || /^[a-zA-Z][a-zA-Z0-9+\-.]*:/.test(returnUrl))) {{ + returnUrl = null; + }} + if (!returnUrl) {{ var path = window.location.pathname || ""; var uiIndex = path.indexOf("/ui");