Unvalidated profile update endpoints are a direct path to CWE-20 (Improper Input Validation) and CWE-434 (Unrestricted File Upload), both flagged under OWASP A03. A bio field with no length limit lets attackers stuff multi-megabyte payloads into your database on every request. Usernames without character constraints enable injection into downstream rendering contexts. Unvalidated avatar uploads allow executable content to reach your storage bucket, creating stored XSS or server-side execution vectors.
High because unvalidated file uploads and unbounded text fields create stored injection risks that affect every viewer of the profile, not just the submitter.
Add Zod validation to your profile update handler in app/api/users/[id]/route.ts before touching the database. Enforce a 500-character bio cap, a strict alphanumeric-plus-dash username regex, and MIME-type allowlisting on any uploaded file.
// app/api/users/[id]/route.ts
import { z } from 'zod'
const UpdateProfileSchema = z.object({
bio: z.string().max(500).optional(),
username: z.string().regex(/^[a-zA-Z0-9_-]{3,20}$/).optional()
})
export async function PATCH(req: Request, { params }: { params: { id: string } }) {
const body = await req.json()
const parsed = UpdateProfileSchema.safeParse(body)
if (!parsed.success) {
return Response.json({ error: parsed.error.flatten() }, { status: 400 })
}
const updated = await db.user.update({
where: { id: params.id },
data: parsed.data
})
return Response.json(updated)
}
ID: community-social-engagement.profiles-identity.profile-edit-capability
Severity: high
What to look for: Enumerate all relevant files and Look for API endpoints or server actions that handle user profile updates. Search for endpoints like /api/users/[id] (PUT/PATCH), or server actions like updateUserProfile, uploadAvatar. Check for input validation on profile fields (bio length limits, forbidden characters in usernames). Check for secure file upload handling if avatar/image uploads are supported. Quote the exact code pattern or configuration value found.
Pass criteria: At least 1 implementation must be present. At least one endpoint or server action exists for updating user profiles. Input validation is applied to prevent excessively long bios, invalid usernames, and unsafe file uploads. An example: bio.length <= 500, username matches /^[a-zA-Z0-9_-]+$/.
Fail criteria: No profile update endpoint exists, or endpoints accept any input without validation, or file uploads are unvalidated. A partial or incomplete implementation does not count as pass.
Skip (N/A) when: Profiles are read-only (users cannot edit their own profiles).
Cross-reference: For performance analysis of feed queries and real-time updates, the Performance & Load audit covers database optimization and caching.
Detail on fail: "No profile update endpoint found" or "Profile update API at /api/users/[id] accepts bio field with no length validation — users can paste unlimited text"
Remediation: Protect profile update endpoints with validation and secure file handling:
// app/api/users/[id]/route.ts
export async function PATCH(req: Request, { params }: { params: { id: string } }) {
const { bio, username } = await req.json()
// Validate inputs
if (bio && bio.length > 500) {
return Response.json({ error: 'Bio too long' }, { status: 400 })
}
if (username && !username.match(/^[a-zA-Z0-9_-]{3,20}$/)) {
return Response.json({ error: 'Invalid username' }, { status: 400 })
}
const updated = await db.user.update({
where: { id: params.id },
data: { bio, username }
})
return Response.json(updated)
}