import assert from "node:assert/strict"; import crypto from "node:crypto"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { pathToFileURL } from "node:url"; import test from "node:test"; import ts from "typescript"; const repoRoot = path.resolve(import.meta.dirname, "../../.."); const temporary = fs.mkdtempSync(path.join(os.tmpdir(), "external-vfont-unit-")); for (const name of ["asset-path", "external-vfont"]) { const sourcePath = path.join(repoRoot, `web/protocol/${name}.ts`); const transpiled = ts.transpileModule(fs.readFileSync(sourcePath, "utf8"), { compilerOptions: { module: ts.ModuleKind.ES2022, target: ts.ScriptTarget.ES2022 }, fileName: sourcePath, reportDiagnostics: true, }); assert.deepEqual(transpiled.diagnostics, []); fs.writeFileSync(path.join(temporary, `${name}.mjs`), transpiled.outputText.replace('"./asset-path"', '"./asset-path.mjs"')); } const font = await import(pathToFileURL(path.join(temporary, "external-vfont.mjs"))); const importSourcePath = path.join(repoRoot, "web/app/src/fonts/external-vfont-import.ts"); const importTranspiled = ts.transpileModule(fs.readFileSync(importSourcePath, "utf8"), { compilerOptions: { module: ts.ModuleKind.ES2022, target: ts.ScriptTarget.ES2022 }, fileName: importSourcePath, reportDiagnostics: true, }); assert.deepEqual(importTranspiled.diagnostics, []); fs.writeFileSync(path.join(temporary, "external-vfont-import.mjs"), importTranspiled.outputText.replace('"../../../protocol/external-vfont"', '"./external-vfont.mjs"')); const importer = await import(pathToFileURL(path.join(temporary, "external-vfont-import.mjs"))); const pfb = fs.readFileSync(path.join(repoRoot, "blender-5.2.0/release/datafiles/bfont.pfb")); const data = pfb.buffer.slice(pfb.byteOffset, pfb.byteOffset + pfb.byteLength); const digest = crypto.createHash("sha256").update(pfb).digest("hex"); const request = { sourcePath: "//fonts/bfont.pfb", mimeType: "application/x-font-type1", byteLength: pfb.byteLength, sha256: digest, data }; test("M9-01 validates a real project-relative PFB before storage or Main mutation", async () => { const protocolSource = fs.readFileSync(path.join(repoRoot, "web/protocol/external-vfont.ts"), "utf8"); assert.doesNotMatch(protocolSource, /StorageClient|WebEngineClient|storage\.worker|web-engine\.worker/); const validated = await font.validateExternalVFontImport(request); assert.deepEqual({ ...validated, data: undefined }, { schemaVersion: 1, sourcePath: "//fonts/bfont.pfb", fileName: "bfont.pfb", format: "PFB", mimeType: "application/x-font-type1", byteLength: 25181, sha256: "a33954fdab9fb09b9d308cb7f970518293128922ffc523c0a22b3b314a9a56c6", data: undefined, }); assert.notEqual(validated.data, request.data); assert.deepEqual(new Uint8Array(validated.data), new Uint8Array(request.data)); }); test("M9-01 blocks path escape, type spoofing, size overflow and hash drift", async () => { const cases = [ [{ ...request, sourcePath: "../../outside.pfb" }, "NON_MESH_RESOURCE_OUTSIDE_PROJECT"], [{ ...request, sourcePath: "//assets/bfont.pfb" }, "NON_MESH_RESOURCE_OUTSIDE_PROJECT"], [{ ...request, sourcePath: "//fonts/bfont.ttf", mimeType: "font/ttf" }, "NON_MESH_BINARY_INVALID"], [{ ...request, mimeType: "font/otf" }, "NON_MESH_BINARY_INVALID"], [{ ...request, byteLength: font.EXTERNAL_VFONT_MAX_BYTES + 1 }, "NON_MESH_DATA_BUDGET_EXCEEDED"], [{ ...request, byteLength: request.byteLength - 1 }, "NON_MESH_BINARY_INVALID"], [{ ...request, sha256: "0".repeat(64) }, "ASSET_SOURCE_HASH_MISMATCH"], ]; for (const [value, code] of cases) await assert.rejects(font.validateExternalVFontImport(value), (error) => error.code === code); }); test("M9-02 accepts only a matching OPFS content-addressed receipt before Main import", async () => { const validated = await font.validateExternalVFontImport(request); const stored = { assetId: `sha256:${digest}`, projectId: "m9-vfont-unit", sha256: digest, bytes: pfb.byteLength, mimeType: request.mimeType, sourcePath: "fonts/bfont.pfb", path: `projects/m9-vfont-unit/assets/sha256/${digest.slice(0, 2)}/${digest}`, createdAt: "2026-08-16T00:00:00.000Z", lastAccessAt: "2026-08-16T00:00:00.000Z", persisted: true, deduplicated: false, }; const mainImport = font.createExternalVFontMainImport(validated, stored); assert.deepEqual({ ...mainImport, data: undefined }, { schemaVersion: 1, projectId: stored.projectId, assetId: stored.assetId, assetPath: stored.path, sourcePath: "//fonts/bfont.pfb", name: "bfont", format: "PFB", mimeType: request.mimeType, byteLength: pfb.byteLength, sha256: digest, data: undefined, }); assert.notEqual(mainImport.data, validated.data); for (const receipt of [ { ...stored, persisted: false }, { ...stored, sha256: "0".repeat(64) }, { ...stored, bytes: stored.bytes - 1 }, { ...stored, path: `indexeddb:${stored.projectId}:${digest}` }, ]) { assert.throws(() => font.createExternalVFontMainImport(validated, receipt), (error) => error.code === "ASSET_SOURCE_HASH_MISMATCH" || error.code === "NON_MESH_RESOURCE_MISSING"); } }); test("M9-03 verifies the project asset before one Main font-style replacement", async () => { const stored = { asset: { assetId: `sha256:${digest}`, projectId: "m9-vfont-unit", sha256: digest, bytes: pfb.byteLength, mimeType: request.mimeType, sourcePath: "fonts/bfont.pfb", path: `projects/m9-vfont-unit/assets/sha256/${digest.slice(0, 2)}/${digest}`, createdAt: "2026-08-16T00:00:00.000Z", lastAccessAt: "2026-08-16T00:00:00.000Z", }, data: data.slice(0), }; const vfont = { id: "vfont:bfont", name: "bfont", sourcePath: "//fonts/bfont.pfb", builtin: false, packed: true, packedByteLength: pfb.byteLength, sha256: digest }; const original = { regular: "vfont:Bfont", bold: "vfont:Bfont", italic: "vfont:Bfont", boldItalic: "vfont:Bfont" }; const snapshot = { nonMeshData: [{ id: "data:font", type: "FONT", fontLinks: original }], vfonts: [vfont] }; const events = []; const result = await importer.replaceExternalVFontStyleInMain({ projectId: stored.asset.projectId, sha256: digest, dataId: "data:font", vfontId: vfont.id, style: "regular", snapshot, storage: { readAsset: async () => { events.push("storage:verified"); return stored; } }, engine: { applyCommand: async (command) => { events.push("main:committed"); return { snapshot: { ...snapshot, nonMeshData: [{ ...snapshot.nonMeshData[0], fontLinks: command.links }] } }; } }, }); assert.deepEqual(events, ["storage:verified", "main:committed"]); assert.deepEqual(result.previousLinks, original); assert.equal(result.links.regular, vfont.id); }); test("M9-03 blocks a missing project asset before Main replacement", async () => { let mainCalls = 0; const snapshot = { nonMeshData: [{ id: "data:font", type: "FONT", fontLinks: { regular: "vfont:Bfont", bold: "vfont:Bfont", italic: "vfont:Bfont", boldItalic: "vfont:Bfont" } }], vfonts: [{ id: "vfont:bfont", name: "bfont", sourcePath: "//fonts/bfont.pfb", builtin: false, packed: true, packedByteLength: pfb.byteLength, sha256: digest }], }; await assert.rejects(importer.replaceExternalVFontStyleInMain({ projectId: "m9-vfont-unit", sha256: digest, dataId: "data:font", vfontId: "vfont:bfont", style: "regular", snapshot, storage: { readAsset: async () => { const error = new Error("missing"); error.code = "NON_MESH_RESOURCE_MISSING"; throw error; } }, engine: { applyCommand: async () => { mainCalls += 1; throw new Error("unexpected Main call"); } }, }), (error) => error.code === "NON_MESH_RESOURCE_MISSING"); assert.equal(mainCalls, 0); }); test.after(() => fs.rmSync(temporary, { recursive: true, force: true }));