From 98c7dcec743d2294ca596a22696ee657f577281f Mon Sep 17 00:00:00 2001 From: SebF33 <78741537+SebF33@users.noreply.github.com> Date: Sun, 12 Apr 2026 00:21:05 +0200 Subject: [PATCH 1/2] fix: handle commas in static file paths --- .../fresh/src/middlewares/static_files.ts | 7 +++++ .../src/middlewares/static_files_test.ts | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/packages/fresh/src/middlewares/static_files.ts b/packages/fresh/src/middlewares/static_files.ts index ee06b0f7e3f..33c5e4745bb 100644 --- a/packages/fresh/src/middlewares/static_files.ts +++ b/packages/fresh/src/middlewares/static_files.ts @@ -3,6 +3,7 @@ import { ASSET_CACHE_BUST_KEY } from "../constants.ts"; import { BUILD_ID } from "@fresh/build-id"; import { tracer } from "../otel.ts"; import { getBuildCache } from "../context.ts"; +import { systemPathToUrlEncoded } from "../dev/dev_build_cache.ts"; /** * Fresh middleware to serve static files from the `static/` directory. @@ -25,6 +26,12 @@ export function staticFiles(): Middleware { : "/"; } + try { + pathname = systemPathToUrlEncoded(decodeURIComponent(pathname)); + } catch { + return await ctx.next(); + } + // Fast path bail out const startTime = performance.now() + performance.timeOrigin; const file = await buildCache.readFile(pathname); diff --git a/packages/fresh/src/middlewares/static_files_test.ts b/packages/fresh/src/middlewares/static_files_test.ts index 3ba5daacb51..3c0b311c74c 100644 --- a/packages/fresh/src/middlewares/static_files_test.ts +++ b/packages/fresh/src/middlewares/static_files_test.ts @@ -296,3 +296,29 @@ Deno.test("static files - fallthrough", async () => { expect(text).toEqual("it works"); expect(called).toEqual(["before", "after"]); }); + +Deno.test("static files - comma in pathname", async () => { + const key = systemPathToUrlEncoded("foo,bar.css"); + + const buildCache = new MockBuildCache({ + [key]: { + content: "body {}", + hash: null, + }, + }); + + const server = serveMiddleware( + staticFiles(), + { buildCache }, + ); + + // Browser/request path usually keeps the comma unencoded + let res = await server.get("/foo,bar.css"); + await res.body?.cancel(); + expect(res.status).toEqual(200); + + // Encoded form should also resolve + res = await server.get("/foo%2Cbar.css"); + await res.body?.cancel(); + expect(res.status).toEqual(200); +}); From 370e147e29287259ca55394ee6f12e63ec9403bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Wed, 22 Apr 2026 10:18:07 +0200 Subject: [PATCH 2/2] refactor: inline path normalization to avoid dev module import in production Move the decode/re-encode logic into a local `normalizePathname` helper instead of importing `systemPathToUrlEncoded` from `dev_build_cache.ts`, which pulls the entire dev module graph into the production middleware. Also narrow the catch clause to only handle URIError. --- packages/fresh/src/middlewares/static_files.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/fresh/src/middlewares/static_files.ts b/packages/fresh/src/middlewares/static_files.ts index 33c5e4745bb..288f9a98eb6 100644 --- a/packages/fresh/src/middlewares/static_files.ts +++ b/packages/fresh/src/middlewares/static_files.ts @@ -3,7 +3,14 @@ import { ASSET_CACHE_BUST_KEY } from "../constants.ts"; import { BUILD_ID } from "@fresh/build-id"; import { tracer } from "../otel.ts"; import { getBuildCache } from "../context.ts"; -import { systemPathToUrlEncoded } from "../dev/dev_build_cache.ts"; + +/** Decode and re-encode each path segment so that characters like commas + * are percent-encoded consistently with how `prepareStaticFile` stores + * entries in the build cache. */ +function normalizePathname(pathname: string): string { + return "/" + + pathname.split("/").filter(Boolean).map(encodeURIComponent).join("/"); +} /** * Fresh middleware to serve static files from the `static/` directory. @@ -27,8 +34,9 @@ export function staticFiles(): Middleware { } try { - pathname = systemPathToUrlEncoded(decodeURIComponent(pathname)); - } catch { + pathname = normalizePathname(decodeURIComponent(pathname)); + } catch (_e: unknown) { + if (!(_e instanceof URIError)) throw _e; return await ctx.next(); }