Files
textbee/api
isra el 290149c8fb fix(security): stop sending the password hash to the browser
The user schema had no projection on `password` and no select: false, so
every read of a user document loaded the bcrypt hash. Three paths then
sent it to the client:

- GET /auth/who-am-i returns req.user, which the auth guard populates
  with an unprojected usersService.findOne. Every session check.
- The login response returns the whole user document.
- The register response returns the freshly created document.

The hash is bcrypt, so it is not directly usable, and this is not a
plaintext leak. It still matters: it hands an attacker unlimited offline
guessing with no rate limit, no lockout and no audit trail, against
whatever password strength the user chose. It also lands anywhere a
response goes, which for this app includes Microsoft Clarity session
recording, plus devtools, browser extensions, error reporting and any
XSS. Password reuse makes a cracked hash a problem beyond this app.

Fixed in two layers. The schema marks the hash select: false, so nothing
loads it by default and any future read is safe by construction. The two
flows that genuinely need it, login and change-password, ask for it
explicitly via usersService.findOneWithPassword.

select: false does not cover a document held in memory, so the login and
register responses also strip it through withoutPassword before
returning. Those were the two paths that had it loaded on purpose or had
just written it.

The schema guard was confirmed to fail against the pre-change schema
before being trusted.

Verified: 61 API tests pass (from 53). Typecheck introduces no new
errors (2 pre-existing before this change, 1 after).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 04:50:06 +03:00
..