fix: reject malformed cursor IDs and page discriminants
This commit is contained in:
@@ -259,7 +259,12 @@ export function validateInventoryCursorUse(state: InventoryCursorState, lookup:
|
|||||||
|
|
||||||
/** Enforce the page discriminants without implementing page retrieval. */
|
/** Enforce the page discriminants without implementing page retrieval. */
|
||||||
export function validateInventoryPageResult(result: InventoryQueryResult): InventoryPageValidation {
|
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') {
|
if (result.pageState === 'after_end') {
|
||||||
return result.rows.length === 0 && result.nextCursor === null
|
return result.rows.length === 0 && result.nextCursor === null
|
||||||
? { valid: true }
|
? { valid: true }
|
||||||
@@ -278,7 +283,7 @@ function validCursorPosition(position: unknown): boolean {
|
|||||||
const candidate = position as { offset?: unknown; lastItemId?: unknown };
|
const candidate = position as { offset?: unknown; lastItemId?: unknown };
|
||||||
if (!Number.isSafeInteger(candidate.offset) || (candidate.offset as number) < 0) return false;
|
if (!Number.isSafeInteger(candidate.offset) || (candidate.offset as number) < 0) return false;
|
||||||
if (candidate.offset === 0) return candidate.lastItemId === null;
|
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 {
|
function sameSelectedResourceList(left: readonly SelectedResource[], right: readonly SelectedResource[]): boolean {
|
||||||
|
|||||||
@@ -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, { ...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: 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: '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(inventoryCursorState.pin.authorizationEpochs['character:90000000000000001']).toBe(1);
|
||||||
expect(inventoryResult.status).toBe('ready');
|
expect(inventoryResult.status).toBe('ready');
|
||||||
if (inventoryResult.status === '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.snapshotGenerations.assets).toBe('assets-g7');
|
||||||
expect(inventoryResult.provenance.input.referenceVersion).toBe('reference-g1');
|
expect(inventoryResult.provenance.input.referenceVersion).toBe('reference-g1');
|
||||||
expect(validateInventoryPageResult(inventoryResult)).toEqual({ valid: true });
|
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)).toEqual({ valid: true });
|
||||||
expect(validateCompleteReportPins({ ...inventoryResult.provenance.input, selectedResources: [syntheticResource, syntheticResource] })).toEqual({ valid: false, reason: 'duplicate_resource' });
|
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' });
|
expect(validateCompleteReportPins({ ...inventoryResult.provenance.input, datasetPins: [inventoryResult.provenance.input.datasetPins[0]!, inventoryResult.provenance.input.datasetPins[0]!] })).toEqual({ valid: false, reason: 'duplicate_pin' });
|
||||||
|
|||||||
Reference in New Issue
Block a user