fix: reject malformed cursor IDs and page discriminants

This commit is contained in:
jb
2026-09-19 10:08:10 -04:00
parent 373eb60d0b
commit 39b680bb37
2 changed files with 11 additions and 2 deletions
+7 -2
View File
@@ -259,7 +259,12 @@ export function validateInventoryCursorUse(state: InventoryCursorState, lookup:
/** Enforce the page discriminants without implementing page retrieval. */
export function validateInventoryPageResult(result: InventoryQueryResult): InventoryPageValidation {
if (result.status !== 'ready') return { valid: true };
if (result.status !== 'ready') {
return ['partial', 'stale', 'authorization_required', 'unavailable'].includes(result.status)
? { valid: true }
: { valid: false, reason: 'invalid_page_state' };
}
if (!['initial', 'continuation', 'after_end'].includes(result.pageState)) return { valid: false, reason: 'invalid_page_state' };
if (result.pageState === 'after_end') {
return result.rows.length === 0 && result.nextCursor === null
? { valid: true }
@@ -278,7 +283,7 @@ function validCursorPosition(position: unknown): boolean {
const candidate = position as { offset?: unknown; lastItemId?: unknown };
if (!Number.isSafeInteger(candidate.offset) || (candidate.offset as number) < 0) return false;
if (candidate.offset === 0) return candidate.lastItemId === null;
return typeof candidate.lastItemId === 'string' && /^(0|[1-9][0-9]*)$/.test(candidate.lastItemId);
return typeof candidate.lastItemId === 'string' && /^(0|[1-9][0-9]*)$(?![\s\S])/.test(candidate.lastItemId);
}
function sameSelectedResourceList(left: readonly SelectedResource[], right: readonly SelectedResource[]): boolean {
+4
View File
@@ -58,6 +58,8 @@ describe('T02 typed contract fixtures', () => {
expect(validateInventoryCursorUse(inventoryCursorState, { ...inventoryCursorLookup, selectedResources: [syntheticResource, syntheticResource] })).toEqual({ valid: false, reason: 'resource_mismatch' });
expect(validateInventoryCursorUse({ ...inventoryCursorState, position: { offset: -1, lastItemId: null } } as unknown as typeof inventoryCursorState, inventoryCursorLookup)).toEqual({ valid: false, reason: 'malformed_state' });
expect(validateInventoryCursorUse({ ...inventoryCursorState, position: { offset: 1, lastItemId: '01' } }, inventoryCursorLookup)).toEqual({ valid: false, reason: 'malformed_state' });
expect(validateInventoryCursorUse({ ...inventoryCursorState, position: { offset: 1, lastItemId: '1\n' } }, inventoryCursorLookup)).toEqual({ valid: false, reason: 'malformed_state' });
expect(validateInventoryCursorUse({ ...inventoryCursorState, status: 'unexpected' } as unknown as typeof inventoryCursorState, inventoryCursorLookup)).toEqual({ valid: false, reason: 'malformed_state' });
expect(inventoryCursorState.pin.authorizationEpochs['character:90000000000000001']).toBe(1);
expect(inventoryResult.status).toBe('ready');
if (inventoryResult.status === 'ready') {
@@ -66,6 +68,8 @@ describe('T02 typed contract fixtures', () => {
expect(inventoryResult.provenance.input.snapshotGenerations.assets).toBe('assets-g7');
expect(inventoryResult.provenance.input.referenceVersion).toBe('reference-g1');
expect(validateInventoryPageResult(inventoryResult)).toEqual({ valid: true });
expect(validateInventoryPageResult({ ...inventoryResult, pageState: 'unexpected' } as unknown as typeof inventoryResult)).toEqual({ valid: false, reason: 'invalid_page_state' });
expect(validateInventoryPageResult({ ...inventoryResult, status: 'unexpected' } as unknown as typeof inventoryResult)).toEqual({ valid: false, reason: 'invalid_page_state' });
expect(validateCompleteReportPins(inventoryResult.provenance.input)).toEqual({ valid: true });
expect(validateCompleteReportPins({ ...inventoryResult.provenance.input, selectedResources: [syntheticResource, syntheticResource] })).toEqual({ valid: false, reason: 'duplicate_resource' });
expect(validateCompleteReportPins({ ...inventoryResult.provenance.input, datasetPins: [inventoryResult.provenance.input.datasetPins[0]!, inventoryResult.provenance.input.datasetPins[0]!] })).toEqual({ valid: false, reason: 'duplicate_pin' });