From 6b49a14b9ffe24d45c83ba776ef40db6177f37c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Malfait?= Date: Mon, 18 May 2026 10:12:19 +0200 Subject: [PATCH] feat(auth): set 50-character maximum length on passwords (#20655) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Cap password length at 50 characters in the shared regex used by sign-up, password reset, and password change (both `twenty-front` and `twenty-server`). - Update the user-facing validation message on sign-up and password reset to mention both the 8 min and 50 max bounds. - Extend the `PASSWORD_REGEX` unit test to cover the new upper bound. The cap also prevents unbounded inputs from reaching bcrypt, which silently truncates passwords above 72 bytes and can mask user-visible bugs. ## Test plan - [x] `npx jest src/modules/auth/utils/__tests__/passwordRegex.test.ts` passes (8-char min and 50-char max). - [ ] Sign up with a 51-character password — form rejects with "Password must be between 8 and 50 characters". - [ ] Sign up with an 8–50 character password — succeeds. - [ ] Password reset rejects a 51-character password with the same message. - [ ] Existing users with longer passwords (if any pre-exist) can still sign in (the regex only gates write paths: sign-up, change, reset). --- .../src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts | 5 ++++- .../modules/auth/utils/__tests__/passwordRegex.test.ts | 8 ++++++++ .../twenty-front/src/modules/auth/utils/passwordRegex.ts | 2 +- packages/twenty-front/src/pages/auth/PasswordReset.tsx | 4 ++-- .../src/engine/core-modules/auth/auth.util.ts | 2 +- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts index 6857d62ecb..916c218d76 100644 --- a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts +++ b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts @@ -26,7 +26,10 @@ const makeValidationSchema = (signInUpStep: SignInUpStep) => signInUpStep === SignInUpStep.Password ? z .string() - .regex(PASSWORD_REGEX, t`Password must be min. 8 characters`) + .regex( + PASSWORD_REGEX, + t`Password must be between 8 and 50 characters`, + ) : z.string().optional(), captchaToken: z.string().default(''), }) diff --git a/packages/twenty-front/src/modules/auth/utils/__tests__/passwordRegex.test.ts b/packages/twenty-front/src/modules/auth/utils/__tests__/passwordRegex.test.ts index adf01551c0..c3c4cb1448 100644 --- a/packages/twenty-front/src/modules/auth/utils/__tests__/passwordRegex.test.ts +++ b/packages/twenty-front/src/modules/auth/utils/__tests__/passwordRegex.test.ts @@ -8,4 +8,12 @@ describe('PASSWORD_REGEX', () => { expect(PASSWORD_REGEX.test(validPassword)).toBe(true); expect(PASSWORD_REGEX.test(invalidPassword)).toBe(false); }); + + it('should match passwords with at most 50 characters', () => { + const validPassword = 'a'.repeat(50); + const invalidPassword = 'a'.repeat(51); + + expect(PASSWORD_REGEX.test(validPassword)).toBe(true); + expect(PASSWORD_REGEX.test(invalidPassword)).toBe(false); + }); }); diff --git a/packages/twenty-front/src/modules/auth/utils/passwordRegex.ts b/packages/twenty-front/src/modules/auth/utils/passwordRegex.ts index 1e38ac7520..9bf1646b1c 100644 --- a/packages/twenty-front/src/modules/auth/utils/passwordRegex.ts +++ b/packages/twenty-front/src/modules/auth/utils/passwordRegex.ts @@ -1 +1 @@ -export const PASSWORD_REGEX = /^.{8,}$/; +export const PASSWORD_REGEX = /^.{8,50}$/; diff --git a/packages/twenty-front/src/pages/auth/PasswordReset.tsx b/packages/twenty-front/src/pages/auth/PasswordReset.tsx index d4502fbbb8..469bca0654 100644 --- a/packages/twenty-front/src/pages/auth/PasswordReset.tsx +++ b/packages/twenty-front/src/pages/auth/PasswordReset.tsx @@ -40,14 +40,14 @@ import { import { useNavigateApp } from '~/hooks/useNavigateApp'; import { logError } from '~/utils/logError'; -const passwordMinLengthMessage = msg`Password must be min. 8 characters`; +const passwordLengthMessage = msg`Password must be between 8 and 50 characters`; const validationSchema = z .object({ passwordResetToken: z.string(), newPassword: z .string() - .regex(PASSWORD_REGEX, i18n._(passwordMinLengthMessage)), + .regex(PASSWORD_REGEX, i18n._(passwordLengthMessage)), }) .required(); diff --git a/packages/twenty-server/src/engine/core-modules/auth/auth.util.ts b/packages/twenty-server/src/engine/core-modules/auth/auth.util.ts index 8e829cb6a2..cb483b7a4f 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/auth.util.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/auth.util.ts @@ -7,7 +7,7 @@ import { import * as bcrypt from 'bcrypt'; -export const PASSWORD_REGEX = /^.{8,}$/; +export const PASSWORD_REGEX = /^.{8,50}$/; const saltRounds = 10;