Fix QA critical/warning findings on profile feature

C-01: Replace setattr loop with explicit field assignment in update_profile
C-02: Fix useEffect dependency to profileQuery.dataUpdatedAt for re-sync
W-01: Add audit log entry for profile updates
W-02: Use less misleading generic error for email uniqueness on registration
W-03: Early return on empty PUT body to avoid unnecessary commit

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Kyle 2026-03-02 19:09:15 +08:00
parent 45f3788fb0
commit 02efe04fc4
2 changed files with 19 additions and 5 deletions

View File

@ -448,7 +448,7 @@ async def register(
select(User).where(User.email == data.email)
)
if existing_email.scalar_one_or_none():
raise HTTPException(status_code=400, detail="Registration could not be completed. Please try a different username.")
raise HTTPException(status_code=400, detail="Registration could not be completed. Please check your details and try again.")
password_hash = hash_password(data.password)
# SEC-01: Explicit field assignment — never **data.model_dump()
@ -645,12 +645,16 @@ async def get_profile(
@router.put("/profile", response_model=ProfileResponse)
async def update_profile(
data: ProfileUpdate,
request: Request,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Update the current user's profile fields (first_name, last_name, email)."""
update_data = data.model_dump(exclude_unset=True)
if not update_data:
return ProfileResponse.model_validate(current_user)
# Email uniqueness check if email is changing
if "email" in update_data and update_data["email"] != current_user.email:
new_email = update_data["email"]
@ -661,9 +665,19 @@ async def update_profile(
if existing.scalar_one_or_none():
raise HTTPException(status_code=400, detail="Email is already in use")
# SEC-01: Explicit field assignment
for key, value in update_data.items():
setattr(current_user, key, value)
# SEC-01: Explicit field assignment — only allowed profile fields
if "first_name" in update_data:
current_user.first_name = update_data["first_name"]
if "last_name" in update_data:
current_user.last_name = update_data["last_name"]
if "email" in update_data:
current_user.email = update_data["email"]
await log_audit_event(
db, action="auth.profile_updated", actor_id=current_user.id,
detail={"fields": list(update_data.keys())},
ip=get_client_ip(request),
)
await db.commit()
await db.refresh(current_user)

View File

@ -73,7 +73,7 @@ export default function SettingsPage() {
setLastName(profileQuery.data.last_name ?? '');
setProfileEmail(profileQuery.data.email ?? '');
}
}, [profileQuery.data?.username]);
}, [profileQuery.dataUpdatedAt]);
// Sync state when settings load
useEffect(() => {