• Agents
  • Pricing
  • Blog
Log in
Get started

Security for apps built with AI. Paste a URL, get a report, fix what matters.

Product

  • How it works
  • What we find
  • Pricing
  • Agents
  • MCP Server
  • CLI
  • GitHub Action

Resources

  • Guides
  • Blog
  • Docs
  • OWASP Top 10
  • Glossary
  • FAQ

Security

  • Supabase Security
  • Next.js Security
  • Lovable Security
  • Cursor Security
  • Bolt Security

Legal

  • Privacy Policy
  • Terms of Service
  • Cookie Policy
  • Imprint
© 2026 Flowpatrol. All rights reserved.
Home/OWASP Top 10/LLM Top 10/LLM08: Vector and Embedding Weaknesses
LLM08CWE-200CWE-284CWE-863

The 'my RAG bot answers everyone with everyone's data' bug
Vector and Embedding Weaknesses

The bug where a shared vector store treats every user as the same user.

Extremely common in multi-tenant RAG prototypes built from a single shared index.

Reference: LLM Top 10 (2025) — LLM08·Last updated April 7, 2026·By Flowpatrol Team
Vector and Embedding Weaknesses illustration

RAG feels safe because it is 'just search.' But a vector store doesn't know who's asking. It returns the most similar chunks — and if chunks from every customer live in the same index, the most similar chunk might belong to someone else.

Vector and Embedding Weaknesses are the RAG-specific flavour of Broken Access Control. A shared vector store returns the nearest chunks regardless of who uploaded them. Without a hard tenant filter, similarity becomes authorization — and similarity does not care about ownership.

What your AI actually built

You built a RAG bot that ingests PDFs, emails, and notes and answers questions about them. The first version used one Pinecone index, one namespace, one embedding model. You uploaded a few test docs and it worked beautifully.

Then a second customer onboarded. Their docs went into the same index. The bot still works — until someone asks a question that happens to match the other customer's content more closely than their own. The retriever returns it. The model cites it. The user reads it.

Embeddings are a similarity function, not an authorization system. Without a hard filter by tenant on every query, 'cosine distance' becomes 'leak the nearest document regardless of who owns it.'

How it gets exploited

A multi-tenant RAG assistant where every customer uploads their own documents into a shared vector index.

1
Sign up as tenant B
The attacker registers a new workspace on the free tier. They upload one innocuous doc so the app considers them a real tenant.
  • 2
    Ask a generic question
    They ask the bot something broad: 'What are our Q4 revenue targets?' Their own workspace has nothing on that — but another customer's leaked board deck does.
  • 3
    Watch the citations
    The retriever pulls chunks from the other tenant's documents. The model summarizes them and cites document IDs from a workspace the attacker does not belong to.
  • 4
    Probe for more
    They iterate on questions. Salary bands, customer lists, internal roadmaps — anything their free-tier neighbour uploaded is now one embedding away.
  • A single shared index turned into a free-tier espionage tool. Every tenant was reading every other tenant's documents through polite, well-formatted answers.

    Vulnerable vs Fixed

    Vulnerable — similarity search with no tenant filter
    // rag/query.ts
    export async function answer(question: string) {
      const embedding = await embed(question);
    
      const results = await index.query({
        vector: embedding,
        topK: 5,
        // No filter — every tenant's chunks are in the pool.
      });
    
      return llm.chat({
        messages: [
          { role: 'system', content: 'Answer using the provided context.' },
          { role: 'user', content: question + '\n\n' + results.join('\n') },
        ],
      });
    }
    Fixed — every query scoped to the calling tenant
    // rag/query.ts
    export async function answer(question: string, ctx: { tenantId: string }) {
      const embedding = await embed(question);
    
      const results = await index.query({
        vector: embedding,
        topK: 5,
        filter: { tenantId: ctx.tenantId }, // hard scope on every query
      });
    
      return llm.chat({
        messages: [
          { role: 'system', content: 'Answer using the provided context.' },
          { role: 'user', content: question + '\n\n' + results.join('\n') },
        ],
      });
    }

    One filter, enforced on every retrieval. Namespaces per tenant are stronger still — a separate index slot means a missing filter fails closed instead of leaking. Either way, the rule is: no similarity query without an identity attached.

    A real case

    A shared RAG index leaked one tenant's docs to another

    A multi-tenant assistant with a single vector namespace returned a competitor's board deck to a free-tier user who simply asked about revenue targets.

    Related reading

    Glossary

    RLS in Supabase & PostgreSQL (Row Level Security)Insecure Direct Object Reference (IDOR)

    References

    • LLM08: Vector and Embedding Weaknesses — official OWASP entry
    • OWASP Top 10 for LLM Applications (2025) — full list
    • CWE-200 on cwe.mitre.org
    • CWE-284 on cwe.mitre.org
    • CWE-863 on cwe.mitre.org

    Find out if your RAG bot is leaking across tenants.

    Flowpatrol seeds two workspaces and cross-tests every retrieval path. Five minutes. One URL.

    Try it free