fix: add input validation, Zod schemas, rate limiting, and IP trust
- Add Zod schema for webhook response parsing (orchestrator.ts) - Add Zod schemas for POST /respond and /react request bodies - Add safe integer validation for batch count param - Prefer cf-connecting-ip over spoofable x-forwarded-for - Add ID format validation on URL params - Add rate limiting on /auth/login (30/min) and /update (10/min) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
276cbd6e31
commit
5bfb63aa7f
@@ -1,3 +1,4 @@
|
||||
import { z } from 'zod'
|
||||
import { nanoid } from 'nanoid'
|
||||
import { toError } from '../lib/utils.js'
|
||||
import { db, schema, sqlite } from '../db/index.js'
|
||||
@@ -17,6 +18,11 @@ import { getCurrentSeason } from './seasons.js'
|
||||
import { onFightFinished as onTournamentFightFinished } from './tournaments.js'
|
||||
import { trackFightCompleted, trackBotActive, trackMetric } from './analytics.js'
|
||||
|
||||
const webhookResponseSchema = z.object({
|
||||
answer: z.string().nullable().optional(),
|
||||
trash_talk: z.string().optional(),
|
||||
}).passthrough()
|
||||
|
||||
interface BotRecord {
|
||||
id: string
|
||||
name: string
|
||||
@@ -184,17 +190,23 @@ async function callWebhook(
|
||||
return { answer: null, timeMs: elapsed, timedOut: false, error: true }
|
||||
}
|
||||
|
||||
let data: { answer?: string; trash_talk?: string }
|
||||
let parsed: unknown
|
||||
try {
|
||||
data = JSON.parse(text)
|
||||
parsed = JSON.parse(text)
|
||||
} catch {
|
||||
console.log(`[webhook] ${url} returned non-JSON in ${elapsed}ms: ${text.slice(0, 200)}`)
|
||||
return { answer: null, timeMs: elapsed, timedOut: false, error: true }
|
||||
}
|
||||
|
||||
const data = webhookResponseSchema.safeParse(parsed)
|
||||
if (!data.success) {
|
||||
console.log(`[webhook] ${url} invalid response shape in ${elapsed}ms: ${data.error.message}`)
|
||||
return { answer: null, timeMs: elapsed, timedOut: false, error: true }
|
||||
}
|
||||
|
||||
// Enforce size limits on fields
|
||||
const answer = data.answer ? data.answer.slice(0, MAX_ANSWER_LENGTH) : null
|
||||
const trashTalk = data.trash_talk ? data.trash_talk.slice(0, MAX_TRASH_TALK_LENGTH) : undefined
|
||||
const answer = data.data.answer ? data.data.answer.slice(0, MAX_ANSWER_LENGTH) : null
|
||||
const trashTalk = data.data.trash_talk ? data.data.trash_talk.slice(0, MAX_TRASH_TALK_LENGTH) : undefined
|
||||
|
||||
console.log(`[webhook] ${url} OK in ${elapsed}ms answer=${(answer || '').slice(0, 80)}`)
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user