July 17, 2026
Every team building on AI ships the same feature. A lot of them ship the same bug.
Every company building on top of LLMs eventually ships workspaces. Teams, projects, organizations, tenants, whatever you call them. The…
By Dmitry Maranik
3 min read
Every company building on top of LLMs eventually ships workspaces. Teams, projects, organizations, tenants, whatever you call them. The pitch is always the same: your knowledge base, your chat history, your documents, walled off from everyone else on the same instance.
That wall is the one thing a multi-tenant product cannot get wrong. So a few months ago I started checking whether it holds.
I source-reviewed more than 200 self-hostable AI and SaaS products for a single class of bug, and confirmed cross-tenant data exposure in 78 of them: one tenant able to read, and in some cases modify or delete, another tenant's data.
The bug: writes are guarded, reads are not
Here is the shape that kept repeating. A developer adds "delete this item" and remembers to check ownership: does this item belong to the caller's tenant? Then someone adds "view this item," or "list its embeddings," and the check never gets copied over. The read fetches by a raw id with no tenant filter. The ids are usually sequential integers, so "view item 5" quietly returns another tenant's item 5.
I started calling these un-retrofitted read siblings. Once you have seen one, you stop reading the whole codebase and start grepping: find the endpoint that checks ownership, then look at the neighbors that don't. Across 78 products it was the same shape almost every time. This is not 84 unrelated bugs. It is one bug, 84 times.
There is a second, nastier variant. The check runs, but on the wrong thing. The handler authorizes the path you supplied rather than the object you asked for. You pass your own workspace's id, which you are allowed to touch, alongside a victim's object id, and the permission check and the object lookup disagree about whose data this is. The two most damaging versions I confirmed were an embedding-vector read (raw RAG vectors, which you can partially reconstruct source text from) and a "re-index my connector" action that pulled a victim's private GitHub and Notion content using the victim's own stored OAuth token.
The ones already fixed
Most of the 84 findings are still under coordinated disclosure, so I name only the ones that are already public because the maintainer shipped a fix. The teams that moved fastest earned the credit:
- SurfSense fixed a cross-tenant connector re-index that exfiltrated another tenant's GitHub and Notion content using their stored credentials. PR #1503
- AnythingLLM fixed an embed widget exposed to anonymous origins by default. CVE-2025–63390, PR #5759
- Baserow fixed a cross-workspace disclosure of a field's values by serial id. PR #5613
- aideepin fixed a cross-user read of another user's RAG chunk text, embedding vectors, and document text by id. PR #105
- Flagsmith fixed a cross-project read of another project's flag values, and the fix swept four sibling viewsets at once, which is exactly the right way to respond. PR #7945
The rest stay unnamed until their fix ships, because I am not going to hand attackers a live target before users are protected.
What this means if you build on AI
Three things, in order of how often they bit.
Authorize reads like you authorize writes. The list/get/search endpoint is not the low-risk one. It is the exfiltration endpoint. When you add an ownership check, grep for every sibling that touches the same object and give them the same check. Better still, enforce tenant scoping at a layer the individual handler cannot forget: a query interceptor, a row-level policy, a middleware. Then isolation is the default instead of something each endpoint opts into.
Treat embeddings and cache keys as tenant data. Some of the worst leaks were not documents. They were RAG vectors and response caches whose keys forgot to include the tenant. If two tenants can land in the same cache bucket or read each other's vectors, that is a data leak.
If you self-host, verify the isolation you are assuming. Several popular tools are single-tenant, or shared by design, in their open-source edition. That is a fine design. But if you are putting multiple customers on one instance expecting a wall, check that the wall exists.
On disclosure
Everything here was tested on isolated, self-hosted instances with synthetic accounts and synthetic data. Nothing touched a third-party or production system. Every finding went to the maintainer privately first, with a 90-day hold and an offer to write the fix. The live list of what has been found and disclosed is kept current at sectum.ai/research, where each advisory appears as it publishes. This piece started as a writeup on our blog, which stays the canonical, up-to-date version: I source-reviewed 200+ self-hosted AI tools for tenant isolation.
If you ship multi-tenant AI, the fastest useful thing you can do this week is grep your own reads: find where you check ownership on a write, then confirm the sibling read does the same. The checks I ran are open source. Sectum AI stands up two tenants, runs the cross-tenant battery, and tells you whether the wall holds, before someone less friendly finds out for you.