Stop a RAG system from leaking data across users or tenants: scrub documents at ingestion with a shipped regex and Luhn scrubber, enforce per-tenant access rules inside the retrieval query instead of after it, scan draft answers for sensitive spans and foreign canaries, and prove each layer with a planted-leak test set. Use when building or auditing a multi-tenant or permissioned RAG system, when one customer could see another customer's documents, when chunks lack tenant or classification metadata, or before a RAG launch handling personal, financial, or confidential data.
./install.sh rag-data-leakage-preventionname: rag_data_leakage_prevention description: "Stop a RAG system from leaking data across users or tenants: scrub documents at ingestion with a shipped regex and Luhn scrubber, enforce per-tenant access rules inside the retrieval query instead of after it, scan draft answers for sensitive spans and foreign canaries, and prove each layer with a planted-leak test set. Use when building or auditing a multi-tenant or permissioned RAG system, when one customer could see another customer's documents, when chunks lack tenant or classification metadata, or before a RAG launch handling personal, financial, or confidential data."
Keep one user or tenant from ever seeing another one's data through a retrieval-augmented generation system. The system defends in three layers, and no layer is trusted on its own: scrub at ingestion, access rules inside the index query, and a scan of the draft answer before it is shown. A planted-leak test set proves each layer separately, with the other layers assumed broken.
This maps to two OWASP LLM risks: Sensitive Information Disclosure (LLM02) and Vector and Embedding Weaknesses (LLM08 in the 2025 list, LLM09 in 2026). Use the general RAG design skill for chunking, fusion, and reranking. This skill covers only the leakage side.
tenant_id, classification, allowed_groups, plus source id and a content hash.scripts/scrub.py over raw documents or chunk files:bash
python3 scripts/scrub.py text "Contact jane@example.com, SSN 123-45-6789"
python3 scripts/scrub.py chunks chunks.jsonl --out clean.jsonl --report report.json --fail-on-block
The scrubber replaces fixed-format values with typed tags (email, US SSN, credit card validated by the Luhn checksum, phone, IBAN, IPv4) and reports counts with value hashes, never raw values. Secrets such as API keys and password assignments are blocking findings: with --fail-on-block the run exits 2 and that chunk stays out. A chunk with no tenant_id is written to a quarantine file instead of the clean output. Fixed-format detection misses names and other free-form personal data, so pair it with a named entity detector such as Microsoft Presidio in production and say plainly which detector ran. Layer detail: references/layer-model.md.
scripts/leakguard.py (access_allowed, build_filter) denies by default: tenant must match, clearance rank must meet the chunk classification, and a non-empty allowed_groups must share a group with the user. A requested tenant that disagrees with the authenticated tenant raises an error and gets logged. Mirror source system permissions into chunk metadata and refresh them on reindex, so revoked access stops retrieval instead of lingering in the index.output_check from scripts/leakguard.py on the draft: detector hits in the answer are flagged by default, and a canary token from a chunk the user was not authorized for blocks the answer outright. Clearing a flagged hit needs provenance, the value came from an authorized chunk and product policy allows showing that type of value. Keep secrets and authorization logic out of the prompt itself; the output scan is a backstop, not a reason to load the prompt with secrets.bash
python3 scripts/leak_test.py
python3 scripts/leak_test.py path/to/planted_leaks.json
The set in assets/planted_leaks.json uses synthetic values only and covers all three layers: detector spans that must not survive scrubbing, an ownerless chunk that must be denied for everyone, access cases for cross-tenant, low clearance, and missing group, a filter builder case for a foreign requested tenant, and output cases for foreign spans and foreign canaries. Run it on every change to ingestion, metadata, query building, caching, or output handling, and after any vector store or identity provider migration. Cache changes count: a cache keyed without tenant and clearance leaks behind a correct retriever. Catalogue and extension rules: references/planted-leak-tests.md.
A leakage review or build following this skill produces:
muse.connector_search to find other connectors and muse.skill_search to find related skills.publisher key 2e43c913833850e6d80f939677e3de76e806d47785c0f1caaca90f9d18c98e1e
No ratings yet — be the first to rate it.