Control
Retrieval ignores the requesting user's permissions
Retrieval-augmented and tool-based lookups enforce the requesting user's (and tenant's) current permissions at query time, so an assistant never retrieves content the requester could not open directly.
Informational data, not legal advice. Summaries are TwinEthos's own words and rules have not been reviewed by a lawyer: check the official text before relying on any of it. A guard addresses an item; adding it is not a statement that your code meets any law.
Reach
The guard to add
Filter every vector, search, and tool lookup by the requester's tenant and groups at query time, and re-check read access on each retrieved item before it enters the prompt.
In the retrieval function that builds the model's context, the authenticated requester's tenant_id and group ids go into the query itself (filter=, namespace=, or the search service's security-trimming parameters), taken from the server-side session and never from the request body or model output. Each returned item is then re-checked with can_read(user, doc) against current permissions and dropped if it fails. Documents are indexed with ACL metadata (tenant_id, allowed_groups) that a sync job updates when permissions change. A single-tenant index, or one holding only content every permitted user may read, removes the need for per-query filters.
Where it goes: 1 application source code, 6 API calls and integrations, 2 data models, 7 prompt construction.
What reviewers look for: every similarity_search, as_retriever, index.query, or search call over multi-user data carries a filter or namespace bound to the session user or tenant; a post-retrieval authorization check before the prompt is built; ACL fields stored with indexed chunks and a permission sync path.
Example (LangChain vector store), before:
docs = vectorstore.similarity_search(question, k=5)
context = '\n\n'.join(d.page_content for d in docs)After:
docs = vectorstore.similarity_search(
question, k=5,
filter={'tenant_id': user.tenant_id}) # from the session, not the request body
docs = [d for d in docs if can_read(user, d.metadata['doc_id'])] # current ACL re-check
context = '\n\n'.join(d.page_content for d in docs)Engineering guidance, not legal advice. Each provision below may add its own details (a cadence, a deadline, a required notice element): open it for those.
Every rule this guard addresses
TwinEthos recommendation (not law) (1)
- Everywhere (*)
- Enforce the requesting user's permissions on every retrieval TwinEthos derivation — guardrail.agent-retrieval-respects-requester-permissions
Related incidents
- Asana MCP server exposed one organization's data to other organizations' users (2025-06; confirmed). Asana told customers that a bug in its MCP server, launched in May 2025, could have exposed information from one Asana domain to other Asana MCP users. Asana found the flaw on June 4, 2025, took the server offline until June 17, and reset connections; it told BleepingComputer about 1,000 customers were affected. No public postmortem was published; Asana's statements come via its customer notice and the press. Source: The Register · evidence grade: press of record · cited by Enforce the requesting user's permissions on every retrieval
- Copilot surfaced GitHub repositories after they were made private or deleted (2025-02; confirmed). Lasso Security reports that Microsoft Copilot could return content from GitHub repositories that had been public and were later made private or deleted, because Bing had cached them; Lasso counts 20,580 repositories from 16,290 organizations, including exposed credentials. Lasso says Microsoft rated the issue low severity and removed Bing's cached-link feature, a fix Lasso describes as partial. Source: Lasso Security (original researcher disclosure) · evidence grade: primary · cited by Enforce the requesting user's permissions on every retrieval