No validation on systemRole values — arbitrary roles accepted #5
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
The
AdminService.updateUser()andcreateUser()methods accept any string forsystemRolewithout validating against an allowed set. An admin could setsystemRoleto an arbitrary value like"superadmin".Affected files
AdminService.java:96-100—updateUsersets role without validationAdminService.java:62—createUsersets role without validationAttack scenario
A compromised admin account sets an arbitrary role value. While this doesn't directly escalate privileges today (only
ROLE_ADMINis checked inSecurityConfig), it's a ticking time bomb — any future role check against an unexpected value will behave unpredictably.Recommended fix
Validate
systemRoleagainst a whitelist ("user","admin") in both methods. Reject any other value with aValidationException. Consider using an enum.Severity
High — no input validation on a privilege-controlling field.
👨💻 Kai — Frontend Engineer
This fix is primarily backend, but there are frontend implications I want to flag before implementation starts.
Questions for the backend team:
systemRoleis submitted —400with a validation message, or422? I need to know so I can wire up the error state in the admin user form correctly.<select>.Frontend-side concerns:
<select>(or equivalent) bound to an explicit list of valid values — not a free-text input. If it's currently a text field, that's a UI design smell that mirrors the backend bug. I'll check the current admin form component.<select>, we should validate server-side (which this issue is addressing) — a crafted HTTP request bypasses any client-side constraint. So the fix here is necessary regardless of what the UI does.+page.server.tsaction error handling.No blocking concerns on my end — just want to coordinate on the error response shape before I implement the error UI.
🏗️ Backend Engineer — Spring Boot / PostgreSQL Specialist
Solid find. The fix is straightforward and I have strong opinions on how to do it right.
Recommended implementation path:
Use a Java enum, not a string whitelist —
SystemRole.USERandSystemRole.ADMIN. Enums give you compile-time safety, exhaustive switch coverage, and Jackson deserializes them cleanly. A string whitelist is just a poor man's enum.Map the enum to the DB column — if
system_roleis stored asvarchar, annotate with@Enumerated(EnumType.STRING). If it's a PostgreSQL enum type, use a custom converter. Either way, invalid strings never reach the service layer.Validate at the controller boundary — the
AdminUserRequestDTO should declaresystemRoleasSystemRole(the enum), notString. Jackson will throw aHttpMessageNotReadableExceptionwith a 400 beforeAdminServiceis even called. No manual whitelist check needed.Remove the manual validation in
AdminServiceif you add it there — that's controller-layer concern, not service-layer. The service should accept a validated enum, not re-validate strings.Questions before implementation:
system_rolestored in the DB as a plainvarcharor a PostgreSQLCREATE TYPE ... AS ENUM? The mapping strategy differs."admin") vs uppercase ("ADMIN")? We need a consistent casing convention before we add the enum.DB-level consideration: Even with the enum fix in Java, I'd recommend adding a
CHECKconstraint on thesystem_rolecolumn:CHECK (system_role IN ('user', 'admin')). Defense in depth — the DB should not accept invalid values even if something bypasses the application layer.🧪 QA Engineer
Good catch. Here's the test matrix I'd want covered before this is marked done.
Unit tests for
AdminService:createUserwith valid role"user"→ succeedscreateUserwith valid role"admin"→ succeedscreateUserwith invalid role"superadmin"→ throwsValidationException(or equivalent)createUserwith null role → what's the behavior? Should it default to"user"or reject?updateUserwith valid role → succeedsupdateUserwith invalid role → throwsValidationExceptionupdateUserwith empty string role → throwsValidationExceptionIntegration tests (controller level):
POST /v1/admin/userswithsystemRole: "superadmin"→ expect400or422, not201PUT /v1/admin/users/{id}withsystemRole: "superadmin"→ expect400or422Edge cases I'd flag:
"Admin","ADMIN","USER"? Is the validation case-sensitive? Document and test both directions.systemRoleis omitted entirely from the request body — null vs missing field — does the behavior differ?Test infrastructure note: The enum deserialization path is worth a dedicated
@ParameterizedTestwith a list of invalid strings — that keeps the test readable and makes adding new invalid values trivial.🔒 Sable — Security Engineer
I filed this issue, so let me add the threat model detail and fix constraints for whoever implements it.
Why this is High and not Medium:
The "only
ROLE_ADMINis checked today" reasoning is exactly why this is dangerous — it's a latent vulnerability waiting for a new role check to be added incorrectly. SettingsystemRole: "superadmin"today writes an unexpected value to the DB. Any future code that doesif (!role.equals("user"))instead ofif (role.equals("admin"))would accidentally treat"superadmin"as admin-equivalent. We can't reason about future code correctness against an unbounded string field.Security requirements for the fix:
<select>are bypassed trivially with a raw HTTP requestValidationException(or 400 response) does not leak internal type names, package paths, or enum class names in the response bodyDB-level enforcement: I want a
CHECKconstraint onsystem_roleindependent of the application fix. If someone connects directly to the DB or if the ORM is bypassed via a native query, the constraint is the last line of defense.Audit log: Any successful role change via
AdminService.updateUser()should already be hitting theadmin_audit_log. Confirm that's the case — the fix shouldn't silently remove that logging path.🎨 Atlas — UI/UX Designer
This is a backend validation issue, but the admin UI has a design responsibility here too.
Design concerns:
The role selector must be a constrained control — if the admin form uses a free-text input for
systemRole, that's a design problem I want corrected regardless of this backend fix. A<select>or radio group with exactly the valid options makes it impossible for an admin to accidentally type an invalid value, and the intent is immediately clear.Labeling: "System Role" is technical language. In the admin UI, consider labeling it "Account type" with options "Standard user" and "Administrator" — map to the enum values on submit. Clearer for admins who aren't engineers.
Error state: If the API rejects a role value (edge case after the UI fix, but still possible via direct API), the form needs a clear inline error on the role field — not just a generic toast. The error should tell the admin what went wrong without exposing technical details.
Confirmation for role escalation: Changing a user to
adminis a significant privilege event. Consider a confirmation step ("You are granting administrator access to [name]. Confirm?") as a UX safety net against accidental escalations — independent of the technical validation.Questions: