diff --git a/.env.example b/.env.example
index 22dca3adb..87e1b5a5a 100644
--- a/.env.example
+++ b/.env.example
@@ -55,6 +55,10 @@ TURBO_TELEMETRY_DISABLED=1
# Example: DOCKER_SOCKET_PATHS=/var/run/docker.sock,/run/user/1000/podman/podman.sock
# DOCKER_SOCKET_PATHS=
+# Redis DB index for the Next.js cache handler (default: 1).
+# Separate from the main pub/sub DB (0) to allow independent FLUSHDB.
+# REDIS_CACHE_DB=1
+
# Enable kubernetes tool
# ENABLE_KUBERNETES=true
diff --git a/apps/nextjs/src/app/api/user-medias/[id]/route.ts b/apps/nextjs/src/app/api/user-medias/[id]/route.ts
index 35cd3ea61..ce2aecdb1 100644
--- a/apps/nextjs/src/app/api/user-medias/[id]/route.ts
+++ b/apps/nextjs/src/app/api/user-medias/[id]/route.ts
@@ -5,7 +5,7 @@ import type { NextRequest } from "next/server";
import { db, eq } from "@homarr/db";
import { medias } from "@homarr/db/schema";
-import { sanitizeSvg } from "../svg-purify";
+import { looksLikeSvg, sanitizeSvg } from "../svg-purify";
export async function GET(_req: NextRequest, props: { params: Promise<{ id: string }> }) {
const params = await props.params;
@@ -23,8 +23,9 @@ export async function GET(_req: NextRequest, props: { params: Promise<{ id: stri
let content = new Uint8Array(image.content);
- // Sanitize SVG content to prevent XSS attacks
- if (image.contentType === "image/svg+xml" || image.contentType === "image/svg") {
+ const isSvgMime = image.contentType === "image/svg+xml" || image.contentType === "image/svg";
+ const isSvgContent = !isSvgMime && looksLikeSvg(content);
+ if (isSvgMime || isSvgContent) {
const svgText = new TextDecoder().decode(content);
const sanitized = sanitizeSvg(svgText);
content = new TextEncoder().encode(sanitized);
diff --git a/apps/nextjs/src/app/api/user-medias/sanitize.spec.ts b/apps/nextjs/src/app/api/user-medias/sanitize.spec.ts
index c30e24401..f3adde9c2 100644
--- a/apps/nextjs/src/app/api/user-medias/sanitize.spec.ts
+++ b/apps/nextjs/src/app/api/user-medias/sanitize.spec.ts
@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
-import { purify, sanitizeSvg, svgSanitizeOptions } from "./svg-purify";
+import { looksLikeSvg, purify, sanitizeSvg, svgSanitizeOptions } from "./svg-purify";
describe("SVG sanitization", () => {
it("strips script tags from SVG", () => {
@@ -67,6 +67,21 @@ describe("SVG sanitization", () => {
expect(clean).not.toContain("onbegin");
});
+ it("DOMPurify actually strips dirty payload (guard against no-op shim)", () => {
+ const dirty = '';
+ const clean = sanitizeSvg(dirty);
+ expect(clean).not.toBe(dirty);
+ expect(clean).not.toContain("