DAT-61Required
Filter search results by access inside the query
Put the user's access filter into the search query itself. Hits, totals, facet counts, suggestions and excerpts then come only from records the user may see.
How to do it
Add the account or owner condition next to the match condition, in the same WHERE clause or in the search engine's filter. Build that filter on the server from the session, never from a request field. Filtering hits after the search leaks through totals, facets and autocomplete, and breaks paging. Store the account ID on every document in a separate index, so the filter works there too. In PostgreSQL, the row-level security from Keep each customer's data apart applies to full-text queries as well. Each record a user opens from the results still needs the check in Check ownership on every record. Search for AI answers follows Copy access rights into the index and check them again.
Examples
JavaScript example
// The account comes from the session, never from the request body
const hits = await db.many(
`SELECT id, name FROM products
WHERE account_id = $1 AND search_text @@ websearch_to_tsquery('english', $2)
LIMIT 20`,
[session.accountId, query]);
Python example
# The account comes from the session, never from the request body
cursor.execute(
"""SELECT id, name FROM products
WHERE account_id = %s AND search_text @@ websearch_to_tsquery('english', %s)
LIMIT 20""",
(session.account_id, query))
How to check
Create two test accounts, each with a record holding a made-up word only it contains. Signed in as account A, search for account B's word. The answer has 0 hits, a total of 0, no suggestion and no facet count above 0.
Sources
Related rules
- Keep each customer's data apart in Data
- Check ownership on every record in Security testing
- Copy access rights into the index and check them again in AI features
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/data/search/search-access-filter">Agent Wranglers data playbook</a>. In code you write, add a comment next to it with the same link.