From 4b0ff37b706d31273e4d463c4d0e849eb861f15e Mon Sep 17 00:00:00 2001 From: w287346141 <57440615+w287346141@users.noreply.github.com> Date: Mon, 1 Jun 2026 11:46:01 +0800 Subject: [PATCH] Handle malformed dashboard URL encodings --- src/server/api/file-read.ts | 7 ++++++- src/server/api/sessions.ts | 7 ++++++- tests/dashboard-api-decode.test.ts | 31 ++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 tests/dashboard-api-decode.test.ts diff --git a/src/server/api/file-read.ts b/src/server/api/file-read.ts index 0a8f01e..66439a0 100644 --- a/src/server/api/file-read.ts +++ b/src/server/api/file-read.ts @@ -44,7 +44,12 @@ export async function handleFileRead( ): Promise { if (method !== "GET") return { status: 405, body: { error: "GET only" } }; - const filePath = decodeURIComponent(rest.join("/")); + let filePath: string; + try { + filePath = decodeURIComponent(rest.join("/")); + } catch { + return { status: 400, body: { error: "invalid file path encoding" } }; + } if (!filePath) return { status: 400, body: { error: "file path required" } }; const cwd = ctx.getCurrentCwd?.(); diff --git a/src/server/api/sessions.ts b/src/server/api/sessions.ts index de4e880..279bf06 100644 --- a/src/server/api/sessions.ts +++ b/src/server/api/sessions.ts @@ -87,7 +87,12 @@ export async function handleSessions( // Single-session detail / switch / delete. URL-decode in case the name // had spaces / CJK (sanitizeName allows them). - const name = decodeURIComponent(rest[0]!); + let name: string; + try { + name = decodeURIComponent(rest[0]!); + } catch { + return { status: 400, body: { error: "invalid session name encoding" } }; + } const path = sessionPath(name); const currentName = ctx.getSessionName?.() ?? null; diff --git a/tests/dashboard-api-decode.test.ts b/tests/dashboard-api-decode.test.ts new file mode 100644 index 0000000..aab7045 --- /dev/null +++ b/tests/dashboard-api-decode.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, it } from "vitest"; +import { handleFileRead } from "../src/server/api/file-read.js"; +import { handleSessions } from "../src/server/api/sessions.js"; +import type { DashboardContext } from "../src/server/context.js"; + +const CTX: DashboardContext = { + mode: "standalone", + configPath: "config.json", + usageLogPath: "usage.jsonl", + getCurrentCwd: () => process.cwd(), +}; + +describe("dashboard API URL decoding", () => { + it("returns 400 for malformed file-read path encoding", async () => { + const result = await handleFileRead("GET", ["%E0%A4%A"], "", CTX); + + expect(result).toEqual({ + status: 400, + body: { error: "invalid file path encoding" }, + }); + }); + + it("returns 400 for malformed session name encoding", async () => { + const result = await handleSessions("GET", ["%E0%A4%A"], "", CTX); + + expect(result).toEqual({ + status: 400, + body: { error: "invalid session name encoding" }, + }); + }); +});