feat(auth): enforce length limits for name and password during registration

feat(comments): add content length validation for comments and annotations

feat(guest-gate): restrict guest name length and update localStorage handling

feat(share-link-unlock): set maxLength for password input field
This commit is contained in:
Yusuf İpek
2026-04-10 20:28:55 +03:00
parent d1ffc4c4db
commit 03bfd565e8
4 changed files with 21 additions and 7 deletions
+4 -4
View File
@@ -21,8 +21,8 @@ export async function POST(request: NextRequest) {
const { name, email, password, inviteCode, invitationToken } = body;
// Validate required fields
if (!name || typeof name !== 'string' || name.trim().length < 2) {
return apiErrors.badRequest('Name must be at least 2 characters');
if (!name || typeof name !== 'string' || name.trim().length < 2 || name.trim().length > 100) {
return apiErrors.badRequest('Name must be between 2 and 100 characters');
}
if (!email || typeof email !== 'string') {
@@ -72,8 +72,8 @@ export async function POST(request: NextRequest) {
}
}
if (!password || typeof password !== 'string' || password.length < 8) {
return apiErrors.badRequest('Password must be at least 8 characters');
if (!password || typeof password !== 'string' || password.length < 8 || password.length > 128) {
return apiErrors.badRequest('Password must be between 8 and 128 characters');
}
// Check if email already exists
@@ -252,6 +252,17 @@ export async function POST(request: NextRequest, { params }: RouteParams) {
return apiErrors.badRequest('Either content, a voice recording, an image attachment, or an annotation is required');
}
// Length limits to prevent DB bloat and DoS on export/notification paths
if (content !== undefined && content !== null && String(content).length > 10_000) {
return apiErrors.badRequest('Comment content must be 10,000 characters or fewer');
}
if (guestName !== undefined && guestName !== null && String(guestName).length > 100) {
return apiErrors.badRequest('Guest name must be 100 characters or fewer');
}
if (annotationData !== undefined && annotationData !== null && JSON.stringify(annotationData).length > 50_000) {
return apiErrors.badRequest('Annotation data is too large');
}
// If replying, verify parent exists in same version
if (parentId) {
const parent = await db.comment.findFirst({
+5 -3
View File
@@ -26,8 +26,9 @@ export function GuestGate({ children }: { children: ReactNode }) {
}
const confirm = () => {
if (!guestName.trim()) return;
localStorage.setItem('openframe_guest_name', guestName.trim());
const trimmed = guestName.trim();
if (!trimmed || trimmed.length > 100) return;
localStorage.setItem('openframe_guest_name', trimmed);
setConfirmed(true);
};
@@ -47,13 +48,14 @@ export function GuestGate({ children }: { children: ReactNode }) {
<Input
placeholder="Your name"
value={guestName}
maxLength={100}
onChange={(e) => setGuestName(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') confirm();
}}
autoFocus
/>
<Button className="w-full" disabled={!guestName.trim()} onClick={confirm}>
<Button className="w-full" disabled={!guestName.trim() || guestName.trim().length > 100} onClick={confirm}>
Continue
</Button>
</div>
+1
View File
@@ -61,6 +61,7 @@ export function ShareLinkUnlock({ videoId }: ShareLinkUnlockProps) {
type="password"
placeholder="Password"
value={password}
maxLength={128}
onChange={(event) => setPassword(event.target.value)}
onKeyDown={(event) => {
if (event.key === 'Enter') {