Files
ThothII/backend/test/workspaces-bindings.test.ts
T

257 lines
9.5 KiB
TypeScript

import {
chmodSync, mkdirSync, mkdtempSync, realpathSync, rmSync, symlinkSync, writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, expect, test } from "vitest";
import {
resolveBinding, resolveEvidenceBinding, resolveRuntimeBindings, supportsSessionRuntime,
} from "../src/workspaces/bindings.js";
import { parseWorkspaceYaml } from "../src/workspaces/schema.js";
const workspaceV4 = parseWorkspaceYaml(`workspace:
schema_version: 4
id: psd-clinical
name: Policlinico San Donato
language: it
dwh:
engine: postgres
database: postgres
schema: datawarehouse
supported_transports: [postgres_direct, rest_api, ssh_tunnel]
`);
const temporaryRoots: string[] = [];
afterEach(() => {
temporaryRoots.splice(0).forEach((root) => rmSync(root, { recursive: true, force: true }));
});
function secretPath(name: string): { root: string; path: string } {
const root = mkdtempSync(join(tmpdir(), "thoth-binding-"));
temporaryRoots.push(root);
const secrets = join(root, "secrets");
mkdirSync(secrets);
const path = join(secrets, name);
writeFileSync(path, "");
return { root: secrets, path };
}
test("resolves workspace-v4 direct DWH bindings from the stable namespace", () => {
const password = secretPath("dwh-password");
const result = resolveBinding(workspaceV4, "DWH", {
THT_WS_PSD_CLINICAL_DWH_TRANSPORT: "postgres_direct",
THT_WS_PSD_CLINICAL_DWH_HOST: "dwh.internal",
THT_WS_PSD_CLINICAL_DWH_PORT: "5432",
THT_WS_PSD_CLINICAL_DWH_USER: "reader",
THT_WS_PSD_CLINICAL_DWH_PASSWORD_FILE: password.path,
}, [password.root]);
expect(result).toMatchObject({
transport: "postgres_direct",
missing: [],
values: {
THT_WS_PSD_CLINICAL_DWH_HOST: "dwh.internal",
THT_WS_PSD_CLINICAL_DWH_PORT: "5432",
THT_WS_PSD_CLINICAL_DWH_PASSWORD_FILE: realpathSync(password.path),
},
});
});
test("requires workspace-v4 REST credentials unless the DWH diagnostic declares auth none", () => {
expect(resolveBinding(workspaceV4, "DWH", {
THT_WS_PSD_CLINICAL_DWH_TRANSPORT: "rest_api",
THT_WS_PSD_CLINICAL_DWH_BASE_URL: "https://dwh.example.test",
}, []).missing).toContain("THT_WS_PSD_CLINICAL_DWH_API_KEY_FILE");
const noAuth = parseWorkspaceYaml(`workspace:
schema_version: 4
id: psd-clinical
name: No auth
language: en
dwh:
engine: postgres
database: postgres
schema: public
supported_transports: [rest_api]
diagnostics:
dwh_rest:
method: GET
path: /health
auth: none
response: { database: database, schema: schema }
`);
expect(resolveBinding(noAuth, "DWH", {
THT_WS_PSD_CLINICAL_DWH_TRANSPORT: "rest_api",
THT_WS_PSD_CLINICAL_DWH_BASE_URL: "https://dwh.example.test",
}, []).missing).toEqual([]);
});
test("rejects unsupported transports and secret paths outside configured roots", () => {
const outside = secretPath("outside-password");
const allowed = secretPath("allowed-password");
const directOnly = parseWorkspaceYaml(`workspace:
schema_version: 4
id: psd-clinical
name: Direct only
language: en
dwh:
engine: postgres
database: postgres
schema: public
supported_transports: [postgres_direct]
`);
expect(resolveBinding(directOnly, "DWH", {
THT_WS_PSD_CLINICAL_DWH_TRANSPORT: "rest_api",
}, []).missing).toContain("THT_WS_PSD_CLINICAL_DWH_TRANSPORT");
const result = resolveBinding(workspaceV4, "DWH", {
THT_WS_PSD_CLINICAL_DWH_TRANSPORT: "postgres_direct",
THT_WS_PSD_CLINICAL_DWH_HOST: "dwh.internal",
THT_WS_PSD_CLINICAL_DWH_PORT: "5432",
THT_WS_PSD_CLINICAL_DWH_USER: "reader",
THT_WS_PSD_CLINICAL_DWH_PASSWORD_FILE: outside.path,
}, [allowed.root]);
expect(result.missing).toContain("THT_WS_PSD_CLINICAL_DWH_PASSWORD_FILE");
expect(JSON.stringify(result)).not.toContain(outside.path);
});
test("runtime bindings contain only DWH and Evidence roles", () => {
const password = secretPath("dwh-password");
const bindings = resolveRuntimeBindings(workspaceV4, {
THT_WS_PSD_CLINICAL_DWH_TRANSPORT: "postgres_direct",
THT_WS_PSD_CLINICAL_DWH_HOST: "dwh.internal",
THT_WS_PSD_CLINICAL_DWH_PORT: "5432",
THT_WS_PSD_CLINICAL_DWH_USER: "reader",
THT_WS_PSD_CLINICAL_DWH_PASSWORD_FILE: password.path,
THT_WS_PSD_CLINICAL_VECTOR_BASE_URL: "https://ignored.example.test",
}, [password.root]);
expect(Object.keys(bindings)).toEqual(["dwh", "evidence"]);
expect(bindings.dwh.missing).toEqual([]);
expect(supportsSessionRuntime(bindings)).toBe(true);
});
function withEvidence(source: Record<string, unknown>) {
return parseWorkspaceYaml(`workspace:
schema_version: 4
id: psd-clinical
name: Policlinico San Donato
language: it
dwh:
engine: postgres
database: postgres
schema: datawarehouse
supported_transports: [postgres_direct]
evidence:
source: ${JSON.stringify(source)}
`);
}
const evidenceVariable = (suffix: string) => `THT_WS_PSD_CLINICAL_EVIDENCE_${suffix}`;
test.each([
{ type: "filesystem", uri: "psd-clinical/evidence" },
{ type: "http", uris: ["https://evidence.example.test/guide.md"], authentication: "none" },
{ type: "s3", uri: "s3://clinical-evidence/published/", credentials: "ambient" },
])("does not resolve Evidence variables for $type modes without file credentials", (source) => {
expect(resolveEvidenceBinding(withEvidence(source), {
[evidenceVariable("SIGNED_URLS_FILE")]: "/CANARY/http",
[evidenceVariable("ACCESS_KEY_FILE")]: "/CANARY/access",
}, ["/run/secrets"])).toEqual({ values: {}, missing: [] });
});
test("requires only a safe HTTP signed-URL file and never reads its contents", () => {
const signed = secretPath("evidence-signed-urls");
writeFileSync(signed.path, "CANARY-SIGNED-URL-CONTENT");
const source = withEvidence({
type: "http",
uris: ["https://evidence.example.test/guide.md"],
authentication: "signed_urls_file",
});
const variable = evidenceVariable("SIGNED_URLS_FILE");
expect(resolveEvidenceBinding(source, {}, [signed.root]).missing).toEqual([variable]);
const resolved = resolveEvidenceBinding(source, {
[variable]: signed.path,
[evidenceVariable("ACCESS_KEY_FILE")]: signed.path,
}, [signed.root]);
expect(resolved).toEqual({ values: { [variable]: realpathSync(signed.path) }, missing: [] });
expect(JSON.stringify(resolved)).not.toContain("CANARY-SIGNED-URL-CONTENT");
});
test("canonicalizes an in-root Evidence symlink before passing it to the harness", () => {
const signed = secretPath("evidence-signed-target");
const link = join(signed.root, "signed-urls-link");
symlinkSync(signed.path, link);
const source = withEvidence({
type: "http",
uris: ["https://evidence.example.test/guide.md"],
authentication: "signed_urls_file",
});
const variable = evidenceVariable("SIGNED_URLS_FILE");
expect(resolveEvidenceBinding(source, { [variable]: link }, [signed.root])).toEqual({
values: { [variable]: realpathSync(signed.path) }, missing: [],
});
});
test("requires S3 access and secret files together while accepting an optional safe session token", () => {
const access = secretPath("evidence-access");
const secret = secretPath("evidence-secret");
const token = secretPath("evidence-token");
const source = withEvidence({
type: "s3", uri: "s3://clinical-evidence/published/", credentials: "static_files",
});
const env = {
[evidenceVariable("ACCESS_KEY_FILE")]: access.path,
[evidenceVariable("SECRET_KEY_FILE")]: secret.path,
[evidenceVariable("SESSION_TOKEN_FILE")]: token.path,
[evidenceVariable("SIGNED_URLS_FILE")]: access.path,
};
expect(resolveEvidenceBinding(source, {
[evidenceVariable("ACCESS_KEY_FILE")]: access.path,
}, [access.root]).missing).toEqual([evidenceVariable("SECRET_KEY_FILE")]);
expect(resolveEvidenceBinding(source, env, [access.root, secret.root, token.root])).toEqual({
values: {
[evidenceVariable("ACCESS_KEY_FILE")]: realpathSync(access.path),
[evidenceVariable("SECRET_KEY_FILE")]: realpathSync(secret.path),
[evidenceVariable("SESSION_TOKEN_FILE")]: realpathSync(token.path),
},
missing: [],
});
});
test("rejects relative, missing, directory, unreadable, and escaping symlink Evidence paths", () => {
const allowed = secretPath("valid");
const outside = secretPath("outside");
const directory = join(allowed.root, "directory");
mkdirSync(directory);
const link = join(allowed.root, "escape");
symlinkSync(outside.path, link);
const unreadable = join(allowed.root, "unreadable");
writeFileSync(unreadable, "secret");
chmodSync(unreadable, 0o000);
const source = withEvidence({
type: "http", uris: ["https://evidence.example.test/guide.md"], authentication: "signed_urls_file",
});
const variable = evidenceVariable("SIGNED_URLS_FILE");
for (const path of ["relative", join(allowed.root, "missing"), directory, unreadable, link]) {
expect(resolveEvidenceBinding(source, { [variable]: path }, [allowed.root])).toEqual({
values: {}, missing: [variable],
});
}
chmodSync(unreadable, 0o600);
});
test("includes Evidence binding completeness in session runtime support without changing v3 compatibility", () => {
const unsigned = resolveRuntimeBindings(workspaceV4, {}, ["/run/secrets"]);
expect(unsigned.evidence).toEqual({ values: {}, missing: [] });
expect(supportsSessionRuntime(unsigned)).toBe(true);
const signed = resolveRuntimeBindings(withEvidence({
type: "http", uris: ["https://evidence.example.test/guide.md"], authentication: "signed_urls_file",
}), {}, ["/run/secrets"]);
expect(signed.evidence.missing).toEqual([evidenceVariable("SIGNED_URLS_FILE")]);
expect(supportsSessionRuntime(signed)).toBe(false);
});