Fix pentest findings: setup 500 error + password reuse prevention

- L-01: Setup endpoint used scalar_one_or_none() on unbounded User
  query, throwing 500 MultipleResultsFound when >1 user exists.
  Replaced with select(func.count()) for a reliable check.
- L-02: Change-password allowed reusing the same password, defeating
  must_change_password enforcement. Added equality check before
  accepting the new password.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Kyle 2026-02-28 02:42:00 +08:00
parent 1aeb725410
commit d269742aa2

View File

@ -259,8 +259,8 @@ async def setup(
First-time setup: create the admin User + Settings + default calendars.
Only works when no users exist (i.e., fresh install).
"""
existing = await db.execute(select(User))
if existing.scalar_one_or_none():
user_count = await db.execute(select(func.count()).select_from(User))
if user_count.scalar_one() > 0:
raise HTTPException(status_code=400, detail="Setup already completed")
password_hash = hash_password(data.password)
@ -588,6 +588,9 @@ async def change_password(
await _record_failed_login(db, current_user)
raise HTTPException(status_code=401, detail="Invalid current password")
if data.new_password == data.old_password:
raise HTTPException(status_code=400, detail="New password must be different from your current password")
current_user.password_hash = hash_password(data.new_password)
current_user.last_password_change_at = datetime.now()