fix(static-assets): stop serving stale logo cache

This commit is contained in:
user 2026-04-30 11:34:25 -07:00
parent 215f538d4f
commit b8a141cefd
2 changed files with 10 additions and 27 deletions

View File

@ -12229,7 +12229,7 @@ def get_logo_url():
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
which serves the file only when it is a supported image. Without
this filter, the unauthenticated endpoint would disclose internal
hostnames or filesystem paths to any caller.
"""
@ -12275,9 +12275,6 @@ async def get_image():
if assets_dir != current_dir and not os.path.exists(default_logo):
default_logo = default_site_logo
cache_dir = assets_dir if os.access(assets_dir, os.W_OK) else current_dir
cache_path = os.path.join(cache_dir, "cached_logo.jpg")
logo_path = os.getenv("UI_LOGO_PATH", default_logo)
verbose_proxy_logger.debug("Reading logo from path: %s", logo_path)
@ -12302,19 +12299,6 @@ async def get_image():
if logo_path.startswith(("http://", "https://")):
return RedirectResponse(url=logo_path)
# [OPTIMIZATION] For default logo, check if the cached image exists.
# Validate the cache before serving so stale pre-fix cache files cannot
# keep exposing non-image responses fetched before this hardening.
if os.path.exists(cache_path):
safe_cache = resolve_validated_local_image_path(cache_path)
if safe_cache is not None:
safe_cache_path, media_type = safe_cache
return FileResponse(safe_cache_path, media_type=media_type)
verbose_proxy_logger.warning(
"Ignoring cached logo at %s because it is not a supported image file",
cache_path,
)
# Default logo (resolved from the bundled asset, not user-controlled).
safe_logo = resolve_validated_local_image_path(logo_path)
if safe_logo is not None:

View File

@ -4073,10 +4073,10 @@ async def test_get_image_custom_local_logo_bypasses_cache(monkeypatch, tmp_path)
@pytest.mark.asyncio
async def test_get_image_default_logo_still_uses_cache(monkeypatch, tmp_path):
async def test_get_image_default_logo_ignores_stale_cache(monkeypatch, tmp_path):
"""
Test that when UI_LOGO_PATH is NOT set (default logo), the cache
optimization still works cached_logo.jpg is returned if it exists.
Test that when UI_LOGO_PATH is NOT set, stale pre-fix cached_logo.jpg
files are ignored and the default logo is served.
"""
from unittest.mock import patch
@ -4105,7 +4105,8 @@ async def test_get_image_default_logo_still_uses_cache(monkeypatch, tmp_path):
len(calls_to_file_response) == 1
), "FileResponse should be called exactly once"
served_path = calls_to_file_response[0]
assert served_path == str(cache_path.resolve())
assert served_path != str(cache_path.resolve())
assert served_path.endswith("logo.jpg")
@pytest.mark.asyncio
@ -4114,14 +4115,12 @@ async def test_get_image_custom_logo_missing_falls_through_to_default(
):
"""
Test that when UI_LOGO_PATH points to a non-existent local file,
get_image falls through to the cache/default logo instead of failing.
get_image falls through to the default logo instead of failing.
"""
from unittest.mock import patch
from litellm.proxy.proxy_server import get_image
cache_path = tmp_path / "cached_logo.jpg"
cache_path.write_bytes(b"\xff\xd8\xff cached logo")
custom_logo_path = tmp_path / "nonexistent_logo.jpg"
monkeypatch.setenv("UI_LOGO_PATH", str(custom_logo_path))
monkeypatch.delenv("LITELLM_NON_ROOT", raising=False)
@ -4147,7 +4146,7 @@ async def test_get_image_custom_logo_missing_falls_through_to_default(
assert served_path != str(
custom_logo_path
), "Should not attempt to serve a non-existent custom logo"
assert served_path == str(cache_path.resolve())
assert served_path.endswith("logo.jpg")
@pytest.mark.asyncio
@ -4156,8 +4155,8 @@ async def test_get_image_custom_logo_missing_no_cache_serves_default(
):
"""
Test that when UI_LOGO_PATH points to a non-existent file AND there is no
cached_logo.jpg, get_image serves the default logo instead of the
non-existent custom path.
cached_logo.jpg, get_image serves the default logo instead of the non-existent
custom path.
"""
from unittest.mock import patch