537 lines
17 KiB
TypeScript
537 lines
17 KiB
TypeScript
import { readFileSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
import { parse } from "yaml";
|
|
import { expect, test } from "vitest";
|
|
import * as workspaceSchema from "../src/workspaces/schema.js";
|
|
import {
|
|
parseWorkspaceYaml,
|
|
serializeWorkspaceYaml,
|
|
validateWorkspaceDescriptor,
|
|
} from "../src/workspaces/schema.js";
|
|
|
|
export const validYaml = `workspace:
|
|
schema_version: 4
|
|
id: psd-clinical
|
|
name: Policlinico San Donato
|
|
description: Clinical data warehouse workspace
|
|
language: it
|
|
dwh:
|
|
engine: postgres
|
|
database: postgres
|
|
schema: datawarehouse
|
|
port: 5432
|
|
timeout_ms: 5000
|
|
supported_transports:
|
|
- postgres_direct
|
|
- rest_api
|
|
- ssh_tunnel
|
|
`;
|
|
|
|
test("rejects unknown keys and invalid immutable IDs", () => {
|
|
expect(() => parseWorkspaceYaml(validYaml.replace(" language: it", " language: it\n label: PSD")))
|
|
.toThrow(/unrecognized key/i);
|
|
expect(() => parseWorkspaceYaml(validYaml.replace("id: psd-clinical", "id: PSD")))
|
|
.toThrow(/id/i);
|
|
});
|
|
|
|
test("rejects executable or otherwise custom YAML tags in canonical descriptors", () => {
|
|
expect(() => parseWorkspaceYaml(validYaml.replace(
|
|
"name: Policlinico San Donato",
|
|
"name: !command echo-never-execute",
|
|
))).toThrow(/tag|yaml/i);
|
|
});
|
|
|
|
test("accepts optional connection ports and timeouts but rejects unsafe values", () => {
|
|
expect(parseWorkspaceYaml(validYaml).dwh.port).toBe(5432);
|
|
expect(() => parseWorkspaceYaml(validYaml.replace("port: 5432", "port: 0")))
|
|
.toThrow(/port/i);
|
|
expect(() => parseWorkspaceYaml(validYaml.replace("port: 5432", "port: 65536")))
|
|
.toThrow(/port/i);
|
|
expect(() => parseWorkspaceYaml(validYaml.replace("timeout_ms: 5000", "timeout_ms: 0")))
|
|
.toThrow(/timeout/i);
|
|
});
|
|
|
|
test("accepts a model-free schema v4 workspace", () => {
|
|
expect(parseWorkspaceYaml(validYaml)).toMatchObject({
|
|
workspace: { schema_version: 4, id: "psd-clinical" },
|
|
dwh: { database: "postgres", schema: "datawarehouse" },
|
|
});
|
|
});
|
|
|
|
test("committed example descriptors parse as exact schema v4 workspaces", () => {
|
|
const example = readFileSync(resolve(process.cwd(), "../deploy/workspaces/example.yaml"), "utf8");
|
|
const psdExample = readFileSync(resolve(process.cwd(), "../deploy/workspaces/psd.yaml.example"), "utf8");
|
|
|
|
expect(() => parseWorkspaceYaml(example)).not.toThrow();
|
|
expect(() => parseWorkspaceYaml(psdExample)).not.toThrow();
|
|
expect(parseWorkspaceYaml(example)).toMatchObject({
|
|
workspace: { schema_version: 4 },
|
|
});
|
|
expect(parseWorkspaceYaml(psdExample)).toMatchObject({
|
|
workspace: { schema_version: 4 },
|
|
});
|
|
});
|
|
|
|
test("rejects installation-owned model and vector fields", () => {
|
|
expect(() => parseWorkspaceYaml(`${validYaml}llm_policy:\n allowed: [zai/glm-5.3]\n`))
|
|
.toThrow(/unrecognized key|llm_policy/i);
|
|
expect(() => parseWorkspaceYaml(`${validYaml}semantic_index: {}\n`))
|
|
.toThrow(/unrecognized key|semantic_index/i);
|
|
});
|
|
|
|
test.each([
|
|
["v1", `workspace:
|
|
schema_version: 1
|
|
id: psd-clinical
|
|
name: Policlinico San Donato
|
|
language: it
|
|
dwh:
|
|
engine: postgres
|
|
database: postgres
|
|
schema: datawarehouse
|
|
supported_transports:
|
|
- postgres_direct
|
|
semantic_index:
|
|
vector_store:
|
|
engine: pgvector
|
|
collection: clinical_documents
|
|
dimensions: 1024
|
|
distance: cosine
|
|
supported_transports:
|
|
- pgvector_direct
|
|
embedding:
|
|
provider: ollama_compatible
|
|
model: nomic-embed-text-v2-moe
|
|
dimensions: 1024
|
|
llm_policy:
|
|
allowed:
|
|
- zai/glm-5.2
|
|
`],
|
|
["v2", `workspace:
|
|
schema_version: 2
|
|
id: psd-clinical
|
|
name: Policlinico San Donato
|
|
language: it
|
|
dwh:
|
|
engine: postgres
|
|
database: postgres
|
|
schema: datawarehouse
|
|
supported_transports:
|
|
- postgres_direct
|
|
semantic_index:
|
|
vector_store:
|
|
engine: pgvector
|
|
database: postgres
|
|
schema: vectors
|
|
collection: clinical_documents
|
|
dimensions: 1024
|
|
distance: cosine
|
|
supported_transports:
|
|
- pgvector_direct
|
|
embedding:
|
|
provider: ollama_compatible
|
|
model: nomic-embed-text-v2-moe
|
|
dimensions: 1024
|
|
llm_policy:
|
|
allowed:
|
|
- zai/glm-5.2
|
|
`],
|
|
])("rejects schema %s descriptors at parser and object-validator boundaries", (_version, yaml) => {
|
|
expect(() => parseWorkspaceYaml(yaml)).toThrow(/schema_version|invalid literal|4/i);
|
|
expect(() => validateWorkspaceDescriptor(parse(yaml))).toThrow(/schema_version|invalid literal|4/i);
|
|
});
|
|
|
|
test("does not expose the redundant canonical validator or v1 migration", () => {
|
|
const legacyExports = workspaceSchema as Record<string, unknown>;
|
|
|
|
expect(legacyExports.validateCanonicalWorkspace).toBeUndefined();
|
|
expect(legacyExports.migrateWorkspaceV1ToV2).toBeUndefined();
|
|
});
|
|
|
|
test("constructs diagnostic URLs only when the resolved URL remains on the service origin", () => {
|
|
const resolveDiagnosticUrl = (workspaceSchema as { resolveDiagnosticUrl?: unknown }).resolveDiagnosticUrl;
|
|
|
|
expect(resolveDiagnosticUrl).toBeTypeOf("function");
|
|
expect((resolveDiagnosticUrl as (baseUrl: string, path: string) => URL)("https://service.example/base", "/rpc/ping"))
|
|
.toMatchObject({ href: "https://service.example/base/rpc/ping" });
|
|
expect(() => (resolveDiagnosticUrl as (baseUrl: string, path: string) => URL)(
|
|
"https://service.example/base", "//diagnostic.invalid/rpc",
|
|
)).toThrow(/origin/i);
|
|
});
|
|
|
|
test("rejects REST diagnostic declarations without their matching connector transport", () => {
|
|
const diagnostics = `diagnostics:
|
|
dwh_rest:
|
|
method: POST
|
|
path: /rpc/ping
|
|
auth: bearer
|
|
response:
|
|
database: database
|
|
schema: schema
|
|
`;
|
|
const declared = `${validYaml}${diagnostics}`;
|
|
|
|
expect(() => parseWorkspaceYaml(declared.replace(" - rest_api\n", ""))).toThrow(/dwh_rest/i);
|
|
});
|
|
|
|
test("serializes canonical YAML that parses back to the same workspace", () => {
|
|
const workspace = parseWorkspaceYaml(validYaml);
|
|
const serialized = serializeWorkspaceYaml(workspace);
|
|
|
|
expect(serializeWorkspaceYaml(parseWorkspaceYaml(serialized))).toBe(serialized);
|
|
expect(parseWorkspaceYaml(serialized)).toEqual(workspace);
|
|
});
|
|
|
|
|
|
function validWorkspaceObject(): Record<string, any> {
|
|
return parseWorkspaceYaml(validYaml) as Record<string, any>;
|
|
}
|
|
|
|
function withEvidence(source: Record<string, unknown>, policy?: Record<string, unknown>): Record<string, any> {
|
|
const workspace = structuredClone(validWorkspaceObject());
|
|
workspace.evidence = policy === undefined ? { source } : { source, policy };
|
|
return workspace;
|
|
}
|
|
|
|
function expectSafeEvidenceError(workspace: unknown, path: RegExp, canary?: string): void {
|
|
let message = "";
|
|
try {
|
|
validateWorkspaceDescriptor(workspace);
|
|
} catch (error) {
|
|
message = error instanceof Error ? error.message : String(error);
|
|
}
|
|
expect(message.replace(/\s+/g, " ")).toMatch(path);
|
|
if (canary !== undefined) expect(message).not.toContain(canary);
|
|
}
|
|
|
|
const explicitPolicy = { max_chunk_chars: 8_000, retain_published_generations: 5 };
|
|
|
|
const validEvidenceSources = [
|
|
{
|
|
name: "filesystem with explicit values",
|
|
source: {
|
|
type: "filesystem",
|
|
uri: "psd-clinical/evidence",
|
|
patterns: ["documents/**/*.pdf", "notes/*.md"],
|
|
max_bytes: 12_000_000,
|
|
},
|
|
},
|
|
{
|
|
name: "HTTP without authentication",
|
|
source: {
|
|
type: "http",
|
|
uris: ["https://evidence.example/manifest.json", "http://evidence.example/files/list.txt"],
|
|
authentication: "none",
|
|
connect_timeout_ms: 2_000,
|
|
read_timeout_ms: 20_000,
|
|
max_bytes: 12_000_000,
|
|
max_redirects: 2,
|
|
allow_private_hosts: false,
|
|
max_cache_bytes: 24_000_000,
|
|
},
|
|
},
|
|
{
|
|
name: "HTTP signed URL manifest",
|
|
source: {
|
|
type: "http",
|
|
uris: ["https://evidence.example/signed-urls.txt"],
|
|
authentication: "signed_urls_file",
|
|
connect_timeout_ms: 2_000,
|
|
read_timeout_ms: 20_000,
|
|
max_bytes: 12_000_000,
|
|
max_redirects: 2,
|
|
allow_private_hosts: true,
|
|
max_cache_bytes: 24_000_000,
|
|
},
|
|
},
|
|
{
|
|
name: "S3 with ambient credentials",
|
|
source: {
|
|
type: "s3",
|
|
uri: "s3://clinical-evidence/published/",
|
|
region: "eu-west-1",
|
|
credentials: "ambient",
|
|
trusted_endpoint: false,
|
|
allow_private_endpoint: false,
|
|
allow_insecure_endpoint: false,
|
|
max_bytes: 12_000_000,
|
|
max_objects: 2_000,
|
|
max_pages: 20,
|
|
page_size: 100,
|
|
},
|
|
},
|
|
{
|
|
name: "S3 with static-file credentials and a trusted endpoint",
|
|
source: {
|
|
type: "s3",
|
|
uri: "s3://clinical-evidence/published/",
|
|
endpoint_url: "https://objects.example",
|
|
region: "eu-west-1",
|
|
credentials: "static_files",
|
|
trusted_endpoint: true,
|
|
allow_private_endpoint: false,
|
|
allow_insecure_endpoint: false,
|
|
max_bytes: 12_000_000,
|
|
max_objects: 2_000,
|
|
max_pages: 20,
|
|
page_size: 100,
|
|
},
|
|
},
|
|
] as const;
|
|
|
|
test.each(validEvidenceSources)("accepts evidence source: $name", ({ source }) => {
|
|
expect(validateWorkspaceDescriptor(withEvidence(source, explicitPolicy))).toMatchObject({
|
|
evidence: { source, policy: explicitPolicy },
|
|
});
|
|
});
|
|
|
|
test("applies filesystem and policy defaults to the canonical descriptor", () => {
|
|
const parsed = validateWorkspaceDescriptor(withEvidence({
|
|
type: "filesystem",
|
|
uri: "psd-clinical/evidence",
|
|
}));
|
|
|
|
expect(parsed).toMatchObject({
|
|
evidence: {
|
|
source: {
|
|
type: "filesystem",
|
|
uri: "psd-clinical/evidence",
|
|
patterns: ["**/*.md"],
|
|
max_bytes: 10 * 1024 * 1024,
|
|
},
|
|
policy: { max_chunk_chars: 4_000, retain_published_generations: 3 },
|
|
},
|
|
});
|
|
});
|
|
|
|
test("defaults schema-versioned filesystem Evidence to curated documents only", () => {
|
|
const parsed = validateWorkspaceDescriptor({
|
|
...withEvidence({
|
|
type: "filesystem",
|
|
uri: "psd-clinical/evidence",
|
|
}),
|
|
evidence: {
|
|
schema_version: 2,
|
|
source: { type: "filesystem", uri: "psd-clinical/evidence" },
|
|
},
|
|
});
|
|
|
|
expect(parsed.evidence).toMatchObject({
|
|
schema_version: 2,
|
|
source: { patterns: ["curated/**/*.md"] },
|
|
});
|
|
});
|
|
|
|
test("rejects a schema-versioned Evidence layout that mixes source and curated runtime patterns", () => {
|
|
expectSafeEvidenceError({
|
|
...withEvidence({
|
|
type: "filesystem",
|
|
uri: "psd-clinical/evidence",
|
|
}),
|
|
evidence: {
|
|
schema_version: 2,
|
|
source: {
|
|
type: "filesystem",
|
|
uri: "psd-clinical/evidence",
|
|
patterns: ["source/**/*.md", "curated/**/*.md"],
|
|
},
|
|
},
|
|
}, /source.*curated|curated.*source/i);
|
|
});
|
|
|
|
test("rejects a schema-versioned Evidence layout that acquires source documents at runtime", () => {
|
|
expectSafeEvidenceError({
|
|
...withEvidence({
|
|
type: "filesystem",
|
|
uri: "psd-clinical/evidence",
|
|
}),
|
|
evidence: {
|
|
schema_version: 2,
|
|
source: {
|
|
type: "filesystem",
|
|
uri: "psd-clinical/evidence",
|
|
patterns: ["source/**/*.md"],
|
|
},
|
|
},
|
|
}, /curated/i);
|
|
});
|
|
|
|
test.each(["curated/**/*.yaml", "curated/**"])(
|
|
"rejects a schema-versioned Evidence layout that uses the non-canonical curated pattern %s",
|
|
(pattern) => {
|
|
expectSafeEvidenceError({
|
|
...withEvidence({
|
|
type: "filesystem",
|
|
uri: "psd-clinical/evidence",
|
|
}),
|
|
evidence: {
|
|
schema_version: 2,
|
|
source: {
|
|
type: "filesystem",
|
|
uri: "psd-clinical/evidence",
|
|
patterns: [pattern],
|
|
},
|
|
},
|
|
}, /curated\/\*\*\/\*\.md/i);
|
|
},
|
|
);
|
|
|
|
test("keeps evidence optional on schema v4", () => {
|
|
expect(validateWorkspaceDescriptor(validWorkspaceObject())).not.toHaveProperty("evidence");
|
|
});
|
|
|
|
test("serializes defaulted evidence canonically and parses it without loss", () => {
|
|
const canonical = validateWorkspaceDescriptor(withEvidence({
|
|
type: "filesystem",
|
|
uri: "psd-clinical/evidence",
|
|
}));
|
|
if (canonical.workspace.schema_version !== 4) throw new Error("expected schema v4");
|
|
|
|
expect(parseWorkspaceYaml(serializeWorkspaceYaml(canonical))).toEqual(canonical);
|
|
});
|
|
|
|
const invalidFilesystemPaths = [
|
|
"/psd-clinical/evidence",
|
|
"../psd-clinical/evidence",
|
|
"./psd-clinical/evidence",
|
|
"/psd-clinical/evidence",
|
|
"psd-clinical/evidence/..",
|
|
"\\psd-clinical\\evidence",
|
|
"psd-clinical/evidence\u0000",
|
|
"other-workspace/evidence",
|
|
"psd-clinical",
|
|
"psd-clinical/evidence/nested",
|
|
];
|
|
|
|
test.each(invalidFilesystemPaths)("rejects unsafe or noncanonical filesystem URI %#", (uri) => {
|
|
expectSafeEvidenceError(withEvidence({ type: "filesystem", uri }), /evidence.*source.*uri/i);
|
|
});
|
|
|
|
const invalidPatterns = ["", "/absolute", "../escape", ".", "folder/./file", "folder//file", "folder/../file", "a\\b", "a\u0007b"];
|
|
|
|
test.each(invalidPatterns)("rejects unsafe evidence glob %#", (pattern) => {
|
|
expectSafeEvidenceError(withEvidence({
|
|
type: "filesystem",
|
|
uri: "psd-clinical/evidence",
|
|
patterns: [pattern],
|
|
}), /evidence.*source.*patterns/i);
|
|
});
|
|
|
|
test("rejects empty and duplicate filesystem patterns", () => {
|
|
const source = { type: "filesystem", uri: "psd-clinical/evidence" };
|
|
expectSafeEvidenceError(withEvidence({ ...source, patterns: [] }), /patterns/i);
|
|
expectSafeEvidenceError(withEvidence({ ...source, patterns: ["**/*.pdf", "**/*.pdf"] }), /patterns/i);
|
|
});
|
|
|
|
test.each(["ftp", "git", "unknown"])("rejects unsupported evidence discriminator %s", (type) => {
|
|
expectSafeEvidenceError(withEvidence({ type, uri: "psd-clinical/evidence" }), /evidence.*source.*type/i);
|
|
});
|
|
|
|
test("rejects unknown evidence keys", () => {
|
|
expectSafeEvidenceError({ ...withEvidence({
|
|
type: "filesystem",
|
|
uri: "psd-clinical/evidence",
|
|
}), evidence: {
|
|
source: { type: "filesystem", uri: "psd-clinical/evidence", mystery: true },
|
|
policy: explicitPolicy,
|
|
mystery: true,
|
|
} }, /unrecognized|mystery/i);
|
|
});
|
|
|
|
const credentialFields = [
|
|
"password", "api_key", "access_key", "secret_key", "session_token", "signed_url", "headers", "ca_contents",
|
|
];
|
|
|
|
test.each(credentialFields)("rejects credential-shaped evidence field %s without leaking it", (field) => {
|
|
const canary = `CANARY-${field}-DO-NOT-LEAK`;
|
|
expectSafeEvidenceError(withEvidence({
|
|
type: "filesystem",
|
|
uri: "psd-clinical/evidence",
|
|
[field]: canary,
|
|
}), /evidence.*source/i, canary);
|
|
});
|
|
|
|
const invalidHttpUris = [
|
|
"https://user:CANARY-HTTP@example.com/manifest",
|
|
"https://example.com/manifest?token=CANARY-HTTP",
|
|
"https://example.com/manifest#CANARY-HTTP",
|
|
"ftp://example.com/manifest/CANARY-HTTP",
|
|
"\nhttps://example.com/CANARY-HTTP",
|
|
];
|
|
|
|
test.each(invalidHttpUris)("rejects unsafe HTTP descriptor URI %# without leaking it", (uri) => {
|
|
expectSafeEvidenceError(withEvidence({ type: "http", uris: [uri] }), /evidence.*source.*uris.*0/i, "CANARY-HTTP");
|
|
});
|
|
|
|
test("rejects empty and canonically duplicate HTTP manifests", () => {
|
|
expectSafeEvidenceError(withEvidence({ type: "http", uris: [] }), /uris/i);
|
|
expectSafeEvidenceError(withEvidence({
|
|
type: "http",
|
|
uris: ["https://EXAMPLE.com:443/manifest", "https://example.com/manifest"],
|
|
}), /uris/i);
|
|
});
|
|
|
|
const invalidHttpBounds = [
|
|
["connect_timeout_ms", 0], ["read_timeout_ms", 0], ["max_bytes", 0],
|
|
["max_redirects", -1], ["max_cache_bytes", 0], ["connect_timeout_ms", Number.MAX_SAFE_INTEGER + 1],
|
|
] as const;
|
|
|
|
test.each(invalidHttpBounds)("rejects invalid HTTP bound %s=%s", (field, value) => {
|
|
expectSafeEvidenceError(withEvidence({
|
|
type: "http",
|
|
uris: ["https://example.com/manifest"],
|
|
[field]: value,
|
|
}), new RegExp(field, "i"));
|
|
});
|
|
|
|
const invalidS3Uris = [
|
|
"https://bucket/prefix", "s3:///prefix", "s3://user:CANARY-S3@bucket/prefix",
|
|
"s3://bucket/prefix?token=CANARY-S3", "s3://bucket/prefix#CANARY-S3",
|
|
"s3://bucket:123/CANARY-S3", "s3://bucket/%2e%2e/CANARY-S3",
|
|
"s3://127.0.0.1/CANARY-S3", "s3://UPPERCASE/CANARY-S3",
|
|
];
|
|
|
|
test.each(invalidS3Uris)("rejects invalid S3 URI %# without leaking it", (uri) => {
|
|
expectSafeEvidenceError(withEvidence({ type: "s3", uri }), /evidence.*source.*uri/i, "CANARY-S3");
|
|
});
|
|
|
|
const invalidS3Endpoints = [
|
|
{ endpoint_url: "ftp://objects.example", trusted_endpoint: true },
|
|
{ endpoint_url: "https://user:CANARY-S3@objects.example", trusted_endpoint: true },
|
|
{ endpoint_url: "https://objects.example/path", trusted_endpoint: true },
|
|
{ endpoint_url: "https://objects.example?token=CANARY-S3", trusted_endpoint: true },
|
|
{ endpoint_url: "https://objects.example#CANARY-S3", trusted_endpoint: true },
|
|
{ endpoint_url: "https://objects.example", trusted_endpoint: false },
|
|
{ endpoint_url: "http://objects.example", trusted_endpoint: true, allow_insecure_endpoint: false },
|
|
{ endpoint_url: "HTTP://objects.example", trusted_endpoint: true, allow_insecure_endpoint: false },
|
|
{ trusted_endpoint: true },
|
|
{ allow_private_endpoint: true },
|
|
{ allow_insecure_endpoint: true },
|
|
];
|
|
|
|
test.each(invalidS3Endpoints)("rejects unsafe or inconsistent S3 endpoint %#", (endpoint) => {
|
|
expectSafeEvidenceError(withEvidence({ type: "s3", uri: "s3://bucket/prefix", ...endpoint }), /evidence.*source/i, "CANARY-S3");
|
|
});
|
|
|
|
const invalidS3Bounds = [
|
|
["max_bytes", 0], ["max_objects", 0], ["max_pages", 0], ["page_size", 0],
|
|
["max_objects", Number.MAX_SAFE_INTEGER + 1],
|
|
] as const;
|
|
|
|
test.each(invalidS3Bounds)("rejects invalid S3 bound %s=%s", (field, value) => {
|
|
expectSafeEvidenceError(withEvidence({
|
|
type: "s3", uri: "s3://bucket/prefix", [field]: value,
|
|
}), new RegExp(field, "i"));
|
|
});
|
|
|
|
test.each([
|
|
["max_chunk_chars", 0],
|
|
["max_chunk_chars", Number.MAX_SAFE_INTEGER + 1],
|
|
["retain_published_generations", 0],
|
|
["retain_published_generations", Number.MAX_SAFE_INTEGER + 1],
|
|
] as const)("rejects invalid evidence policy bound %s=%s", (field, value) => {
|
|
expectSafeEvidenceError(withEvidence({
|
|
type: "filesystem", uri: "psd-clinical/evidence",
|
|
}, { ...explicitPolicy, [field]: value }), new RegExp(field, "i"));
|
|
});
|