security: bound Zod inputs, add SSE per-user cap and tRPC body limit (#51)
CI / Architecture Guardrails (pull_request) Successful in 2m6s
CI / Lint (pull_request) Successful in 7m29s
CI / Typecheck (pull_request) Successful in 8m3s
CI / Unit Tests (pull_request) Successful in 8m11s
CI / Build (pull_request) Successful in 5m24s
CI / E2E Tests (pull_request) Successful in 5m25s
CI / Fresh-Linux Docker Deploy (pull_request) Successful in 6m30s
CI / Release Images (pull_request) Has been skipped
CI / Assistant Split Regression (pull_request) Successful in 3m47s

Mechanical .max() bounds across 9 router schemas per the convention in
#51: IDs at 64, names at 200, search/filter strings at 500, arrays at
100-5000 depending on domain. Webhook secret bounded at min(16)/max(256).

Reports route now validates startDate/endDate via zod with year bounds
and rejects end<start. SSE timeline route enforces a per-user connection
cap of 8 (returns 429 with Retry-After). tRPC route rejects bodies over
2 MiB via Content-Length check before auth/DB work.

Covers 12 call-sites listed in #51. ESLint rule and zod conventions doc
remain as follow-up.
This commit is contained in:
2026-04-18 13:31:18 +02:00
parent f0251a654a
commit 40ca0c3046
12 changed files with 254 additions and 148 deletions
@@ -438,7 +438,7 @@ export const resourceMutationProcedures = {
}),
batchHardDelete: adminProcedure
.input(z.object({ ids: z.array(z.string()).min(1) }))
.input(z.object({ ids: z.array(z.string().max(64)).min(1).max(500) }))
.mutation(async ({ ctx, input }) => {
const resources = await ctx.db.resource.findMany({
where: { id: { in: input.ids } },