fix: bound aggregate nested property sets

This commit is contained in:
2026-07-22 20:15:46 +02:00
parent 6a87ef6510
commit edb19bfcc8
2 changed files with 92 additions and 7 deletions

View File

@@ -27,6 +27,11 @@ interface ObjectStreamHeader {
osidStreamNotPresent: boolean;
}
interface PropertySetParseContext {
limits: OneNoteParserLimits;
nestedSetCount: number;
}
export function parseObjectPropSet(
bytes: Uint8Array,
reference: ChunkReference,
@@ -77,7 +82,7 @@ function parseObjectPropSetReader(
}
}
const properties = readPropertySet(reader, limits, 0);
const properties = readPropertySet(reader, { limits, nestedSetCount: 0 }, 0);
return { objectIds, objectSpaceIds, contextIds, properties };
}
@@ -180,9 +185,10 @@ function readCompactIds(reader: BinaryReader, count: number): CompactId[] {
function readPropertySet(
reader: BinaryReader,
limits: OneNoteParserLimits,
context: PropertySetParseContext,
depth: number
): PropertySet {
const { limits } = context;
if (depth > limits.maxPropertyDepth) {
throw new OneNoteParserError(
'LIMIT_EXCEEDED',
@@ -209,7 +215,7 @@ function readPropertySet(
rawId,
id: rawId & 0x03ffffff,
type,
value: readPropertyValue(reader, rawId, type, limits, depth),
value: readPropertyValue(reader, rawId, type, context, depth),
} satisfies PropertyEntry;
});
return { entries };
@@ -219,9 +225,10 @@ function readPropertyValue(
reader: BinaryReader,
rawId: number,
type: number,
limits: OneNoteParserLimits,
context: PropertySetParseContext,
depth: number
): PropertyValue {
const { limits } = context;
switch (type) {
case 0x1:
return { kind: 'empty' };
@@ -262,19 +269,23 @@ function readPropertyValue(
case 0xd:
return { kind: 'contextIds', count: boundedCount(reader, limits) };
case 0x10: {
const countOffset = reader.offset;
const count = boundedCount(reader, limits);
reserveNestedPropertySets(context, count, countOffset);
const id = reader.u32();
const sets: PropertySet[] = [];
for (let index = 0; index < count; index += 1) {
sets.push(readPropertySet(reader, limits, depth + 1));
sets.push(readPropertySet(reader, context, depth + 1));
}
return { kind: 'propertyValues', id, sets };
}
case 0x11:
case 0x11: {
reserveNestedPropertySets(context, 1, reader.offset);
return {
kind: 'propertySet',
set: readPropertySet(reader, limits, depth + 1),
set: readPropertySet(reader, context, depth + 1),
};
}
default:
throw new OneNoteParserError(
'INVALID_STRUCTURE',
@@ -284,6 +295,21 @@ function readPropertyValue(
}
}
function reserveNestedPropertySets(
context: PropertySetParseContext,
count: number,
offset: number
): void {
if (count > context.limits.maxObjectReferences - context.nestedSetCount) {
throw new OneNoteParserError(
'LIMIT_EXCEEDED',
`Nested PropertySet count exceeds ${context.limits.maxObjectReferences}`,
{ offset, structure: 'Nested PropertySets' }
);
}
context.nestedSetCount += count;
}
function boundedCount(
reader: BinaryReader,
limits: OneNoteParserLimits