import { describe, expect, it } from "vitest"; import { TOOLCRAFT_MODEL_IMPORT_LIMIT_CEILINGS } from "../model-import-limits"; import { decodeToolcraftModelDocument, encodeToolcraftModelDocument, ToolcraftModelDocumentCodecError, type ToolcraftModelDocumentCodecFailureCode, } from "./model-document-codec"; import { createValidModelDocument } from "./model-document-test-support"; const HEADER_BYTES = 16; const DESCRIPTOR_BYTES = 16; const PAYLOAD_OFFSET = HEADER_BYTES + DESCRIPTOR_BYTES * 4; function descriptorOffset(index: number): number { return HEADER_BYTES + index * DESCRIPTOR_BYTES; } function rewriteMetadata( mutate: (metadata: Record) => void, ): Uint8Array { const encoded = encodeToolcraftModelDocument(createValidModelDocument()); const metadataLength = Number( new DataView(encoded.buffer).getBigUint64(descriptorOffset(0) + 8, true), ); const metadata = JSON.parse( new TextDecoder().decode( encoded.subarray(PAYLOAD_OFFSET, PAYLOAD_OFFSET + metadataLength), ), ) as Record; mutate(metadata); const metadataBytes = new TextEncoder().encode(JSON.stringify(metadata)); const payload = encoded.subarray(PAYLOAD_OFFSET + metadataLength); const rewritten = new Uint8Array( PAYLOAD_OFFSET + metadataBytes.byteLength + payload.byteLength, ); rewritten.set(encoded.subarray(0, PAYLOAD_OFFSET)); rewritten.set(metadataBytes, PAYLOAD_OFFSET); rewritten.set(payload, PAYLOAD_OFFSET + metadataBytes.byteLength); new DataView(rewritten.buffer).setBigUint64( descriptorOffset(0) + 8, BigInt(metadataBytes.byteLength), true, ); return rewritten; } function firstNode(metadata: Record): Record { return (metadata.nodes as Record[])[0]!; } function firstPrimitive( metadata: Record, ): Record { return (metadata.primitives as Record[])[0]!; } function expectCodecFailure( bytes: Uint8Array, code: ToolcraftModelDocumentCodecFailureCode, limits?: { maxDecodedBytes?: number; maxEstimatedWorkerBytes?: number; }, ): void { try { decodeToolcraftModelDocument(bytes, limits); throw new Error("Expected hostile metadata to fail."); } catch (error) { expect(error).toBeInstanceOf(ToolcraftModelDocumentCodecError); expect(error).toMatchObject({ code }); } } describe("canonical model nested metadata preflight", () => { it.each([ [ "17-value localMatrix", (metadata: Record) => { (firstNode(metadata).localMatrix as unknown[]).push(0); }, ], [ "four-value document bound", (metadata: Record) => { ((metadata.bounds as Record).max as unknown[]).push(0); }, ], [ "four-value primitive bound", (metadata: Record) => { const bounds = firstPrimitive(metadata).bounds as Record; (bounds.min as unknown[]).push(0); }, ], ] as const)("rejects a fixed-cardinality %s", (_label, mutate) => { expectCodecFailure(rewriteMetadata(mutate), "invalid-metadata-shape"); }); it("rejects overlong roots before copying them", () => { const encoded = rewriteMetadata((metadata) => { metadata.rootNodeIds = Array.from( { length: TOOLCRAFT_MODEL_IMPORT_LIMIT_CEILINGS.maxNodes + 1 }, () => "node-1", ); }); expectCodecFailure(encoded, "max-nodes-exceeded"); }); it.each([ ["children", TOOLCRAFT_MODEL_IMPORT_LIMIT_CEILINGS.maxNodes], ["primitiveIds", TOOLCRAFT_MODEL_IMPORT_LIMIT_CEILINGS.maxPrimitives], ] as const)("rejects overlong node %s before copying it", (field, limit) => { const encoded = rewriteMetadata((metadata) => { firstNode(metadata)[field] = Array.from( { length: limit + 1 }, () => field === "children" ? "node-1" : "primitive-1", ); }); expectCodecFailure(encoded, "max-references-exceeded"); }); it("rejects overlong canonicalization and repair operation arrays", () => { const canonicalOperations = rewriteMetadata((metadata) => { const provenance = metadata.provenance as Record; provenance.operations = Array.from( { length: 3 }, () => "sequential-indexing", ); }); expectCodecFailure(canonicalOperations, "invalid-metadata-shape"); const repairOperations = rewriteMetadata((metadata) => { const provenance = metadata.provenance as Record; provenance.repair = { algorithmVersion: "repair@1", operations: Array.from( { length: 7 }, () => "regenerate-normals", ), planDigest: "sha256:plan", recipeId: "recipe-1", }; }); expectCodecFailure(repairOperations, "invalid-metadata-shape"); }); it("accounts metadata strings under worker memory rather than UTF-8 size", () => { const encoded = rewriteMetadata((metadata) => { firstNode(metadata).name = "x".repeat(1_100); }); expect(decodeToolcraftModelDocument(encoded, { maxDecodedBytes: 2_048, }).nodes[0]?.name).toBe("x".repeat(1_100)); expectCodecFailure( encoded, "estimated-worker-memory-limit-exceeded", { maxDecodedBytes: 2_048, maxEstimatedWorkerBytes: 32 * 1_024, }, ); }); });