fix: sanitize workspace API envelopes

This commit is contained in:
2026-08-09 20:38:54 +02:00
parent 80aa989523
commit a681c431fb
2 changed files with 103 additions and 9 deletions
+49 -1
View File
@@ -41,7 +41,7 @@ test("uploads a workspace bundle without JSON content type", async () => {
let contentType: string | null = null;
server.use(http.post("/api/workspaces/import", ({ request }) => {
contentType = request.headers.get("content-type");
return HttpResponse.json({ draft: { workspace: {} } });
return HttpResponse.json({ draft: { workspace } });
}));
await importWorkspace(new File(["zip"], "clinical.thoth-workspace.zip", { type: "application/zip" }));
@@ -52,6 +52,54 @@ test("uploads a workspace bundle without JSON content type", async () => {
expect(contentType ?? "").not.toMatch(/application\/json/i);
});
test("sanitizes imported Evidence before returning a browser draft", async () => {
server.use(http.post("/api/workspaces/import", () => HttpResponse.json({
draft: { workspace: evidenceWorkspace, contract: { variables: [] } },
})));
const result = await importWorkspace(new File(["zip"], "clinical.thoth-workspace.zip"));
expect(result.draft.workspace.evidence).toEqual(evidenceWorkspace.evidence);
expect(result.draft.workspace).not.toBe(evidenceWorkspace);
});
test("rejects imported Evidence with a secret-shaped field", async () => {
const malformed = {
...evidenceWorkspace,
evidence: { ...evidenceWorkspace.evidence, signed_urls_file: "/run/secrets/urls" },
};
server.use(http.post("/api/workspaces/import", () => HttpResponse.json({
draft: { workspace: malformed, contract: {} },
})));
await expect(importWorkspace(new File(["zip"], "clinical.thoth-workspace.zip")))
.rejects.toThrow("invalid imported workspace draft");
});
test("rejects read responses with a missing or inconsistent revision", async () => {
server.use(http.get("/api/workspaces/psd-clinical", () => HttpResponse.json({
workspace: evidenceWorkspace,
revision: { ...revision, id: "other-workspace" },
})));
await expect(getWorkspace("psd-clinical")).rejects.toThrow("invalid workspace revision");
server.use(http.get("/api/workspaces/psd-clinical", () => HttpResponse.json({
workspace: evidenceWorkspace, revision: null,
})));
await expect(getWorkspace("psd-clinical")).rejects.toThrow("invalid workspace revision");
});
test("rejects a publish response with a malformed revision", async () => {
server.use(http.post("/api/workspaces/publish", () => HttpResponse.json({
revision: { ...revision, commit: "not-a-commit" },
})));
await expect(publishWorkspace({
action: "update", workspace: evidenceWorkspace,
baseCommit: revision.commit, baseBlob: revision.blob,
})).rejects.toThrow("invalid workspace revision");
});
test("rejects a conflict payload that attempts to surface a secret field", async () => {
server.use(http.post("/api/workspaces/publish", () => HttpResponse.json({
code: "workspace_conflict", message: "Workspace changed in the registry.", fields: ["dwh.password"],