diff --git a/scripts/docs/move-i18n-mirrors.mjs b/scripts/docs/move-i18n-mirrors.mjs index 110f3dfda2..444e99946d 100644 --- a/scripts/docs/move-i18n-mirrors.mjs +++ b/scripts/docs/move-i18n-mirrors.mjs @@ -18,7 +18,7 @@ import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; -import { execSync } from "node:child_process"; +import { execFileSync } from "node:child_process"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const ROOT = path.resolve(__dirname, "..", ".."); @@ -116,19 +116,23 @@ for (const locale of fs.readdirSync(I18N_DIR)) { } if (!fs.existsSync(subDir)) fs.mkdirSync(subDir, { recursive: true }); + const relSrc = path.relative(ROOT, src); + const relDst = path.relative(ROOT, dst); try { - execSync(`git mv -k -- "${path.relative(ROOT, src)}" "${path.relative(ROOT, dst)}"`, { + execFileSync("git", ["mv", "-k", "--", relSrc, relDst], { cwd: ROOT, stdio: "pipe", }); moved++; - } catch (e) { - // fallback: copy + delete + } catch { + // fallback: copy + delete; emulate `|| true` for the rm by ignoring its failure fs.renameSync(src, dst); - execSync( - `git rm --cached -- "${path.relative(ROOT, src)}" 2>/dev/null || true; git add -- "${path.relative(ROOT, dst)}"`, - { cwd: ROOT, stdio: "pipe", shell: "/bin/bash" } - ); + try { + execFileSync("git", ["rm", "--cached", "--", relSrc], { cwd: ROOT, stdio: "pipe" }); + } catch { + // file may not be tracked yet — safe to ignore + } + execFileSync("git", ["add", "--", relDst], { cwd: ROOT, stdio: "pipe" }); moved++; } } diff --git a/src/app/api/logs/console/route.ts b/src/app/api/logs/console/route.ts index cdfe0984dc..1ebd324b26 100644 --- a/src/app/api/logs/console/route.ts +++ b/src/app/api/logs/console/route.ts @@ -14,6 +14,7 @@ import { NextRequest, NextResponse } from "next/server"; import { readFileSync, existsSync } from "fs"; import { getAppLogFilePath } from "@/lib/logEnv"; import { requireManagementAuth } from "@/lib/api/requireManagementAuth"; +import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts"; const LEVEL_ORDER: Record = { trace: 5, @@ -114,6 +115,9 @@ export async function GET(req: NextRequest) { }, }); } catch (err: any) { - return NextResponse.json({ error: err.message || "Failed to read logs" }, { status: 500 }); + return NextResponse.json( + { error: sanitizeErrorMessage(err?.message) || "Failed to read logs" }, + { status: 500 } + ); } } diff --git a/src/app/api/token-health/route.ts b/src/app/api/token-health/route.ts index f21abcc5e7..b1176aefd3 100644 --- a/src/app/api/token-health/route.ts +++ b/src/app/api/token-health/route.ts @@ -6,6 +6,7 @@ */ import { getProviderConnections } from "@/lib/localDb"; +import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts"; export async function GET() { try { @@ -31,6 +32,9 @@ export async function GET() { status: errored > 0 ? "error" : healthy < total ? "warning" : "healthy", }); } catch (err) { - return Response.json({ error: err.message, status: "unknown" }, { status: 500 }); + return Response.json( + { error: sanitizeErrorMessage((err as Error)?.message), status: "unknown" }, + { status: 500 } + ); } } diff --git a/tests/unit/error-message-sanitization.test.ts b/tests/unit/error-message-sanitization.test.ts index a977bf8fb6..e855d0dde1 100644 --- a/tests/unit/error-message-sanitization.test.ts +++ b/tests/unit/error-message-sanitization.test.ts @@ -239,3 +239,15 @@ test("buildErrorBody never exposes stack traces in its message", async () => { assert.equal(body.error.message, "Internal error"); assert.ok(!body.error.message.includes("at /opt")); }); + +test("GET /token-health response never leaks stack frames or absolute paths", async () => { + const tokenHealthRoute = await import("../../src/app/api/token-health/route.ts"); + const res = await tokenHealthRoute.GET(); + const body = (await res.json()) as any; + assert.ok(!("stack" in body), "response must not contain stack trace"); + if (typeof body.error === "string") { + assert.ok(!body.error.includes(" at "), "stack frame must not leak in error"); + assert.ok(!/^\//.test(body.error), "absolute POSIX path must not leak"); + assert.ok(!/^[A-Za-z]:[\\/]/.test(body.error), "absolute Windows path must not leak"); + } +});