Skip to content

Commit 283f54d

Browse files
committed
fix: Consistently guard names like the API does
1 parent bce47d2 commit 283f54d

File tree

3 files changed

+120
-0
lines changed

3 files changed

+120
-0
lines changed

src/features/auth/SignUp.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { FormMessage } from '@/components/ui/form/FormMessage';
88
import { Input } from '@/components/ui/input';
99
import { reoClient } from '@/integrations/reo/reo';
1010
import { parseCompanyFromEmail } from '@/lib/string/parseCompanyFromEmail';
11+
import { personNameRegex } from '@/lib/string/regex/personNameRegex';
1112
import { zodRequireEmail } from '@/lib/zod/email';
1213
import { zodRequirePassword } from '@/lib/zod/password';
1314
import { zodResolver } from '@hookform/resolvers/zod';
@@ -26,11 +27,19 @@ const SignUpSchema = z.object({
2627
.string()
2728
.trim()
2829
.min(2, { error: 'Please enter your first name.' })
30+
.regex(
31+
personNameRegex,
32+
{ error: 'First name can only contain letters, spaces, and hyphens.' },
33+
)
2934
.max(40, { error: 'First name cannot be longer than 40 characters.' }),
3035
lastname: z
3136
.string()
3237
.trim()
3338
.min(2, { error: 'Please enter your last name.' })
39+
.regex(
40+
personNameRegex,
41+
{ error: 'Last name can only contain letters, spaces, and hyphens.' },
42+
)
3443
.max(80, { error: 'Last name cannot be longer than 80 characters.' }),
3544
password: zodRequirePassword
3645
.min(8, { error: 'Password must be at least 8 characters long.' }),
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { personNameRegex } from './personNameRegex';
3+
4+
describe('personNameRegex', () => {
5+
it('should match valid names', () => {
6+
const validNames = [
7+
'John Doe',
8+
'Jane Smith',
9+
'Anne-Marie',
10+
"O'Connor",
11+
'François',
12+
'Müller',
13+
'Łukasz',
14+
'José',
15+
'María-José',
16+
'Åse',
17+
'Øyvind',
18+
'Æthelred',
19+
'ßert',
20+
'Česlav',
21+
];
22+
23+
for (const name of validNames) {
24+
expect(name).toMatch(personNameRegex);
25+
}
26+
});
27+
28+
it('should not match deceptive names (URLs)', () => {
29+
const deceptiveNames = [
30+
'https://hacker.com/give-me-your-money',
31+
'http://malicious.site',
32+
'www.google.com',
33+
'hacker.com',
34+
];
35+
36+
for (const name of deceptiveNames) {
37+
expect(name).not.toMatch(personNameRegex);
38+
}
39+
});
40+
41+
it('should not match strings with invalid characters', () => {
42+
const invalidNames = [
43+
'John Doe 123',
44+
'John! Doe',
45+
'John@Doe',
46+
'John#Doe',
47+
'John$Doe',
48+
'John%Doe',
49+
'John^Doe',
50+
'John&Doe',
51+
'John*Doe',
52+
'John(Doe)',
53+
'John+Doe',
54+
'John=Doe',
55+
'John{Doe}',
56+
'John}Doe',
57+
'John[Doe]',
58+
'John]Doe',
59+
'John|Doe',
60+
'John\\Doe',
61+
'John;Doe',
62+
'John:Doe',
63+
'John"Doe',
64+
'John<Doe>',
65+
'John.Doe',
66+
'John/Doe',
67+
'John?Doe',
68+
];
69+
70+
for (const name of invalidNames) {
71+
expect(name).not.toMatch(personNameRegex);
72+
}
73+
});
74+
75+
it('should match strings that are only special characters', () => {
76+
// We are stopping attack vectors, not bad data entry.
77+
const invalidNames = [
78+
' ',
79+
',',
80+
'-',
81+
"'",
82+
' ,',
83+
'- -',
84+
',,,',
85+
];
86+
87+
for (const name of invalidNames) {
88+
expect(name).toMatch(personNameRegex);
89+
}
90+
});
91+
92+
it('should match names with spaces, hyphens, and quotes', () => {
93+
const names = [
94+
'John Doe',
95+
'Anne-Marie',
96+
"O'Connor",
97+
'Doe, John',
98+
' John ', // Regex allows spaces, though SignUp.tsx trims them
99+
];
100+
101+
for (const name of names) {
102+
expect(name).toMatch(personNameRegex);
103+
}
104+
});
105+
106+
it('should not match empty string', () => {
107+
expect('').not.toMatch(personNameRegex);
108+
});
109+
});
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const personNameRegex =
2+
/^[a-zA-ZàáâäãåąčćęèéêëėįìíîïłńòóôöõøùúûüųūÿýżźñçčšžæÀÁÂÄÃÅĄĆČĖĘÈÉÊËÌÍÎÏĮŁŃÒÓÔÖÕØÙÚÛÜŲŪŸÝŻŹÑßÇŒÆČŠŽð ,'-]+$/u;

0 commit comments

Comments
 (0)