fix: close evidence credential disclosure gaps

This commit is contained in:
2026-08-09 19:15:00 +02:00
parent 7126b567b0
commit df8dc1e219
7 changed files with 100 additions and 13 deletions
+46
View File
@@ -5,6 +5,7 @@ import { expect, test, vi } from "vitest";
import yazl from "yazl";
import { buildApp } from "../src/app.js";
import { loadConfig } from "../src/config.js";
import { createProductionWorkspaceDiagnoser } from "../src/workspaces/diagnostics.js";
import { WorkspaceRegistryError } from "../src/workspaces/git-repository.js";
import type { WorkspaceRegistry, WorkspaceRevision } from "../src/workspaces/registry.js";
import { renderWorkspaceDocs, serializeWorkspaceYaml, type CanonicalWorkspace, type WorkspaceV2 } from "../src/workspaces/schema.js";
@@ -283,6 +284,51 @@ test("runs diagnostics for a schema v3 workspace without external semantic bindi
}), { writeProbe: false });
});
test("reports missing Evidence binding through the real test route without changing registry revision", async () => {
const evidenceWorkspace: CanonicalWorkspace = {
...workspace,
evidence: {
source: {
type: "http",
uris: ["https://evidence.example.test/guide.md"],
authentication: "signed_urls_file",
connect_timeout_ms: 5_000,
read_timeout_ms: 30_000,
max_bytes: 10 * 1024 * 1024,
max_redirects: 5,
allow_private_hosts: false,
max_cache_bytes: 64 * 1024 * 1024,
},
policy: { max_chunk_chars: 4_000, retain_published_generations: 3 },
},
};
const read = vi.fn(async () => ({ workspace: evidenceWorkspace, revision }));
const registry = registryFake({ read });
const app = appFor(registry, createProductionWorkspaceDiagnoser(100));
const variable = "THT_WS_PSD_CLINICAL_EVIDENCE_SIGNED_URLS_FILE";
const previous = process.env[variable];
delete process.env[variable];
try {
const res = await app.inject({ method: "POST", url: "/workspaces/psd-clinical/test", payload: {} });
expect(res.statusCode).toBe(200);
const body = res.json();
expect(body.activatable).toBe(false);
expect(body.diagnostics).toEqual(expect.arrayContaining([expect.objectContaining({
code: "binding_missing",
field: "evidence.source.authentication",
variable,
})]));
expect(read).toHaveBeenCalledTimes(1);
expect(registry.publish).not.toHaveBeenCalled();
expect(revision).toMatchObject({ commit: "a".repeat(40), blob: "b".repeat(40) });
} finally {
if (previous === undefined) delete process.env[variable];
else process.env[variable] = previous;
}
});
test("returns a 409 field conflict instead of overwriting a changed workspace", async () => {
const conflict = Object.assign(
new WorkspaceRegistryError("workspace_conflict", "Workspace has changed"),