Back to Technical Mode

RAG / Vector Store Cross-Tenant Data Leakage

Retrieval-Augmented Generation pulls relevant documents from a vector database to give the model context. If that similarity search isn't scoped to the requesting user's own tenant/organization, a query can retrieve — and the model can then summarize or quote from — documents that belong to a completely different customer.

A vector similarity search finds the closest-matching embeddings across whatever it's given to search — it has no inherent concept of 'ownership' unless the query explicitly filters for it
This is architecturally the same bug as a SQL query missing a WHERE tenant_id = ? clause — it's IDOR, just at the embeddings-index layer instead of the row layer
Embedding the tenant/user ID as metadata on every vector at ingestion time, and filtering the search by it, is the standard and necessary fix — most vector databases support metadata filtering natively
Testing this requires an actual cross-tenant query, not just checking that same-tenant retrieval works — a passing 'happy path' test suite can completely miss this class of bug

Want this flow explained in detail as it plays?

POST/api/ask

Headers

Host:app.flowground.dev
Content-Type:application/json

Body

Loading...

Quick-Insert Payloads

Defense in depth

  • Tag every vector at ingestion time with the owning tenant/user ID as metadata, and require that filter on every single retrieval query — never run an unscoped similarity search against multi-tenant data
  • Enforce the tenant filter at the database/query layer, not just in application code that calls the vector store — a missed filter in one code path shouldn't be able to bypass it
  • Write an explicit cross-tenant retrieval test (as tenant A, query for content only tenant B has) as part of the standard test suite for any RAG feature, not just single-tenant happy-path tests

Vulnerable vs. Fixed

async function retrieveContext(question) {
  const embedding = await embed(question);
  return vectorDb.query({ vector: embedding, topK: 5 }); // searches the entire index, every tenant included
}