---
title: "Filter search results by access inside the · Agent Wranglers"
url: https://agentwranglers.se/playbook/search/access-and-limits/search-access-filter
id: SRH-10
level: Required
applies: accounts
risk: data
updated: 2026-10-27
build: a2a6bdfed830
tokens: 1264
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Filter search results by access inside the query

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [Access and limits](https://agentwranglers.se/playbook/search/access-and-limits.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers.md). Updated 2026-10-27.

- ID: SRH-10
- Level: Required
- Chapter: [Access and limits](https://agentwranglers.se/playbook/search/access-and-limits.md)
- Rule: Put the user's access filter into every keyword and vector search query itself. Hits, totals, facets, suggestions, excerpts and chunks sent to a model then come only from records the user may see.
- 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. Give a test user access to a document, search for it, then remove the access. The next search returns nothing from that document, even before the index updates.
- Risk: Data leaked or lost
- Applies when: The project has user accounts.
- How: 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 and the owner, team or group list on every document and chunk in a separate index. Then the filter works there too. Update the stored rights on every permission change event. Rights change faster than indexes, so check the remaining hits against the source system before you show them or send them to a model. In PostgreSQL, the row-level security from [Keep each customer's data apart](https://agentwranglers.se/playbook/data/multi-tenancy/tenant-isolation.md) 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](https://agentwranglers.se/playbook/accounts/roles-and-permissions/object-access.md). Never ask a model to leave out what the user may not read, because it cannot be trusted to. Database rows stay apart by tenant as [Keep files and search indexes apart by tenant](https://agentwranglers.se/playbook/data/multi-tenancy/tenant-scoped-storage.md) describes.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### JavaScript example

```js
// 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

```python
# 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))
```

### TypeScript example

```ts
const candidates = await index.search(question, { filter: { groups: { anyOf: user.groups } }, limit: 20 });
const allowed = await documents.filterReadable(candidates.map(c => c.documentId), user);
const hits = candidates.filter(c => allowed.has(c.documentId));
```

## Sources

- [OWASP Authorization Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html)
- [OWASP LLM09:2026 Vector and Embedding Weaknesses](https://github.com/GenAI-Security-Project/GenAI-LLM-Top10/blob/main/2026/final/LLM09_VectorAndEmbeddingWeaknesses.md)

## Related rules

- DAT-69 [Keep each tenant's files under its own prefix](https://agentwranglers.se/playbook/data/multi-tenancy/tenant-scoped-storage.md) in Data and databases
- DEN-31 [Filter shared dashboards by customer inside the warehouse](https://agentwranglers.se/playbook/data-engineering/access-and-bi-tools/dashboard-row-security.md) in Data engineering
- DAT-7 [Keep each customer's data apart](https://agentwranglers.se/playbook/data/multi-tenancy/tenant-isolation.md) in Data and databases
- ACC-77 [Check ownership on every record](https://agentwranglers.se/playbook/accounts/roles-and-permissions/object-access.md) in Accounts and identity

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/search/access-and-limits/search-access-filter">Agent Wranglers search playbook</a>`. In code you write, add a comment next to it with the same link.

- Next: [Cap the size of every search](https://agentwranglers.se/playbook/search/access-and-limits/search-limits.md)
- [Access and limits](https://agentwranglers.se/playbook/search/access-and-limits.md)
