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 SECTION_COUNT = 4; const PAYLOAD_OFFSET = HEADER_BYTES + DESCRIPTOR_BYTES * SECTION_COUNT; function descriptorOffset(index: number): number { return HEADER_BYTES + index * DESCRIPTOR_BYTES; } function expectCodecFailure( operation: () => unknown, code: ToolcraftModelDocumentCodecFailureCode, ): void { try { operation(); throw new Error("Expected codec operation to fail."); } catch (error) { expect(error).toBeInstanceOf(ToolcraftModelDocumentCodecError); expect(error).toMatchObject({ code }); } } function rewriteMetadata( mutate: (metadata: Record) => void, ): Uint8Array { const encoded = encodeToolcraftModelDocument(createValidModelDocument()); const sourceView = new DataView( encoded.buffer, encoded.byteOffset, encoded.byteLength, ); const metadataLength = Number( sourceView.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 firstPrimitive(metadata: Record): Record { return (metadata.primitives as Record[])[0]!; } describe("canonical model document adversarial envelopes", () => { it("rejects an unsafe metadata integer", () => { const encoded = rewriteMetadata((metadata) => { const positions = firstPrimitive(metadata).positions as Record< string, unknown >; positions.offset = Number.MAX_SAFE_INTEGER + 1; }); expectCodecFailure( () => decodeToolcraftModelDocument(encoded), "unsafe-metadata-integer", ); }); it("enforces raw vertex components before allocating malformed payloads", () => { const encoded = rewriteMetadata((metadata) => { const positions = firstPrimitive(metadata).positions as Record< string, unknown >; positions.count = TOOLCRAFT_MODEL_IMPORT_LIMIT_CEILINGS.maxVertices * 3 + 1; }); expectCodecFailure( () => decodeToolcraftModelDocument(encoded), "max-vertices-exceeded", ); }); it("honors narrowed allocation limits on encode and decode", () => { const document = createValidModelDocument(); const encoded = encodeToolcraftModelDocument(document); expectCodecFailure( () => encodeToolcraftModelDocument(document, { maxDecodedBytes: 80 }), "encoded-size-limit-exceeded", ); expectCodecFailure( () => decodeToolcraftModelDocument(encoded, { maxVertices: 2 }), "max-vertices-exceeded", ); }); it("rejects malformed section counts, offsets, and flags", () => { const invalidCount = encodeToolcraftModelDocument( createValidModelDocument(), ); new DataView(invalidCount.buffer).setUint16(10, SECTION_COUNT + 1, true); expectCodecFailure( () => decodeToolcraftModelDocument(invalidCount), "invalid-section-count", ); const invalidOffset = encodeToolcraftModelDocument( createValidModelDocument(), ); new DataView(invalidOffset.buffer).setUint32(12, PAYLOAD_OFFSET + 1, true); expectCodecFailure( () => decodeToolcraftModelDocument(invalidOffset), "invalid-header-size", ); const invalidFlags = encodeToolcraftModelDocument( createValidModelDocument(), ); new DataView(invalidFlags.buffer).setUint32( descriptorOffset(2) + 4, 1, true, ); expectCodecFailure( () => decodeToolcraftModelDocument(invalidFlags), "invalid-section-flags", ); }); });