import { describe, expect, it, vi } from "vitest"; import { encodeToolcraftModelDocument } from "../canonical/model-document-codec"; import { digestToolcraftModelDocument } from "../canonical/model-document-digest"; import { createModelPrimitive, createValidModelDocument, } from "../canonical/model-document-test-support"; import { TOOLCRAFT_DEFAULT_MODEL_IMPORT_LIMITS } from "../model-import-limits"; import { analyzeToolcraftModel } from "../topology/model-topology-analysis"; import { createToolcraftModelWorkerClient } from "./model-import-worker-client"; import { deferred, FakeWorker, forgedDigest, importRequest, } from "./model-import-worker-client-test-support"; import { snapshotToolcraftModelWorkerAnalysisSummary } from "./model-import-worker-diagnostics"; import type { ToolcraftModelDecodeAnalyzeWorkerResult, ToolcraftModelWorkerRequest, ToolcraftModelWorkerResponse, } from "./model-import-worker-protocol"; import { createToolcraftModelWorkerDraftReceiptDigest, createToolcraftModelWorkerReceiptDigest, digestToolcraftModelWorkerBytes, } from "./model-import-worker-receipt"; function repairableResult( request: ToolcraftModelWorkerRequest, ): ToolcraftModelWorkerResponse { const document = createValidModelDocument({ primitives: [createModelPrimitive({ normals: undefined })], }); const analysis = snapshotToolcraftModelWorkerAnalysisSummary( analyzeToolcraftModel( document, "realtime-mesh", TOOLCRAFT_DEFAULT_MODEL_IMPORT_LIMITS, ), ); const canonicalDocument = new Uint8Array( encodeToolcraftModelDocument(document), ).slice().buffer; const opaquePayload = new Uint8Array(256 * 1024); opaquePayload.set(new TextEncoder().encode( "This is intentionally not a decodable repair-plan frame.", )); const opaqueBytes = opaquePayload.buffer; const planDigest = `sha256:${"1".repeat(64)}`; const repairPlanEnvelope = { bytes: opaqueBytes, envelopeDigest: digestToolcraftModelWorkerBytes(opaqueBytes), planDigest, version: 1 as const, }; const resultWithoutReceipt = { analysis, appearanceResources: [], canonicalDocument, canonicalDocumentDigest: digestToolcraftModelDocument(document), operation: "decode-and-analyze" as const, repairPlanEnvelope, }; const result: ToolcraftModelDecodeAnalyzeWorkerResult = { ...resultWithoutReceipt, receiptDigest: createToolcraftModelWorkerReceiptDigest({ generation: request.generation, jobId: request.jobId, result: resultWithoutReceipt, }), }; return { generation: request.generation, jobId: request.jobId, kind: "result", result, }; } function emitRepairableLifecycle( worker: FakeWorker, request: ToolcraftModelWorkerRequest, response: ToolcraftModelWorkerResponse, ): void { if (response.kind !== "result" || response.result.operation !== "decode-and-analyze") { throw new Error("Expected decode result."); } worker.emitMessage({ generation: request.generation, jobId: request.jobId, kind: "progress", phase: "decoding", progress: 0.9, }); const draftWithoutReceipt = { appearanceResources: [], canonicalDocument: response.result.canonicalDocument.slice(0), canonicalDocumentDigest: response.result.canonicalDocumentDigest, }; worker.emitMessage({ draft: { ...draftWithoutReceipt, receiptDigest: createToolcraftModelWorkerDraftReceiptDigest({ draft: draftWithoutReceipt, generation: request.generation, jobId: request.jobId, }), }, generation: request.generation, jobId: request.jobId, kind: "draft", }); worker.emitMessage({ generation: request.generation, jobId: request.jobId, kind: "progress", phase: "analyzing", progress: 0.9, }); } describe("model worker main-thread trust boundary", () => { it("authenticates but never parses or deep-walks an opaque repair plan", async () => { const worker = new FakeWorker(); const client = createToolcraftModelWorkerClient({ workerFactory: () => worker }); const pending = client.importModel(importRequest("opaque-plan")); const request = worker.posts[0]!.message; const parse = vi.spyOn(JSON, "parse"); const result = repairableResult(request); emitRepairableLifecycle(worker, request, result); worker.emitMessage(result); const response = await pending; expect(parse).not.toHaveBeenCalled(); parse.mockRestore(); expect(response.kind).toBe("result"); if (response.kind !== "result" || response.result.operation !== "decode-and-analyze") { throw new Error("Expected decode result."); } expect(Object.prototype.toString.call( response.result.repairPlanEnvelope?.bytes, )).toBe("[object ArrayBuffer]"); expect(response.result.repairPlanEnvelope?.bytes.byteLength).toBeGreaterThan( 100_000, ); expect("repairPlan" in response.result.analysis).toBe(false); expect(Object.isFrozen(response.result.analysis)).toBe(true); expect(Object.isFrozen(response.result.analysis.diagnostics)).toBe(true); }); it.each(["envelope digest", "receipt", "bounded metadata"] as const)( "rejects a forged %s before publication", async (forgery) => { const worker = new FakeWorker(); const onResult = vi.fn(); const client = createToolcraftModelWorkerClient({ workerFactory: () => worker }); const pending = client.importModel(importRequest(`forged-${forgery}`), { onResult }); const request = worker.posts[0]!.message; const response = structuredClone(repairableResult(request)); if (response.kind !== "result" || response.result.operation !== "decode-and-analyze") { throw new Error("Expected decode result."); } if (forgery === "envelope digest") { (response.result.repairPlanEnvelope as { envelopeDigest: string }) .envelopeDigest = forgedDigest; } else if (forgery === "receipt") { (response.result as { receiptDigest: string }).receiptDigest = forgedDigest; } else { const diagnostics = response.result.analysis.diagnostics as unknown as Array<(typeof response.result.analysis.diagnostics)[number]>; diagnostics[0] = { ...response.result.analysis.diagnostics[0]!, explanation: "x".repeat(10_000), }; } emitRepairableLifecycle(worker, request, response); worker.emitMessage(response); await expect(pending).resolves.toMatchObject({ feedback: { code: "model-worker-invalid-message" }, kind: "error", }); expect(onResult).not.toHaveBeenCalled(); expect(worker.terminate).toHaveBeenCalledTimes(1); }, ); it("cannot publish when superseded during envelope digest verification", async () => { const workers: FakeWorker[] = []; const envelopeVerification = deferred(); const sha256 = vi.fn(async (bytes: ArrayBuffer | Uint8Array) => digestToolcraftModelWorkerBytes(bytes) ); sha256.mockImplementationOnce(async (bytes) => digestToolcraftModelWorkerBytes(bytes) ); sha256.mockImplementationOnce(async (bytes) => digestToolcraftModelWorkerBytes(bytes) ); sha256.mockImplementationOnce(async (bytes) => digestToolcraftModelWorkerBytes(bytes) ); sha256.mockImplementationOnce(() => envelopeVerification.promise); const onResult = vi.fn(); const client = createToolcraftModelWorkerClient({ sha256, workerFactory: () => { const worker = new FakeWorker(); workers.push(worker); return worker; }, }); const first = client.importModel(importRequest("verifying-envelope"), { onResult }); const firstRequest = workers[0]!.posts[0]!.message; const firstResponse = repairableResult(firstRequest); emitRepairableLifecycle(workers[0]!, firstRequest, firstResponse); workers[0]!.emitMessage(firstResponse); await vi.waitFor(() => expect(sha256).toHaveBeenCalledTimes(4)); const second = client.importModel(importRequest("new-generation")); await expect(first).resolves.toMatchObject({ kind: "cancelled" }); if (firstResponse.kind !== "result" || firstResponse.result.operation !== "decode-and-analyze") { throw new Error("Expected decode result."); } envelopeVerification.resolve( firstResponse.result.repairPlanEnvelope!.envelopeDigest, ); await Promise.resolve(); await Promise.resolve(); expect(onResult).not.toHaveBeenCalled(); const secondRequest = workers[1]!.posts[0]!.message; const secondResponse = repairableResult(secondRequest); emitRepairableLifecycle(workers[1]!, secondRequest, secondResponse); workers[1]!.emitMessage(secondResponse); await expect(second).resolves.toMatchObject({ kind: "result" }); }); });