fix: replace z.string().email() with RFC 5322 compliant ZEmail/zEmail (#2655)

This commit is contained in:
Lucas Smith
2026-03-26 13:31:26 +11:00
committed by GitHub
parent 0434bdfacf
commit 814f6e62de
49 changed files with 172 additions and 95 deletions
+2 -1
View File
@@ -21,6 +21,7 @@ import {
ZTextFieldMeta,
} from '@documenso/lib/types/field-meta';
import { toCheckboxCustomText, toRadioCustomText } from '@documenso/lib/utils/fields';
import { ZEmail } from '@documenso/lib/utils/zod';
import type { TSignEnvelopeFieldValue } from '@documenso/trpc/server/envelope-router/sign-envelope-field.types';
import { checkboxValidationSigns } from '@documenso/ui/primitives/document-flow/field-items-advanced-settings/constants';
@@ -37,7 +38,7 @@ export const extractFieldInsertionValues = ({
}: ExtractFieldInsertionValuesOptions): { customText: string; inserted: boolean } => {
return match(fieldValue)
.with({ type: FieldType.EMAIL }, (fieldValue) => {
const parsedEmailValue = z.string().email().nullable().safeParse(fieldValue.value);
const parsedEmailValue = ZEmail.nullable().safeParse(fieldValue.value);
if (!parsedEmailValue.success) {
throw new AppError(AppErrorCode.INVALID_BODY, {
+2 -2
View File
@@ -1,12 +1,12 @@
import type { Envelope } from '@prisma/client';
import { type Field, type Recipient, RecipientRole, SigningStatus } from '@prisma/client';
import { z } from 'zod';
import { isSignatureFieldType } from '@documenso/prisma/guards/is-signature-field';
import { NEXT_PUBLIC_WEBAPP_URL } from '../constants/app';
import { AppError, AppErrorCode } from '../errors/app-error';
import { extractLegacyIds } from '../universal/id';
import { ZEmail } from './zod';
/**
* Roles that require fields to be assigned before a document can be distributed.
@@ -93,7 +93,7 @@ export const mapRecipientToLegacyRecipient = (
};
export const isRecipientEmailValidForSending = (recipient: Pick<Recipient, 'email'>) => {
return z.string().email().safeParse(recipient.email).success;
return ZEmail.safeParse(recipient.email).success;
};
/**
+46
View File
@@ -0,0 +1,46 @@
import { z } from 'zod';
/**
* RFC 5322 compliant email regex.
*
* This is more permissive than Zod's built-in `.email()` validator which rejects
* valid international characters (e.g. "Søren@gmail.com").
*
* Compiled once at module level to avoid re-compilation on every validation call.
*/
const EMAIL_REGEX =
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~\u{0080}-\u{FFFF}-]+@[a-zA-Z0-9\u{0080}-\u{FFFF}](?:[a-zA-Z0-9\u{0080}-\u{FFFF}-]{0,61}[a-zA-Z0-9\u{0080}-\u{FFFF}])?(?:\.[a-zA-Z0-9\u{0080}-\u{FFFF}](?:[a-zA-Z0-9\u{0080}-\u{FFFF}-]{0,61}[a-zA-Z0-9\u{0080}-\u{FFFF}])?)*$/u;
const DEFAULT_EMAIL_MESSAGE = 'Invalid email address';
/**
* A Zod schema for validating email addresses using an RFC 5322 compliant regex.
*
* This supports international characters in the local part and domain
* (e.g. "Søren@gmail.com", "user@dömain.com").
*
* Use `zEmail()` if you need to pass a custom error message.
*/
export const ZEmail = z.string().regex(EMAIL_REGEX, { message: DEFAULT_EMAIL_MESSAGE });
/**
* Creates a Zod email schema with an optional custom error message.
*
* @example
* ```ts
* // With default message
* zEmail()
*
* // With custom message string
* zEmail('Email is invalid')
*
* // With message object
* zEmail({ message: 'Email is invalid' })
* ```
*/
export const zEmail = (options?: string | { message?: string }) => {
const message =
typeof options === 'string' ? options : (options?.message ?? DEFAULT_EMAIL_MESSAGE);
return z.string().regex(EMAIL_REGEX, { message });
};