diff --git a/ui/litellm-dashboard/src/components/molecules/logo/Logo.test.tsx b/ui/litellm-dashboard/src/components/molecules/logo/Logo.test.tsx
index 62ce8e9ee12..a8e7b4e41d4 100644
--- a/ui/litellm-dashboard/src/components/molecules/logo/Logo.test.tsx
+++ b/ui/litellm-dashboard/src/components/molecules/logo/Logo.test.tsx
@@ -81,6 +81,11 @@ describe("Logo", () => {
expect(screen.getByRole("img", { name: "Ext logo" })).toHaveClass("w-5 h-5", { exact: true });
});
+ it("does not treat a user-supplied logo URL that mimics the bundled asset path", () => {
+ render();
+ expect(screen.getByRole("img", { name: "Ext logo" })).toHaveClass("w-5 h-5", { exact: true });
+ });
+
it("applies the treatment to a provider logo resolved through the bundler", () => {
render();
expect(screen.getByRole("img", { name: "openrouter logo" })).toHaveClass("dark:[filter:brightness(0)_invert(1)]");
diff --git a/ui/litellm-dashboard/src/lib/assetPaths.ts b/ui/litellm-dashboard/src/lib/assetPaths.ts
index abce69127cb..af208e7d78c 100644
--- a/ui/litellm-dashboard/src/lib/assetPaths.ts
+++ b/ui/litellm-dashboard/src/lib/assetPaths.ts
@@ -3,6 +3,8 @@ import { normalizeRootPath } from "@/lib/http/resolveApiBase";
const EXTERNAL_SRC = /^(https?:|data:|blob:|\/\/)/i;
+export const isExternalAssetSrc = (value: string): boolean => EXTERNAL_SRC.test(value);
+
/**
* Prefix a root-relative asset path (e.g. "/ui/assets/logos/openai.svg") with the
* proxy's server root path so it resolves when the UI is mounted under a sub-path
@@ -22,7 +24,7 @@ export const withServerRoot = (path: string, root: string): string => {
*/
export const resolveLogoSrc = (value: string | null | undefined, root: string = serverRootPath): string | undefined => {
if (!value) return undefined;
- if (EXTERNAL_SRC.test(value)) return value;
+ if (isExternalAssetSrc(value)) return value;
if (value.includes("/_next/static/")) return value;
const prefix = normalizeRootPath(root);
if (prefix && (value === prefix || value.startsWith(`${prefix}/`))) return value;
diff --git a/ui/litellm-dashboard/src/lib/logoTreatments.test.ts b/ui/litellm-dashboard/src/lib/logoTreatments.test.ts
index b0a2073be8d..08ea283a7a3 100644
--- a/ui/litellm-dashboard/src/lib/logoTreatments.test.ts
+++ b/ui/litellm-dashboard/src/lib/logoTreatments.test.ts
@@ -35,6 +35,17 @@ describe("logoTreatmentFor", () => {
expect(logoTreatmentFor("https://cdn.example.com/github.svg")).toBeUndefined();
});
+ it("does not treat a remote URL that also carries a bundled-looking path", () => {
+ expect(logoTreatmentFor("https://cdn.example.com/assets/logos/github.svg")).toBeUndefined();
+ expect(logoTreatmentFor("http://cdn.example.com/_next/static/media/github.abc123.svg")).toBeUndefined();
+ expect(logoTreatmentFor("//cdn.example.com/assets/logos/github.svg")).toBeUndefined();
+ expect(logoTreatmentFor("HTTPS://CDN.EXAMPLE.COM/assets/logos/github.svg")).toBeUndefined();
+ });
+
+ it("does not treat a data URL that happens to contain a bundled-looking path", () => {
+ expect(logoTreatmentFor("data:image/svg+xml,/assets/logos/github.svg")).toBeUndefined();
+ });
+
it("does not treat a non-logo path whose filename collides with a bundled asset", () => {
expect(logoTreatmentFor("/uploads/user/github.svg")).toBeUndefined();
});
diff --git a/ui/litellm-dashboard/src/lib/logoTreatments.ts b/ui/litellm-dashboard/src/lib/logoTreatments.ts
index f8378800046..7a5a7892d17 100644
--- a/ui/litellm-dashboard/src/lib/logoTreatments.ts
+++ b/ui/litellm-dashboard/src/lib/logoTreatments.ts
@@ -1,3 +1,5 @@
+import { isExternalAssetSrc } from "@/lib/assetPaths";
+
export type LogoTreatment = "invert" | "plate";
const BUNDLED_LOGO_PATH = /(?:\/assets\/logos\/|\/_next\/static\/media\/)/;
@@ -49,7 +51,7 @@ const withoutBundlerHash = (basename: string): string | undefined => {
};
export const logoTreatmentFor = (src: string | null | undefined): LogoTreatment | undefined => {
- if (!src || !BUNDLED_LOGO_PATH.test(src)) return undefined;
+ if (!src || isExternalAssetSrc(src) || !BUNDLED_LOGO_PATH.test(src)) return undefined;
const basename = basenameOf(src);
const key = basename === undefined ? undefined : withoutBundlerHash(basename);
return key === undefined ? undefined : TREATMENT_BY_ASSET[key];