diff --git a/server/lib/schemas.ts b/server/lib/schemas.ts index cf1b40c8..f1aa6c9d 100644 --- a/server/lib/schemas.ts +++ b/server/lib/schemas.ts @@ -1,18 +1,24 @@ import { z } from "zod"; + export const subdomainSchema = z .string() .regex( - /^(?!:\/\/)([a-zA-Z0-9-_]+\.)*[a-zA-Z0-9-_]+$/, + /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/, "Invalid subdomain format" ) .min(1, "Subdomain must be at least 1 character long") + .max(63, "Subdomain must not exceed 63 characters") .transform((val) => val.toLowerCase()); export const tlsNameSchema = z .string() .regex( - /^(?!:\/\/)([a-zA-Z0-9-_]+\.)*[a-zA-Z0-9-_]+$|^$/, + /^([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)(\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*$/, "Invalid subdomain format" - ) + ).max(253, "Domain must not exceed 253 characters") + .refine((val) => { + const labels = val.split('.'); + return labels.every((label) => label.length <= 63); + }, "Each part of the domain must not exceed 63 characters") .transform((val) => val.toLowerCase()); \ No newline at end of file diff --git a/src/components/DomainPicker.tsx b/src/components/DomainPicker.tsx index 35d58fae..c9c3b921 100644 --- a/src/components/DomainPicker.tsx +++ b/src/components/DomainPicker.tsx @@ -263,7 +263,7 @@ export default function DomainPicker2({ if (baseDomain.type === "provided-search") { return subdomain === "" || ( - /^[a-zA-Z0-9.-]+$/.test(subdomain) && + /^[a-zA-Z0-9-]+$/.test(subdomain) && isValidSubdomainStructure(subdomain) ); } @@ -274,7 +274,7 @@ export default function DomainPicker2({ } else if (baseDomain.domainType === "ns" || baseDomain.domainType === "wildcard") { // NS and wildcard domains support multi-level subdomains with dots and hyphens return subdomain === "" || ( - /^[a-zA-Z0-9.-]+$/.test(subdomain) && + /^[a-zA-Z0-9-]+$/.test(subdomain) && isValidSubdomainStructure(subdomain) ); } @@ -287,24 +287,17 @@ export default function DomainPicker2({ if (!input) return ""; return input .toLowerCase() - .replace(/[^a-z0-9.-]/g, ""); + .replace(/[^a-z0-9-]/g, ""); }; const isValidSubdomainStructure = (subdomain: string): boolean => { if (!subdomain) return true; - // check for consecutive dots or hyphens - if (/\.{2,}|-{2,}/.test(subdomain)) return false; + // Check for consecutive hyphens + if (/--/.test(subdomain)) return false; - // check if starts or ends with hyphen or dot - if (/^[-.]|[-.]$/.test(subdomain)) return false; - - // check each label >> (part between dots) - const parts = subdomain.split("."); - for (const part of parts) { - if (!part) return false; // Empty label - if (/^-|-$/.test(part)) return false; // Label starts/ends with hyphen - } + // Check if starts or ends with hyphen + if (/^-|-$/.test(subdomain)) return false; return true; }; @@ -369,7 +362,7 @@ export default function DomainPicker2({ if (value !== sanitized) { toast({ title: "Invalid characters removed", - description: `Only letters, numbers, hyphens, and dots are allowed`, + description: `Only letters, numbers, and hyphens are allowed`, }); }