Search features get built as a genuine convenience, and they also have a specific way of quietly bypassing the same access rules the rest of an app respects, if they're not built carefully. Worth a dedicated check, because this one hides inside a feature that feels completely unrelated to the usual access-control conversation.
How this actually happens
A search feature's most natural implementation is: take the search term, query across the relevant table for anything matching, return the results. If that query searches across the entire table rather than being scoped to only the records the current user is actually allowed to see, the search results themselves become a way to surface data that the rest of the app would never have shown that user directly.
A concrete example
Picture a multi-tenant app where every normal page correctly filters by the current company's ID, a customer list, a report view, all properly scoped. If the search feature was built separately, as its own query against the full underlying table with the search term as the only filter, searching for a common name or keyword could return matches from other companies' records entirely, records that user could never have reached by browsing normally, but can now find simply by searching for the right term.
Why this is such an easy thing to get wrong
Building a fast, broad search often means writing one query that matches text across a table as simply as possible, and then, separately, whatever code renders the results. The scoping that every other page in the app applies consistently is a separate piece of logic that has to be deliberately added to the search query too, and it's an easy detail to skip when the goal in the moment is just getting the search to return relevant-looking results at all.
How to actually test this
- Create two separate test accounts (or two separate tenant/company accounts if your app is multi-tenant) with distinct, identifiable data in each.
- Log in as one account and search for a term you know exists only in the other account's data.
- If anything from the other account shows up in the results, even partially, or even just as a preview snippet, the search isn't respecting the same access boundaries the rest of your app does.
The fix
The search query needs the same scoping filter as every other query touching that data, the current user's or tenant's ID, applied at the database level, not filtered out afterward in your application code after the broader query already ran. Filtering after the fact still means the broader, unscoped data existed in memory at some point during the request, and depending on how results get built and cached, that's a riskier pattern than scoping the query itself from the start.
Search feels like a feature about finding things, not about permissions, which is exactly why it's easy to build without applying the same access-control instincts that show up everywhere else in the app. The underlying question is identical to everything else on this blog: who's allowed to see this, and does the code that returns it actually check.