From 764af94ccc5a9dd5bdd882b39e431ab249ae62fe Mon Sep 17 00:00:00 2001 From: Cody Tseng <64680921+CodyTseng@users.noreply.github.com> Date: Wed, 31 Jan 2024 20:27:59 +0800 Subject: [PATCH] feat: adjust validation logic for tag value (#11) --- packages/validator/__test__/validator.spec.ts | 47 ++++++++++++++++--- .../src/schemas/auth-message.schema.ts | 5 +- .../validator/src/schemas/common.schema.ts | 28 +++++------ .../src/schemas/event-message.schema.ts | 10 +--- .../src/schemas/req-message.schema.ts | 12 ++--- packages/validator/src/types.ts | 8 +--- packages/validator/src/validator.ts | 3 +- 7 files changed, 63 insertions(+), 50 deletions(-) diff --git a/packages/validator/__test__/validator.spec.ts b/packages/validator/__test__/validator.spec.ts index 989baa43..3362ef32 100644 --- a/packages/validator/__test__/validator.spec.ts +++ b/packages/validator/__test__/validator.spec.ts @@ -35,7 +35,7 @@ describe('Validator', () => { ).toEqual(eventMessage); }); - it('should throw error if event message is invalid', () => { + it('should throw error if event message is invalid', async () => { const event = { id: '0', pubkey: @@ -48,16 +48,16 @@ describe('Validator', () => { }; const eventMessage = [MessageType.EVENT, event]; - expect(validator.validateIncomingMessage(eventMessage)).rejects.toThrow( - 'invalid:', - ); + await expect( + validator.validateIncomingMessage(eventMessage), + ).rejects.toThrow('invalid:'); jest .spyOn(validator['incomingMessageSchema'], 'parseAsync') .mockRejectedValue(new Error('test')); - expect(validator.validateIncomingMessage(eventMessage)).rejects.toThrow( - 'test', - ); + await expect( + validator.validateIncomingMessage(eventMessage), + ).rejects.toThrow('test'); }); it('should validate req message', async () => { @@ -180,5 +180,38 @@ describe('Validator', () => { expect(await validator.validateEvent(event)).toEqual(event); }); + + it('should validate tag value correctly', async () => { + const specValidator = new Validator({ maxTagValueLength: 1 }); + const event1 = { + id: '0000000000000000000000000000000000000000000000000000000000000000', + pubkey: + '0000000000000000000000000000000000000000000000000000000000000000', + kind: 1, + content: 'hello nostr', + tags: [ + ['hello', 'world'], + ['a', 'b', 'ccc'], + ], + created_at: 0, + sig: '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + }; + expect(await specValidator.validateEvent(event1)).toEqual(event1); + + const event2 = { + id: '0000000000000000000000000000000000000000000000000000000000000000', + pubkey: + '0000000000000000000000000000000000000000000000000000000000000000', + kind: 1, + content: 'hello nostr', + tags: [ + ['hello', 'world'], + ['a', 'bb'], + ], + created_at: 0, + sig: '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + }; + await expect(specValidator.validateEvent(event2)).rejects.toThrow(); + }); }); }); diff --git a/packages/validator/src/schemas/auth-message.schema.ts b/packages/validator/src/schemas/auth-message.schema.ts index 16516a83..f7a224a8 100644 --- a/packages/validator/src/schemas/auth-message.schema.ts +++ b/packages/validator/src/schemas/auth-message.schema.ts @@ -6,10 +6,7 @@ import { RequiredValidatorOptions } from '../types'; export function createAuthMessageSchema( options: Pick< RequiredValidatorOptions, - | 'maxItemsPerTag' - | 'maxLengthPerTagItem' - | 'maxNumberOfTags' - | 'maxContentLength' + 'maxTagValueLength' | 'maxNumberOfTags' | 'maxContentLength' >, ): z.ZodType { return z.tuple([z.literal(MessageType.AUTH), createEventSchema(options)]); diff --git a/packages/validator/src/schemas/common.schema.ts b/packages/validator/src/schemas/common.schema.ts index dafbfaf5..d7984553 100644 --- a/packages/validator/src/schemas/common.schema.ts +++ b/packages/validator/src/schemas/common.schema.ts @@ -28,23 +28,17 @@ export const EventSigSchema = HexStringSchema.length(128, { }); export function createEventTagSchema({ - maxItemsPerTag, - maxLengthPerTagItem, -}: Pick< - RequiredValidatorOptions, - 'maxItemsPerTag' | 'maxLengthPerTagItem' ->): z.ZodType { - return z - .array( - z - .string({ invalid_type_error: 'must be a string' }) - .max(maxLengthPerTagItem, { - message: `must be less than ${maxLengthPerTagItem} chars`, - }), - ) - .max(maxItemsPerTag, { - message: `must be less than or equal to ${maxItemsPerTag} tag items`, - }); + maxTagValueLength, +}: Pick): z.ZodType { + return z.array(z.string({ invalid_type_error: 'must be a string' })).refine( + tag => { + if (tag.length <= 1 || tag[0].length > 1) return true; + return tag[1].length <= maxTagValueLength; + }, + { + message: `tag value must be less than ${maxTagValueLength} chars`, + }, + ); } export function createEventContentSchema({ diff --git a/packages/validator/src/schemas/event-message.schema.ts b/packages/validator/src/schemas/event-message.schema.ts index 383496cc..12688aee 100644 --- a/packages/validator/src/schemas/event-message.schema.ts +++ b/packages/validator/src/schemas/event-message.schema.ts @@ -14,10 +14,7 @@ import { RequiredValidatorOptions } from '../types'; export function createEventSchema( options: Pick< RequiredValidatorOptions, - | 'maxItemsPerTag' - | 'maxLengthPerTagItem' - | 'maxNumberOfTags' - | 'maxContentLength' + 'maxTagValueLength' | 'maxNumberOfTags' | 'maxContentLength' >, ): z.ZodType { return z.object({ @@ -36,10 +33,7 @@ export function createEventSchema( export function createEventMessageSchema( options: Pick< RequiredValidatorOptions, - | 'maxItemsPerTag' - | 'maxLengthPerTagItem' - | 'maxNumberOfTags' - | 'maxContentLength' + 'maxTagValueLength' | 'maxNumberOfTags' | 'maxContentLength' >, ): z.ZodType { return z.tuple([z.literal(MessageType.EVENT), createEventSchema(options)]); diff --git a/packages/validator/src/schemas/req-message.schema.ts b/packages/validator/src/schemas/req-message.schema.ts index 92e2b839..f09e8c54 100644 --- a/packages/validator/src/schemas/req-message.schema.ts +++ b/packages/validator/src/schemas/req-message.schema.ts @@ -12,17 +12,17 @@ import { function createGenericTagFilterValuesSchema({ maxFilterGenericTagsLength, - maxLengthPerTagItem, + maxTagValueLength, }: Pick< RequiredValidatorOptions, - 'maxFilterGenericTagsLength' | 'maxLengthPerTagItem' + 'maxFilterGenericTagsLength' | 'maxTagValueLength' >): z.ZodType { return z .array( z .string({ invalid_type_error: 'must be a string' }) - .max(maxLengthPerTagItem, { - message: `must be less than or equal to ${maxLengthPerTagItem} characters`, + .max(maxTagValueLength, { + message: `must be less than or equal to ${maxTagValueLength} characters`, }), ) .min(1, { message: 'must be greater than or equal to 1 tagValues' }) @@ -39,7 +39,7 @@ export function createFilterSchema( | 'maxFilterAuthorsLength' | 'maxFilterKindsLength' | 'maxFilterGenericTagsLength' - | 'maxLengthPerTagItem' + | 'maxTagValueLength' | 'maxFilterSearchStringLength' >, ): z.ZodType { @@ -128,7 +128,7 @@ export function createReqMessageSchema( | 'maxFilterAuthorsLength' | 'maxFilterKindsLength' | 'maxFilterGenericTagsLength' - | 'maxLengthPerTagItem' + | 'maxTagValueLength' | 'maxFilterSearchStringLength' >, ): z.ZodType { diff --git a/packages/validator/src/types.ts b/packages/validator/src/types.ts index b819096e..ee34d59c 100644 --- a/packages/validator/src/types.ts +++ b/packages/validator/src/types.ts @@ -5,13 +5,9 @@ export type RawData = Buffer | ArrayBuffer | Buffer[] | string | object; */ export type ValidatorOptions = { /** - * maximum number of items per tag. `Default: 10` + * maximum length of tag value. `Default: 1024` */ - maxItemsPerTag?: number; - /** - * maximum length of each tag item. `Default: 1024` - */ - maxLengthPerTagItem?: number; + maxTagValueLength?: number; /** * maximum number of tags. `Default: 2000` */ diff --git a/packages/validator/src/validator.ts b/packages/validator/src/validator.ts index 18818c08..dd1cc976 100644 --- a/packages/validator/src/validator.ts +++ b/packages/validator/src/validator.ts @@ -88,8 +88,7 @@ export class Validator { private defaultOptions(options: ValidatorOptions): RequiredValidatorOptions { return { - maxItemsPerTag: 10, - maxLengthPerTagItem: 1024, + maxTagValueLength: 1024, maxNumberOfTags: 2000, maxContentLength: 100 * 1024, maxSubscriptionIdLength: 128,