From 004586ee64336ebd0088bdbda87ff490b4e0623b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= Date: Sun, 16 Aug 2026 00:06:39 +0200 Subject: [PATCH] Enforce required on string/multiselect variables without explicit bounds required:true only worked by accident: a bare z.string() or z.array(...) accepts "" / [] just fine, so a required field with no minLength (e.g. secret tokens like webhookToken, apiKey) could be submitted empty and the run would start anyway. Required now implies min(1) when no stricter bound is already configured, for both string and multiselect fields. This is the single schema shared by the client form resolver and the server run-creation route, so both now reject it. Co-Authored-By: Claude Sonnet 5 --- app/src/lib/validation/variable-schema.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/app/src/lib/validation/variable-schema.ts b/app/src/lib/validation/variable-schema.ts index ab1e5be..411e940 100644 --- a/app/src/lib/validation/variable-schema.ts +++ b/app/src/lib/validation/variable-schema.ts @@ -7,7 +7,12 @@ function fieldSchema(variable: VariableConfig): z.ZodTypeAny { switch (variable.type) { case "string": { let s = z.string(); - if (variable.minLength !== undefined) s = s.min(variable.minLength); + // `required` alone has to reject "" even when the author didn't set an explicit + // minLength - otherwise an empty string satisfies a bare z.string() and the run starts + // with a "required" field effectively unset. + const minLength = + variable.minLength ?? (variable.required ? 1 : undefined); + if (minLength !== undefined) s = s.min(minLength); if (variable.maxLength !== undefined) s = s.max(variable.maxLength); if (variable.pattern) s = s.regex(new RegExp(variable.pattern)); field = s; @@ -26,9 +31,12 @@ function fieldSchema(variable: VariableConfig): z.ZodTypeAny { case "enum": field = z.enum(variable.choices as [string, ...string[]]); break; - case "multiselect": - field = z.array(z.enum(variable.choices as [string, ...string[]])); + case "multiselect": { + let arr = z.array(z.enum(variable.choices as [string, ...string[]])); + if (variable.required) arr = arr.min(1); + field = arr; break; + } } if (!variable.required) {