From 9ef8572d6701ac5bcc7cdb53a4b3810dfc712471 Mon Sep 17 00:00:00 2001 From: user <70670632+stuxf@users.noreply.github.com> Date: Wed, 29 Apr 2026 21:15:21 +0000 Subject: [PATCH] fix(proxy): /get_logo_url no longer discloses local UI_LOGO_PATH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The unauthenticated ``/get_logo_url`` endpoint returned the ``UI_LOGO_PATH`` env var verbatim. For HTTP(S) URLs this is intended — the dashboard loads the logo directly from a public/internal CDN. For local filesystem paths it was an information disclosure: any caller could fetch ``/get_logo_url`` and read admin-only filesystem details like ``UI_LOGO_PATH=/etc/litellm/secret-config.json``. Now the endpoint returns the URL only when it begins with ``http://`` or ``https://``. For local paths (or unset) it returns an empty string — the dashboard falls back to ``/get_image`` which serves the file via the path-containment guard added in the previous commit. Tests parametrize the disclosure-blocked cases (``/etc/...``, ``/proc/self/environ``, relative paths) and confirm HTTP / HTTPS URLs still pass through unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) --- litellm/proxy/proxy_server.py | 15 +++++- tests/test_litellm/proxy/test_proxy_server.py | 54 +++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index bbd528072f..ead8e8f6b9 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -12223,9 +12223,20 @@ async def claim_onboarding_link(data: InvitationClaim): @app.get("/get_logo_url", include_in_schema=False) def get_logo_url(): - """Get the current logo URL from environment""" + """Get the current logo URL from environment. + + Only HTTP(S) URLs are returned — those are intended to be loaded + directly by the browser from a public/internal CDN. Local file + paths set via ``UI_LOGO_PATH`` are NOT returned: they are admin- + only filesystem details, the dashboard falls back to ``/get_image`` + which serves the file (with path containment) instead. Without + this filter, the unauthenticated endpoint would disclose internal + hostnames or filesystem paths to any caller. + """ logo_path = os.getenv("UI_LOGO_PATH", "") - return {"logo_url": logo_path} + if logo_path.startswith(("http://", "https://")): + return {"logo_url": logo_path} + return {"logo_url": ""} @app.get("/get_image", include_in_schema=False) diff --git a/tests/test_litellm/proxy/test_proxy_server.py b/tests/test_litellm/proxy/test_proxy_server.py index 1f4f82a64e..da3f963f9d 100644 --- a/tests/test_litellm/proxy/test_proxy_server.py +++ b/tests/test_litellm/proxy/test_proxy_server.py @@ -457,6 +457,60 @@ def test_fallback_login_has_no_deprecation_banner(client_no_auth): assert "