import { spawnSync } from "node:child_process"; import { chmodSync, mkdirSync, mkdtempSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join, resolve } from "node:path"; import { describe, expect, it } from "vitest"; import { formatStartupFailure } from "../src/startup-error.js"; describe("formatStartupFailure", () => { it.each([ [new Error("auth_session_store_invalid"), "backend startup failed: auth_session_store_invalid"], [new Error("auth_config_invalid"), "backend startup failed: auth_config_invalid"], [new Error("workspace_registry_invalid"), "backend startup failed: workspace_registry_invalid"], ])("emits only an allowlisted startup cause", (error, expected) => { expect(formatStartupFailure(error)).toBe(expected); }); it("collapses unknown errors without exposing their message, stack, token, or path", () => { const error = new Error( "EACCES password=plain-secret token=token-secret at /run/secrets/private-token", ); error.stack = "Error: raw failure\n at /app/backend/dist/server.js:42:1"; const formatted = formatStartupFailure(error); expect(formatted).toBe("backend startup failed: startup_unknown"); for (const leaked of [ "EACCES", "plain-secret", "token-secret", "/run/secrets", "raw failure", "server.js", ]) { expect(formatted).not.toContain(leaked); } }); it("sanitizes synchronous configuration failures from the real server subprocess", () => { const root = mkdtempSync(join(tmpdir(), "thothii-startup-secret-")); const secret = "startup-password-do-not-log"; const authDirectory = join(root, `auth-${secret}`); mkdirSync(authDirectory, { mode: 0o700 }); const authFile = join(authDirectory, "auth.yaml"); writeFileSync(authFile, `version: 1\nmode: local\npassword: ${secret}\n`, { mode: 0o600 }); chmodSync(authDirectory, 0o700); const entrypoint = resolve(process.cwd(), "src/server.ts"); const result = spawnSync(process.execPath, ["--import", "tsx", entrypoint], { cwd: process.cwd(), encoding: "utf8", timeout: 15_000, env: { ...process.env, NODE_ENV: "test", THT_AUTH_CONFIG_FILE: authFile, THT_AUTH_STATE_ROOT: join(root, "auth-state"), }, }); expect(result.status).toBe(1); expect(result.stdout).toBe(""); expect(result.stderr.trim()).toBe("backend startup failed: auth_config_invalid"); expect(`${result.stdout}${result.stderr}`).not.toContain(secret); expect(`${result.stdout}${result.stderr}`).not.toContain(authDirectory); expect(`${result.stdout}${result.stderr}`).not.toContain("server.ts"); }); });