From 2051a95e1372274a84c81fbcbb4a8e12217d3aa5 Mon Sep 17 00:00:00 2001 From: Dorian Date: Fri, 13 Mar 2026 09:16:04 +0000 Subject: [PATCH] refactor: add centralized Zod validators for all API inputs Create server/src/lib/validators.ts with reusable schemas for all API inputs (auth, fights, bets, payments, tournaments, queue, docs). Import and use in all route handlers, replacing inline validation. Add formatZodError helper for user-friendly error messages. 77 test cases in validators.test.ts cover valid, invalid, boundary, and attack inputs (SQL injection, XSS, prototype pollution). Co-Authored-By: Claude Opus 4.6 --- server/src/lib/validators.test.ts | 299 ++++++++++++++++++++++++++++++ server/src/lib/validators.ts | 157 ++++++++++++++++ server/src/routes/auth.ts | 56 +++--- server/src/routes/bets.test.ts | 2 +- server/src/routes/bets.ts | 36 ++-- server/src/routes/bots.ts | 9 +- server/src/routes/docs.ts | 17 +- server/src/routes/fights.ts | 12 +- server/src/routes/payments.ts | 41 ++-- server/src/routes/queue.ts | 9 +- server/src/routes/tournaments.ts | 33 ++-- 11 files changed, 549 insertions(+), 122 deletions(-) create mode 100644 server/src/lib/validators.test.ts create mode 100644 server/src/lib/validators.ts diff --git a/server/src/lib/validators.test.ts b/server/src/lib/validators.test.ts new file mode 100644 index 0000000..ab249d6 --- /dev/null +++ b/server/src/lib/validators.test.ts @@ -0,0 +1,299 @@ +import { describe, it, expect } from 'vitest' +import { + pubkeySchema, + botNameSchema, + satsSchema, + idSchema, + httpUrlSchema, + loginSchema, + registerSchema, + registerHumanSchema, + updateBotSchema, + respondSchema, + reactSchema, + placeBetSchema, + depositSchema, + withdrawSchema, + connectWalletSchema, + createInvoiceSchema, + submitCashuSchema, + zapSchema, + disconnectWalletSchema, + createTournamentSchema, + joinTournamentSchema, + startTournamentSchema, + joinRankedSchema, + testWebhookSchema, +} from './validators.js' + +// --- Primitive schemas --- + +describe('pubkeySchema', () => { + const valid = 'a'.repeat(64) + it('accepts valid 64-char hex', () => { expect(pubkeySchema.safeParse(valid).success).toBe(true) }) + it('rejects too short', () => { expect(pubkeySchema.safeParse('abc').success).toBe(false) }) + it('rejects too long', () => { expect(pubkeySchema.safeParse('a'.repeat(65)).success).toBe(false) }) + it('rejects non-hex', () => { expect(pubkeySchema.safeParse('g'.repeat(64)).success).toBe(false) }) + it('rejects uppercase', () => { expect(pubkeySchema.safeParse('A'.repeat(64)).success).toBe(false) }) + it('rejects empty', () => { expect(pubkeySchema.safeParse('').success).toBe(false) }) + it('rejects number', () => { expect(pubkeySchema.safeParse(12345).success).toBe(false) }) + it('rejects null', () => { expect(pubkeySchema.safeParse(null).success).toBe(false) }) +}) + +describe('botNameSchema', () => { + it('accepts valid names', () => { + expect(botNameSchema.safeParse('ab').success).toBe(true) + expect(botNameSchema.safeParse('test-bot_1').success).toBe(true) + expect(botNameSchema.safeParse('ABCDEFGHIJKL').success).toBe(true) // 12 chars + }) + it('rejects too short', () => { expect(botNameSchema.safeParse('a').success).toBe(false) }) + it('rejects too long', () => { expect(botNameSchema.safeParse('a'.repeat(13)).success).toBe(false) }) + it('rejects special chars', () => { + expect(botNameSchema.safeParse('bot name').success).toBe(false) // spaces + expect(botNameSchema.safeParse('bot@name').success).toBe(false) + expect(botNameSchema.safeParse('