From cfd399532530b15465b7c05e190f0ca1b283f0d1 Mon Sep 17 00:00:00 2001 From: Ayush7614 Date: Sat, 5 Sep 2026 15:38:55 +0530 Subject: [PATCH 1/2] fix(audit): return 400 for invalid from/to dates and strict-parse limit --- server/src/app.ts | 14 +++++++++++--- server/src/audit.ts | 31 +++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/server/src/app.ts b/server/src/app.ts index 356d07d9b..d0f38a312 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -8,6 +8,7 @@ import { createAgentRoutes } from "./agents/routes"; import { type AuditReader, type AuditStore, + AuditQueryError, auditQueryFromUrl, recordAuditEvent, } from "./audit"; @@ -456,9 +457,16 @@ export function createApp( return context.json({ error: "Audit logging is not configured." }, 503); } - return context.json( - await auditReader.list(auditQueryFromUrl(new URL(context.req.url))), - ); + try { + return context.json( + await auditReader.list(auditQueryFromUrl(new URL(context.req.url))), + ); + } catch (error) { + if (error instanceof AuditQueryError) { + return context.json({ error: error.message }, 400); + } + throw error; + } }); /* * Who is here, and what they may do. diff --git a/server/src/audit.ts b/server/src/audit.ts index 3e42b2b30..16b71a32a 100644 --- a/server/src/audit.ts +++ b/server/src/audit.ts @@ -579,16 +579,35 @@ export function createAuditReader(database: Database): AuditReader { }; } +export class AuditQueryError extends Error { + constructor(message: string) { + super(message); + this.name = "AuditQueryError"; + } +} + export function auditQueryFromUrl(url: URL): AuditEventQuery { - const requestedLimit = Number.parseInt( - url.searchParams.get("limit") ?? "50", - 10, - ); + const rawLimit = url.searchParams.get("limit") ?? "50"; + const trimmedLimit = rawLimit.trim(); + const requestedLimit = /^\d+$/.test(trimmedLimit) + ? Number.parseInt(trimmedLimit, 10) + : Number.NaN; const limit = Number.isFinite(requestedLimit) ? Math.min(Math.max(requestedLimit, 1), 100) : 50; const optional = (name: string) => url.searchParams.get(name) ?? undefined; + const from = optional("from"); + if (from !== undefined && Number.isNaN(Date.parse(from))) { + throw new AuditQueryError( + 'Query parameter "from" must be a valid date.', + ); + } + const to = optional("to"); + if (to !== undefined && Number.isNaN(Date.parse(to))) { + throw new AuditQueryError('Query parameter "to" must be a valid date.'); + } + return { cursor: optional("cursor"), limit, @@ -596,7 +615,7 @@ export function auditQueryFromUrl(url: URL): AuditEventQuery { actorUserId: optional("actorUserId"), targetType: optional("targetType"), targetId: optional("targetId"), - from: optional("from"), - to: optional("to"), + from, + to, }; } From 032e3c2c83d3e6cf697d04f52868b783c346ab6d Mon Sep 17 00:00:00 2001 From: David McKay Date: Sat, 5 Sep 2026 10:02:46 -0700 Subject: [PATCH 2/2] format --- server/src/audit.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/server/src/audit.ts b/server/src/audit.ts index 16b71a32a..e057138dc 100644 --- a/server/src/audit.ts +++ b/server/src/audit.ts @@ -599,9 +599,7 @@ export function auditQueryFromUrl(url: URL): AuditEventQuery { const from = optional("from"); if (from !== undefined && Number.isNaN(Date.parse(from))) { - throw new AuditQueryError( - 'Query parameter "from" must be a valid date.', - ); + throw new AuditQueryError('Query parameter "from" must be a valid date.'); } const to = optional("to"); if (to !== undefined && Number.isNaN(Date.parse(to))) {