import { describe, expect, it } from 'vitest'; import type { ParserDiagnostic } from '../model/diagnostics.js'; import { DEFAULT_ONENOTE_PARSER_LIMITS } from '../parser/limits.js'; import type { ExGuid, ObjectPropSet, ObjectSpace, PropertyEntry, StoreObject, } from '../onestore/types.js'; import { exGuidKey } from '../onestore/types.js'; import { parseContentObjects } from './content.js'; import { JCID, PROPERTY } from './constants.js'; const GUID = '{55555555-5555-5555-5555-555555555555}'; describe('content table allocation safety', () => { it('rejects a max-u32 declared column count before bitmap allocation', () => { const tableId: ExGuid = { guid: GUID, value: 1 }; const table = object( tableId, JCID.TableNode, props( u32Entry(PROPERTY.RowCount, 1), u32Entry(PROPERTY.ColumnCount, 0xffff_ffff), bytesEntry(PROPERTY.TableColumnsLocked, Uint8Array.of(1, 1)) ) ); const diagnostics: ParserDiagnostic[] = []; const space: ObjectSpace = { id: { guid: GUID, value: 0 }, roots: new Map(), objects: new Map([[exGuidKey(table.id), table]]), }; expect( parseContentObjects([tableId], { space, limits: { ...DEFAULT_ONENOTE_PARSER_LIMITS }, diagnostics, resources: new Map(), }) ).toEqual([]); expect(diagnostics).toContainEqual( expect.objectContaining({ code: 'CONTENT_PARSE_FAILED', message: expect.stringContaining( `Declared table column count exceeds ${DEFAULT_ONENOTE_PARSER_LIMITS.maxTableCells}` ), }) ); }); it('rejects declared table dimensions whose product exceeds the cell limit', () => { const tableId: ExGuid = { guid: GUID, value: 2 }; const table = object( tableId, JCID.TableNode, props( u32Entry(PROPERTY.RowCount, 1_001), u32Entry(PROPERTY.ColumnCount, 1_000) ) ); const diagnostics: ParserDiagnostic[] = []; expect( parseContentObjects([tableId], { space: { id: { guid: GUID, value: 0 }, roots: new Map(), objects: new Map([[exGuidKey(table.id), table]]), }, limits: { ...DEFAULT_ONENOTE_PARSER_LIMITS }, diagnostics, resources: new Map(), }) ).toEqual([]); expect(diagnostics[0]?.message).toContain( `Declared table cell count exceeds ${DEFAULT_ONENOTE_PARSER_LIMITS.maxTableCells}` ); }); it('rejects actual row references before mapping an oversized array', () => { const tableId: ExGuid = { guid: GUID, value: 3 }; const firstRowId: ExGuid = { guid: GUID, value: 4 }; const secondRowId: ExGuid = { guid: GUID, value: 5 }; const table = object( tableId, JCID.TableNode, props(referenceEntry(PROPERTY.ElementChildNodes, 2)) ); table.resolvedObjectIds = [firstRowId, secondRowId]; const firstRow = object(firstRowId, JCID.TableRowNode, props()); const secondRow = object(secondRowId, JCID.TableRowNode, props()); const diagnostics: ParserDiagnostic[] = []; expect( parseContentObjects([tableId], { space: { id: { guid: GUID, value: 0 }, roots: new Map(), objects: new Map( [table, firstRow, secondRow].map((value) => [ exGuidKey(value.id), value, ]) ), }, limits: { ...DEFAULT_ONENOTE_PARSER_LIMITS, maxTableCells: 1 }, diagnostics, resources: new Map(), }) ).toEqual([]); expect(diagnostics[0]?.message).toContain( 'Table row reference count exceeds 1' ); }); }); function object( id: ExGuid, jcid: number, objectProps: ObjectPropSet ): StoreObject { return { id, jcid, props: objectProps, mapping: new Map(), contextId: { guid: GUID, value: 0 }, }; } function props(...entries: PropertyEntry[]): ObjectPropSet { return { objectIds: [], objectSpaceIds: [], contextIds: [], properties: { entries }, }; } function u32Entry(rawId: number, value: number): PropertyEntry { return { rawId, id: rawId & 0x03ff_ffff, type: 0x4, value: { kind: 'u32', value }, }; } function bytesEntry(rawId: number, value: Uint8Array): PropertyEntry { return { rawId, id: rawId & 0x03ff_ffff, type: 0x7, value: { kind: 'bytes', value }, }; } function referenceEntry(rawId: number, count: number): PropertyEntry { return { rawId, id: rawId & 0x03ff_ffff, type: 0x9, value: { kind: 'objectIds', count }, }; }