| Title | mickasmt next-saas-stripe-starter 1.0.0 Improper Access Controls |
|---|
| Description | https://github.com/mickasmt/next-saas-stripe-starter
Any Authenticated User Can Escalate to ADMIN Role
File: actions/update-user-role.ts` (lines 16–37)
Called from: components/forms/user-role-form.tsx` → rendered in `app/(protected)/dashboard/settings/page.tsx`
CWE: CWE-269 (Improper Privilege Management)
OWASP: A01:2021 – Broken Access Control
The vulnerability: The `updateUserRole` server action accepts a `userId` and a `role` from the client. The only authorization check is:
```typescript
if (!session?.user || session?.user.id !== userId) {
throw new Error("Unauthorized");
}
```
This verifies the user is modifying *their own* record — but that is exactly the attack. Any authenticated `USER` can call this action with their own ID and `role: "ADMIN"` to grant themselves full admin privileges. The role value is validated by `userRoleSchema` using `z.nativeEnum(UserRole)`, which accepts both `ADMIN` and `USER` as valid values.
The `UserRoleForm` component is rendered on the settings page for **all users** and even includes the comment `"Remove this field on real production"`, confirming this was intended as a dev-only feature that was never gated.
**Attack scenario:**
1. Attacker registers a normal account (role: `USER`).
2. Navigates to `/dashboard/settings`.
3. Selects `ADMIN` from the role dropdown and submits.
4. `updateUserRole` validates `session.user.id === userId` passes, since the user is modifying themselves.
5. Prisma updates the user's role to `ADMIN`.
6. Attacker now has full access to `/admin` and all admin-protected routes.
**Fix:** Remove the `updateUserRole` action and `UserRoleForm` component entirely. Role changes should only be performable through a separate admin-only action that checks `session.user.role === "ADMIN"` and targets a *different* user:
```typescript
export async function updateUserRole(targetUserId: string, data: FormData) {
const session = await auth();
if (!session?.user || session.user.role !== "ADMIN") {
throw new Error("Unauthorized");
}
if (session.user.id === targetUserId) {
throw new Error("Cannot modify own role");
}
// ... proceed with update
} |
|---|
| User | Ghufran Khan (UID 95493) |
|---|
| Submission | 03/07/2026 18:00 (1 month ago) |
|---|
| Moderation | 03/21/2026 17:49 (14 days later) |
|---|
| Status | Accepted |
|---|
| VulDB entry | 352375 [mickasmt next-saas-stripe-starter 1.0.0 update-user-role.ts updateUserrole userId/role improper authorization] |
|---|
| Points | 17 |
|---|