From bb3f2ee9e5bfdf4c6fcb626ac8cff08d3c033b81 Mon Sep 17 00:00:00 2001 From: Harsh Kumar <9u.harsh@gmail.com> Date: Thu, 27 Aug 2026 17:02:43 +0530 Subject: [PATCH] fix(validation): reject whitespace-only names and titles (#2606) --- .../dashboard/src/components/forms/monitor/form-general.tsx | 2 +- .../src/components/forms/status-page/form-general.tsx | 2 +- packages/db/src/schema/monitors/validation.ts | 1 + packages/db/src/schema/pages/validation.ts | 1 + packages/services/src/monitor/schemas.ts | 6 +++--- packages/services/src/page/schemas.ts | 4 ++-- 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/apps/dashboard/src/components/forms/monitor/form-general.tsx b/apps/dashboard/src/components/forms/monitor/form-general.tsx index 5233f2d60..c13353f3d 100644 --- a/apps/dashboard/src/components/forms/monitor/form-general.tsx +++ b/apps/dashboard/src/components/forms/monitor/form-general.tsx @@ -77,7 +77,7 @@ const HTTP_ASSERTION_TYPES = ["status", "header", "textBody"] as const; const DNS_ASSERTION_TYPES = dnsRecords; const schema = z.object({ - name: z.string().min(1, "Name is required"), + name: z.string().trim().min(1, "Name is required"), type: z.enum(TYPES), method: z.enum(monitorMethods), url: z.string().min(1, "URL is required"), diff --git a/apps/dashboard/src/components/forms/status-page/form-general.tsx b/apps/dashboard/src/components/forms/status-page/form-general.tsx index 11c18fc2c..bc60ceb8d 100644 --- a/apps/dashboard/src/components/forms/status-page/form-general.tsx +++ b/apps/dashboard/src/components/forms/status-page/form-general.tsx @@ -57,7 +57,7 @@ function formatSlug(title: string) { } const schema = z.object({ - title: z.string().min(1, "Title is required"), + title: z.string().trim().min(1, "Title is required"), slug: z .string() .min(3, "Slug is required") diff --git a/packages/db/src/schema/monitors/validation.ts b/packages/db/src/schema/monitors/validation.ts index a6774e948..36736ef19 100644 --- a/packages/db/src/schema/monitors/validation.ts +++ b/packages/db/src/schema/monitors/validation.ts @@ -63,6 +63,7 @@ const headersSchema = z export const insertMonitorSchema = createInsertSchema(monitor, { name: z .string() + .trim() .min(1, "Name must be at least 1 character long") .max(255, "Name must be at most 255 characters long"), periodicity: monitorPeriodicitySchema.prefault("10m"), diff --git a/packages/db/src/schema/pages/validation.ts b/packages/db/src/schema/pages/validation.ts index 0aa0511ef..d424172b7 100644 --- a/packages/db/src/schema/pages/validation.ts +++ b/packages/db/src/schema/pages/validation.ts @@ -68,6 +68,7 @@ export const customThemeWriteSchema = customThemeSchema }); export const insertPageSchema = createInsertSchema(page, { + title: z.string().trim().min(1, "Title must be at least 1 character long"), customDomain: customDomainSchema.prefault(""), accessType: z.enum(pageAccessTypes).prefault("public"), icon: z.string().optional(), diff --git a/packages/services/src/monitor/schemas.ts b/packages/services/src/monitor/schemas.ts index c95f7bd0a..0ccf5eb11 100644 --- a/packages/services/src/monitor/schemas.ts +++ b/packages/services/src/monitor/schemas.ts @@ -37,7 +37,7 @@ const apiTimeoutMs = z.coerce.number().gte(0).lte(120_000); * + `30m`/`1m` respectively). */ export const CreateMonitorInput = z.object({ - name: z.string().min(1), + name: z.string().trim().min(1), jobType: z.enum(monitorJobTypes), url: z.string(), method: z.enum(monitorMethods), @@ -72,7 +72,7 @@ export type CreateMonitorInput = z.infer; */ export const UpdateMonitorConfigInput = z.object({ id: z.number().int(), - name: z.string().min(1).optional(), + name: z.string().trim().min(1).optional(), url: z.string().optional(), method: z.enum(monitorMethods).optional(), headers: z.array(headerPair).optional(), @@ -95,7 +95,7 @@ export type UpdateMonitorConfigInput = z.infer; /** Update the "general" monitor payload — name / endpoint / headers / assertions. */ export const UpdateMonitorGeneralInput = z.object({ id: z.number().int(), - name: z.string().min(1), + name: z.string().trim().min(1), jobType: z.enum(monitorJobTypes), url: z.string(), method: z.enum(monitorMethods), diff --git a/packages/services/src/page/schemas.ts b/packages/services/src/page/schemas.ts index 6016629a0..30ee8c86e 100644 --- a/packages/services/src/page/schemas.ts +++ b/packages/services/src/page/schemas.ts @@ -49,7 +49,7 @@ export type CreatePageInput = { /** Minimal create — the onboarding / `new` path with no monitors. */ export const NewPageInput = z.object({ - title: z.string(), + title: z.string().trim().min(1), // Canonical `slugSchema` from db validation — regex + min(3). // Plain `z.string().toLowerCase()` here let malformed slugs through // that `insertPageSchema` would reject, so `create` and `new` had @@ -81,7 +81,7 @@ export type GetSlugAvailableInput = z.infer; export const UpdatePageGeneralInput = z.object({ id: z.number().int(), - title: z.string(), + title: z.string().trim().min(1), slug: slugSchema, description: z.string().nullish(), icon: z.string().nullish(), -- 2.54.0