diff --git a/src/deps.ts b/src/deps.ts index ba275ba..9589484 100644 --- a/src/deps.ts +++ b/src/deps.ts @@ -20,6 +20,7 @@ export { nip05, nip19, nip21, + verifySignature, } from 'npm:nostr-tools@^1.11.1'; export { findReplyTag } from 'https://gitlab.com/soapbox-pub/mostr/-/raw/c67064aee5ade5e01597c6d23e22e53c628ef0e2/src/nostr/tags.ts'; export { parseFormData } from 'npm:formdata-helper@^0.3.0'; diff --git a/src/schema.ts b/src/schema.ts index 5edf86a..e42a898 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -1,4 +1,4 @@ -import { z } from '@/deps.ts'; +import { verifySignature, z } from '@/deps.ts'; import type { Event } from './event.ts'; @@ -67,10 +67,23 @@ const relaySchema = z.custom((relay) => { } }); +const nostrIdSchema = z.string().regex(/^[0-9a-f]{64}$/); + +const eventSchema = z.object({ + id: nostrIdSchema, + kind: z.number(), + tags: z.array(z.array(z.string())), + content: z.string(), + created_at: z.number(), + pubkey: nostrIdSchema, + sig: z.string(), +}).refine(verifySignature); + const emojiTagSchema = z.tuple([z.literal('emoji'), z.string(), z.string().url()]); export { emojiTagSchema, + eventSchema, filteredArray, jsonSchema, type MetaContent, diff --git a/src/sign.ts b/src/sign.ts index d658796..69416c9 100644 --- a/src/sign.ts +++ b/src/sign.ts @@ -1,5 +1,6 @@ import { type AppContext } from '@/app.ts'; -import { getEventHash, getPublicKey, getSignature, HTTPException } from '@/deps.ts'; +import { getEventHash, getPublicKey, getSignature, HTTPException, z } from '@/deps.ts'; +import { eventSchema } from '@/schema.ts'; import ws from '@/stream.ts'; import type { Event, EventTemplate, SignedEvent } from '@/event.ts'; @@ -15,6 +16,11 @@ function getSignStream(c: AppContext): WebSocket | undefined { } } +const nostrStreamingEventSchema = z.object({ + type: z.literal('nostr.sign'), + data: eventSchema, +}); + /** * Sign Nostr event using the app context. * @@ -29,11 +35,12 @@ async function signEvent(event: EventTemplate, c: try { return await new Promise>((resolve, reject) => { const handleMessage = (e: MessageEvent) => { - // TODO: parse and validate with zod - const data = JSON.parse(e.data); - if (data.event === 'nostr.sign') { + try { + const { data: event } = nostrStreamingEventSchema.parse(JSON.parse(e.data)); stream.removeEventListener('message', handleMessage); - resolve(JSON.parse(data.payload)); + resolve(event as SignedEvent); + } catch (_e) { + // } }; stream.addEventListener('message', handleMessage);