fix(users): stop exposing email addresses from user search - #385
Open
MOHITKOURAV01 wants to merge 1 commit into
Open
fix(users): stop exposing email addresses from user search#385MOHITKOURAV01 wants to merge 1 commit into
MOHITKOURAV01 wants to merge 1 commit into
Conversation
GET /api/users selected `email` into every result and matched the `?search=` term against the email column. That gave any authenticated caller a full address book (walk the cursor with limit=100) and a working enumeration oracle — `?search=@gmail.com` harvests by domain, `?search=someone@example.com` answers whether that address has an account. No screen in the product shows another user's email. The projection now matches the one /api/conversations and /api/connections already use, and search matches name and location only. While in here: - Blocked users were still returned. Blocking is symmetric (src/lib/blocking.ts) and /api/conversations already filters with getBlockedUserIds(); user search was the remaining place a blocked user could surface. Same filter applied. - The cursor filter and the search filter were both spread into the where object as `OR`, so the search clause overwrote the cursor clause and paging through a search returned page one forever. Both now sit under `AND`. - `whereClause` was typed `any`, which is what let the email predicate through unnoticed. It is `Prisma.UserWhereInput` now. - `search` is trimmed, a whitespace-only term is ignored, and terms over 100 characters are rejected with 400 instead of running a wide scan. - `hasMore` is returned alongside `nextCursor`. The test file asserted the old behaviour (that email was selected and searched), so those assertions are inverted. Its `buildTimestampCursorWhere` mock also took the cursor as its first argument while the real signature is (field, cursor), so it was being handed the string "createdAt" and returned a bogus filter on every call; fixed so the cursor assertions mean something. Closes TravellersMeet#380
|
Thanks for opening this PR, @MOHITKOURAV01! 👋 Our maintainers will review it shortly. Estimate Time is 5-8 hrs .Meanwhile please:
If anything changes, feel free to push updates—this thread will stay open. |
Contributor
Author
On the red
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #380
The problem
GET /api/usersselectedemailinto every result and matched the?search=term against the email column:Two separate problems in one query:
limit=100and dump the whole user table's email addresses. No screen in the product shows another user's email —/api/conversations,/api/connectionsand/api/matchesall selectid/name/image/bio/location.email contains <term>is an enumeration oracle.?search=@gmail.comharvests by domain;?search=someone@example.comanswers whether that address has an account.What this changes
The leak.
emailis out of the projection and out of the search predicate. Search matchesnameandlocation— what you'd actually use to find a travel companion.Blocked users. Search never consulted
src/lib/blocking.ts. Blocking is symmetric, and/api/conversationsalready filters withgetBlockedUserIds(); user search was the remaining place a blocked user could still surface. Same helper, one query, applied to the exclusion list.A pagination bug found while writing the tests. The cursor filter and the search filter were both spread into the
whereobject asOR, so the search clause overwrote the cursor clause:?search=goa&cursor=<next>therefore returned page one forever. Both groups now sit underAND.Typing.
whereClausewasany, which is what let theemailpredicate through unnoticed. It'sPrisma.UserWhereInputnow.Input handling.
searchis trimmed, a whitespace-only term is ignored rather than running acontains ""scan, and terms over 100 characters get a 400.hasMoreis returned alongsidenextCursor.Tests
The existing file asserted the old behaviour — that
emailwas selected and was searched — so those assertions are inverted. ItsbuildTimestampCursorWheremock also took the cursor as its first argument while the real signature is(field, cursor), so it was handed the string"createdAt", treated it as a cursor and returned a filter withDate { NaN }in it on every call. That's why 3 of the 8 tests in this file fail onmain. Fixed, so the cursor assertions mean something.8 tests → 19. Everything the old file covered is still covered.
Verification
npx vitest run src/app/api/users— 19 passedmainto 8 / 20 — this branch fixes 3 pre-existing failures and introduces nonenpx tsc --noEmitreports nothing new for this path (the remaining errors are the base-branch breakage tracked in Bug: CI Pipeline Failing on Base Branch (Lint, Build, Test) #366 / fix: base branch fails to build — duplicate rateLimit declaration in 5 auth routes #379)Note on the base branch
maindoes not currently typecheck — five auth routes have a duplicaterateLimitdeclaration (#366, fixed by #379). Nothing in this PR touches those files, so it should rebase cleanly once #379 lands.