mirror of
https://github.com/vernu/textbee.git
synced 2026-07-30 17:07:46 -04:00
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>