Skip to content

Commit 9cb5ee9

Browse files
HVbajoriaBassel17
andauthored
Fix: password validation error messages showing regex patterns [CMS-213] (strapi#24538)
* Fix password validation error messages showing regex patterns * Fixed error messages in password validation * fix: linting issues * fix: password validation message * fix: settings page test * fix: docs fixed * fix: format message properly for error handling password validation on signup * docs: revert doc changes * test: revert api tests * test: revert seetings page frontend tests --------- Co-authored-by: Bassel Kanso <basselkanso82@gmail.com>
1 parent cd57115 commit 9cb5ee9

6 files changed

Lines changed: 192 additions & 77 deletions

File tree

.vscode/settings.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
{
2-
"eslint.workingDirectories": [{ "mode": "auto" }]
2+
"eslint.workingDirectories": [
3+
{
4+
"mode": "auto"
5+
}
6+
]
37
}

packages/core/admin/admin/src/pages/Auth/components/Register.tsx

Lines changed: 105 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -55,24 +55,45 @@ const REGISTER_USER_SCHEMA = yup.object().shape({
5555
return byteSize <= 72;
5656
}
5757
)
58-
.matches(/[a-z]/, {
59-
message: {
60-
id: 'components.Input.error.contain.lowercase',
61-
defaultMessage: 'Password must contain at least 1 lowercase letter',
58+
.test(
59+
'lowercase',
60+
{
61+
message: {
62+
id: 'components.Input.error.contain.lowercase',
63+
defaultMessage: 'Password must contain at least 1 lowercase letter',
64+
},
6265
},
63-
})
64-
.matches(/[A-Z]/, {
65-
message: {
66-
id: 'components.Input.error.contain.uppercase',
67-
defaultMessage: 'Password must contain at least 1 uppercase letter',
66+
(value) => {
67+
if (!value) return true;
68+
return /[a-z]/.test(value);
69+
}
70+
)
71+
.test(
72+
'uppercase',
73+
{
74+
message: {
75+
id: 'components.Input.error.contain.uppercase',
76+
defaultMessage: 'Password must contain at least 1 uppercase letter',
77+
},
6878
},
69-
})
70-
.matches(/\d/, {
71-
message: {
72-
id: 'components.Input.error.contain.number',
73-
defaultMessage: 'Password must contain at least 1 number',
79+
(value) => {
80+
if (!value) return true;
81+
return /[A-Z]/.test(value);
82+
}
83+
)
84+
.test(
85+
'number',
86+
{
87+
message: {
88+
id: 'components.Input.error.contain.number',
89+
defaultMessage: 'Password must contain at least 1 number',
90+
},
7491
},
75-
})
92+
(value) => {
93+
if (!value) return true;
94+
return /\d/.test(value);
95+
}
96+
)
7697
.required({
7798
id: translatedErrors.required.id,
7899
defaultMessage: 'Password is required',
@@ -123,24 +144,45 @@ const REGISTER_ADMIN_SCHEMA = yup.object().shape({
123144
return new TextEncoder().encode(value).length <= 72;
124145
}
125146
)
126-
.matches(/[a-z]/, {
127-
message: {
128-
id: 'components.Input.error.contain.lowercase',
129-
defaultMessage: 'Password must contain at least 1 lowercase letter',
147+
.test(
148+
'lowercase',
149+
{
150+
message: {
151+
id: 'components.Input.error.contain.lowercase',
152+
defaultMessage: 'Password must contain at least 1 lowercase letter',
153+
},
130154
},
131-
})
132-
.matches(/[A-Z]/, {
133-
message: {
134-
id: 'components.Input.error.contain.uppercase',
135-
defaultMessage: 'Password must contain at least 1 uppercase letter',
155+
(value) => {
156+
if (!value) return true;
157+
return /[a-z]/.test(value);
158+
}
159+
)
160+
.test(
161+
'uppercase',
162+
{
163+
message: {
164+
id: 'components.Input.error.contain.uppercase',
165+
defaultMessage: 'Password must contain at least 1 uppercase letter',
166+
},
136167
},
137-
})
138-
.matches(/\d/, {
139-
message: {
140-
id: 'components.Input.error.contain.number',
141-
defaultMessage: 'Password must contain at least 1 number',
168+
(value) => {
169+
if (!value) return true;
170+
return /[A-Z]/.test(value);
171+
}
172+
)
173+
.test(
174+
'number',
175+
{
176+
message: {
177+
id: 'components.Input.error.contain.number',
178+
defaultMessage: 'Password must contain at least 1 number',
179+
},
142180
},
143-
})
181+
(value) => {
182+
if (!value) return true;
183+
return /\d/.test(value);
184+
}
185+
)
144186
.required({
145187
id: translatedErrors.required.id,
146188
defaultMessage: 'Password is required',
@@ -383,16 +425,43 @@ const Register = ({ hasAdmin }: RegisterProps) => {
383425
}
384426
} catch (err) {
385427
if (err instanceof ValidationError) {
386-
helpers.setErrors(
387-
err.inner.reduce<Record<string, string>>((acc, { message, path }) => {
388-
if (path && typeof message === 'object') {
389-
const typedMessage = message as IntlFormattedMessage;
390-
acc[path] = formatMessage(message, typedMessage.values);
428+
const formatDescriptor = (msg: any) => {
429+
try {
430+
if (!msg) return '';
431+
if (typeof msg === 'string') return msg;
432+
// Direct descriptor
433+
if ('id' in msg && 'defaultMessage' in msg) return formatMessage(msg as any);
434+
// Wrapped descriptor: { message: { id, defaultMessage } }
435+
if (msg.message && typeof msg.message === 'object' && 'id' in msg.message) {
436+
return formatMessage(msg.message as any);
437+
}
438+
// errors array: [{ id, defaultMessage }]
439+
if (Array.isArray(msg.errors) && msg.errors.length > 0) {
440+
const first = msg.errors[0];
441+
if (typeof first === 'string') return first;
442+
if (first && typeof first === 'object' && 'id' in first)
443+
return formatMessage(first as any);
391444
}
445+
// fallback to defaultMessage if present
446+
if ('defaultMessage' in msg) return msg.defaultMessage;
447+
return String(msg);
448+
} catch (e) {
449+
return String(msg);
450+
}
451+
};
452+
453+
const computed = err.inner.reduce<Record<string, string>>(
454+
(acc, { message, path }) => {
455+
if (!path) return acc;
456+
acc[path] = formatDescriptor(message);
392457
return acc;
393-
}, {})
458+
},
459+
{}
394460
);
461+
462+
helpers.setErrors(computed);
395463
}
464+
396465
setSubmitCount(submitCount + 1);
397466
}
398467
}}
@@ -522,16 +591,6 @@ const Register = ({ hasAdmin }: RegisterProps) => {
522591
);
523592
};
524593

525-
interface RegisterFormValues {
526-
firstname: string;
527-
lastname: string;
528-
email: string;
529-
password: string;
530-
confirmPassword: string;
531-
registrationToken: string | undefined;
532-
news: boolean;
533-
}
534-
535594
type StringKeys<T> = {
536595
[K in keyof T]: T[K] extends string | undefined ? K : never;
537596
}[keyof T];

packages/core/admin/admin/src/pages/Auth/components/ResetPassword.tsx

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,24 +44,45 @@ const RESET_PASSWORD_SCHEMA = yup.object().shape({
4444
return byteSize <= 72;
4545
}
4646
)
47-
.matches(/[a-z]/, {
48-
message: {
49-
id: 'components.Input.error.contain.lowercase',
50-
defaultMessage: 'Password must contain at least 1 lowercase letter',
47+
.test(
48+
'lowercase',
49+
{
50+
message: {
51+
id: 'components.Input.error.contain.lowercase',
52+
defaultMessage: 'Password must contain at least 1 lowercase letter',
53+
},
5154
},
52-
})
53-
.matches(/[A-Z]/, {
54-
message: {
55-
id: 'components.Input.error.contain.uppercase',
56-
defaultMessage: 'Password must contain at least 1 uppercase letter',
55+
(value) => {
56+
if (!value) return true;
57+
return /[a-z]/.test(value);
58+
}
59+
)
60+
.test(
61+
'uppercase',
62+
{
63+
message: {
64+
id: 'components.Input.error.contain.uppercase',
65+
defaultMessage: 'Password must contain at least 1 uppercase letter',
66+
},
5767
},
58-
})
59-
.matches(/\d/, {
60-
message: {
61-
id: 'components.Input.error.contain.number',
62-
defaultMessage: 'Password must contain at least 1 number',
68+
(value) => {
69+
if (!value) return true;
70+
return /[A-Z]/.test(value);
71+
}
72+
)
73+
.test(
74+
'number',
75+
{
76+
message: {
77+
id: 'components.Input.error.contain.number',
78+
defaultMessage: 'Password must contain at least 1 number',
79+
},
6380
},
64-
})
81+
(value) => {
82+
if (!value) return true;
83+
return /\d/.test(value);
84+
}
85+
)
6586
.required({
6687
id: translatedErrors.required.id,
6788
defaultMessage: 'Password is required',

packages/core/admin/admin/src/pages/Settings/pages/Users/utils/validation.ts

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,39 @@ const COMMON_USER_SCHEMA = {
3838
return new TextEncoder().encode(value).length <= 72;
3939
}
4040
)
41-
.matches(/[a-z]/, {
42-
id: 'components.Input.error.contain.lowercase',
43-
defaultMessage: 'Password must contain at least one lowercase character',
44-
})
45-
.matches(/[A-Z]/, {
46-
id: 'components.Input.error.contain.uppercase',
47-
defaultMessage: 'Password must contain at least one uppercase character',
48-
})
49-
.matches(/\d/, {
50-
id: 'components.Input.error.contain.number',
51-
defaultMessage: 'Password must contain at least one number',
52-
}),
41+
.test(
42+
'lowercase',
43+
{
44+
id: 'components.Input.error.contain.lowercase',
45+
defaultMessage: 'Password must contain at least one lowercase character',
46+
},
47+
(value) => {
48+
if (!value) return true;
49+
return /[a-z]/.test(value);
50+
}
51+
)
52+
.test(
53+
'uppercase',
54+
{
55+
id: 'components.Input.error.contain.uppercase',
56+
defaultMessage: 'Password must contain at least one uppercase character',
57+
},
58+
(value) => {
59+
if (!value) return true;
60+
return /[A-Z]/.test(value);
61+
}
62+
)
63+
.test(
64+
'number',
65+
{
66+
id: 'components.Input.error.contain.number',
67+
defaultMessage: 'Password must contain at least one number',
68+
},
69+
(value) => {
70+
if (!value) return true;
71+
return /\d/.test(value);
72+
}
73+
),
5374
confirmPassword: yup
5475
.string()
5576
.transform((value) => (value === '' ? null : value))

packages/core/strapi/src/cli/commands/admin/create-user.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,18 @@ const emailValidator = yup.string().email('Invalid email address').lowercase();
1919
const passwordValidator = yup
2020
.string()
2121
.min(8, 'Password must be at least 8 characters long')
22-
.matches(/[a-z]/, 'Password must contain at least one lowercase character')
23-
.matches(/[A-Z]/, 'Password must contain at least one uppercase character')
24-
.matches(/\d/, 'Password must contain at least one number');
22+
.test('lowercase', 'Password must contain at least one lowercase character', (value) => {
23+
if (!value) return true;
24+
return /[a-z]/.test(value);
25+
})
26+
.test('uppercase', 'Password must contain at least one uppercase character', (value) => {
27+
if (!value) return true;
28+
return /[A-Z]/.test(value);
29+
})
30+
.test('number', 'Password must contain at least one number', (value) => {
31+
if (!value) return true;
32+
return /\d/.test(value);
33+
});
2534

2635
const adminCreateSchema = yup.object().shape({
2736
email: emailValidator,

packages/core/upload/admin/src/pages/SettingsPage/tests/SettingsPage.test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ describe('SettingsPage', () => {
6464

6565
await waitFor(() => expect(queryByText('Loading content.')).not.toBeInTheDocument());
6666

67+
// Check that AI metadata section is NOT visible
6768
expect(
6869
queryByRole('heading', {
6970
name: 'Generate AI captions and alt texts automatically on upload!',

0 commit comments

Comments
 (0)