Files
ThothII/backend/test/catalog-postgres-access.test.ts
T

172 lines
6.0 KiB
TypeScript

import { EventEmitter } from "node:events";
import { existsSync, mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { PassThrough } from "node:stream";
import type { ChildProcessWithoutNullStreams } from "node:child_process";
import type { Client, ClientConfig } from "pg";
import { afterEach, expect, test, vi } from "vitest";
import {
buildSshArguments,
ConcreteCatalogPostgresAccess,
} from "../src/catalog/postgres-access.js";
import { CATALOG_SECRET_IDS } from "../src/catalog/secrets.js";
import type { WorkspaceDatabase } from "../src/catalog/types.js";
import { WorkspaceSecretStore } from "../src/workspaces/secret-store.js";
const roots: string[] = [];
afterEach(() => {
for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true });
});
function secretStore() {
const root = mkdtempSync(join(tmpdir(), "catalog-ssh-secrets-"));
const runtimeRoot = mkdtempSync(join(tmpdir(), "catalog-ssh-runtime-"));
roots.push(root, runtimeRoot);
return new WorkspaceSecretStore({ root, runtimeRoot, installationId: "test" });
}
function sshDatabase(): WorkspaceDatabase {
return {
id: "11111111-1111-4111-8111-111111111111",
workspaceId: "psd-clinical",
engine: "postgres",
databaseName: "warehouse",
schema: "datawarehouse",
version: 4,
createdAt: "2026-08-27T08:00:00Z",
updatedAt: "2026-08-27T09:00:00Z",
connectionStatus: "reachable",
binding: {
transport: "ssh_tunnel",
username: "warehouse_reader",
sshHost: "bastion.internal",
sshPort: 2222,
sshUsername: "tunnel_user",
sshTargetHost: "postgres.internal",
sshTargetPort: 5432,
tlsServername: "postgres.internal",
},
};
}
function fakeChild(): ChildProcessWithoutNullStreams {
const child = new EventEmitter() as EventEmitter & {
stdin: PassThrough;
stdout: PassThrough;
stderr: PassThrough;
exitCode: number | null;
signalCode: NodeJS.Signals | null;
kill: (signal?: NodeJS.Signals | number) => boolean;
};
child.stdin = new PassThrough();
child.stdout = new PassThrough();
child.stderr = new PassThrough();
child.exitCode = null;
child.signalCode = null;
child.kill = vi.fn((signal: NodeJS.Signals | number = "SIGTERM") => {
child.signalCode = typeof signal === "string" ? signal : "SIGTERM";
child.emit("exit", null, child.signalCode);
return true;
});
return child as unknown as ChildProcessWithoutNullStreams;
}
test("builds a strict host-verified OpenSSH stdio tunnel", () => {
const args = buildSshArguments({
sshHost: "bastion.internal",
sshPort: 2222,
sshUsername: "tunnel_user",
targetHost: "postgres.internal",
targetPort: 5432,
privateKeyFile: "/runtime/id",
knownHostsFile: "/runtime/known_hosts",
passphraseFile: "/runtime/passphrase",
connectTimeoutMs: 5_001,
});
expect(args).toEqual(expect.arrayContaining([
"-F", "/dev/null",
"-o", "BatchMode=no",
"-o", "StrictHostKeyChecking=yes",
"-o", "UserKnownHostsFile=/runtime/known_hosts",
"-o", "GlobalKnownHostsFile=/dev/null",
"-o", "IdentitiesOnly=yes",
"-o", "IdentityAgent=none",
"-o", "PasswordAuthentication=no",
"-o", "KbdInteractiveAuthentication=no",
"-o", "ConnectTimeout=6",
"-W", "postgres.internal:5432",
"--", "tunnel_user@bastion.internal",
]));
});
test("connects pg through OpenSSH, supplies askpass, and releases all secret leases", async () => {
const store = secretStore();
store.putMany("psd-clinical", {
[CATALOG_SECRET_IDS.password]: "db-password ",
[CATALOG_SECRET_IDS.sshPrivateKey]: "PRIVATE KEY\n",
[CATALOG_SECRET_IDS.sshPrivateKeyPassphrase]: "key-passphrase",
[CATALOG_SECRET_IDS.sshKnownHosts]: "bastion.internal ssh-ed25519 AAAATEST\n",
[CATALOG_SECRET_IDS.tlsCa]: "CA CERTIFICATE\n",
});
const child = fakeChild();
let clientConfig: ClientConfig | undefined;
let spawnCall: { command: string; args: readonly string[]; env: NodeJS.ProcessEnv } | undefined;
const end = vi.fn(async () => undefined);
const query = vi.fn(async () => ({ rows: [{ ok: true }] }));
const connect = vi.fn(async () => undefined);
const access = new ConcreteCatalogPostgresAccess(store, {
sshBinary: "/usr/bin/ssh",
askpassPath: "/app/ssh-askpass.mjs",
connectTimeoutMs: 5_000,
spawnSsh: (command, args, options) => {
spawnCall = { command, args, env: options.env };
return child;
},
createClient: (config) => {
clientConfig = config;
return { connect, query, end } as unknown as Client;
},
});
const client = await access.connect(sshDatabase(), new AbortController().signal);
expect(connect).toHaveBeenCalledOnce();
expect(clientConfig).toMatchObject({
host: "postgres.internal",
port: 5432,
database: "warehouse",
user: "warehouse_reader",
password: "db-password ",
connectionTimeoutMillis: 5_000,
ssl: {
ca: "CA CERTIFICATE\n",
servername: "postgres.internal",
rejectUnauthorized: true,
},
});
expect(clientConfig?.stream).toBeTypeOf("function");
expect(spawnCall?.command).toBe("/usr/bin/ssh");
expect(spawnCall?.args.some((argument) => argument.startsWith("IdentityFile="))).toBe(true);
expect(spawnCall?.args.some((argument) => argument.startsWith("UserKnownHostsFile="))).toBe(true);
expect(spawnCall?.env).toMatchObject({
DISPLAY: "thothii",
SSH_ASKPASS: "/app/ssh-askpass.mjs",
SSH_ASKPASS_REQUIRE: "force",
});
const leasedPaths = spawnCall!.args
.filter((argument) => argument.startsWith("IdentityFile=") || argument.startsWith("UserKnownHostsFile="))
.map((argument) => argument.slice(argument.indexOf("=") + 1));
leasedPaths.push(spawnCall!.env.THT_SSH_PASSPHRASE_FILE!);
expect(leasedPaths.every(existsSync)).toBe(true);
await expect(client.query("SELECT 1", [])).resolves.toEqual({ rows: [{ ok: true }] });
await client.end();
expect(end).toHaveBeenCalledOnce();
expect(child.kill).toHaveBeenCalledWith("SIGTERM");
expect(leasedPaths.some(existsSync)).toBe(false);
});