mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
fix(api): sanitize error responses in management routes
Prevent raw exception messages from leaking stack frames or absolute paths in the console logs and token health endpoints. Also harden the i18n mirror move script by replacing shell-based git commands with execFileSync and a safer fallback for untracked files.
This commit is contained in:
@@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<string, number> = {
|
||||
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 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user