diff --git a/app/api/auth/register/route.ts b/app/api/auth/register/route.ts
index b45ab46..3303986 100644
--- a/app/api/auth/register/route.ts
+++ b/app/api/auth/register/route.ts
@@ -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
diff --git a/app/api/versions/[versionId]/comments/route.ts b/app/api/versions/[versionId]/comments/route.ts
index 135a733..7fa5ad8 100644
--- a/app/api/versions/[versionId]/comments/route.ts
+++ b/app/api/versions/[versionId]/comments/route.ts
@@ -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({
diff --git a/components/guest-gate.tsx b/components/guest-gate.tsx
index a13cbb7..09c715b 100644
--- a/components/guest-gate.tsx
+++ b/components/guest-gate.tsx
@@ -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 }) {
setGuestName(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') confirm();
}}
autoFocus
/>
-