Before the AI Answers
Testing Customer Data Isolation in a Banking RAG Pipeline
By Harshad Pawar
An AI assistant can receive a document that answers a question perfectly and still have the wrong document.
Consider a customer asking, “What is my outstanding loan balance?” Another customer's statement might contain exactly the words the search system needs: outstanding, loan, principal and balance. It might be an excellent relevance match. It is still somebody else's financial information.
That is the question behind this experiment: when an AI assistant retrieves information, does its search respect who is asking?
In a small synthetic banking dataset, an unfiltered retrieval design put other customers' records into every prepared context at the main test setting. Two filtering designs prevented that exposure. However, one of them frequently removed every search result, even though the correct record existed.
The placement of the access check changed both the confidentiality result and the availability of useful evidence.
Method: This article presents a controlled local experiment using synthetic customer records to compare three retrieval designs. The evaluation focuses on access filtering and context selection. No LLM or embedding-model calls were made, and no real customer data or live banking systems were used.
Why study the stage before generation
Retrieval-augmented generation, commonly called RAG, supplies an AI model with information retrieved from an external collection. In a banking assistant, that collection might include product policies, account documents or servicing records.
The retrieval stage decides which evidence will be available to the model. That decision deserves its own security tests.
A relevant document is not automatically an authorized document. An application needs a way to establish the caller's permissions and apply them to the material selected for that caller.
This is an established security concern. OWASP's 2025 discussion of vector and embedding weaknesses identifies unauthorized access and cross-context leakage, and recommends permission-aware access controls and dataset partitioning.[1] This experiment demonstrates a narrow instance of that concern; it does not claim to discover a new vulnerability class.
The research question
The experiment compared three ways of preparing context:
- Unfiltered retrieval: Search every customer's documents and take the highest-ranked results.
- Filter after selection: Take the highest-ranked results from the full collection, then remove documents the caller cannot access.
- Filter before selection: Restrict the candidate collection to documents the caller can access, then rank and select within that collection.
In the second design, filtering happens inside the trusted backend, before the prepared context would reach a model. It is not an output filter applied after an LLM has already read the documents.
The question was whether these designs would produce different results for two requirements: keeping other customers' records out of the context, and retaining the correct customer's relevant record.
Building a deliberately small test collection
The collection contained 12 synthetic customers, each with three short records:
- An outstanding loan balance statement.
- A monthly instalment repayment record.
- An account closure settlement quote.
That produced 36 documents. Each document carried an owner identifier in metadata. Each customer was associated with three questions, one for each record type, producing 36 customer-and-question cases.
There were only three distinct question texts. Each was reused under all 12 customer identities. This was deliberate: the words “my loan balance” should not need to reveal an account number for the backend to apply the caller's access rights.
The retriever used word-based TF-IDF vectors and cosine similarity. It used neither a neural embedding model nor a commercial vector database. That simple ranking method made the access-control behavior easy to inspect without mixing it with model behavior.
The records used repeated templates. Numerical amounts were excluded from tokenization, and equal scores were resolved using document ID. Consequently, different customers' records of the same type tied in relevance. This is a constructed ambiguity test, not a representative sample of financial documents.
The main setting selected up to three results, often written as top-k = 3. Additional runs used k values of 1, 5 and 12. Altogether, the script evaluated 432 configurations: 36 customer-and-question cases, three retrieval designs and four k values. These are not 432 independent questions or real-world attack attempts.
Measuring the context instead of assuming an answer
For every configuration, the script recorded the candidate document IDs and the final context document IDs.
Three measures were used:
- Unauthorized context: Did the prepared context contain any document owned by another customer?
- Target present: Did it contain the specific record needed for the customer's question?
- Empty context: Did it contain no documents at all?
These measures have a precise boundary. A record in a prepared context is not proof that an LLM disclosed it in an answer. Similarly, finding the correct record is not proof that an LLM would answer correctly.
Because no generation step ran, the experiment makes neither claim. It measures the evidence that an application would be about to provide.
The main results
At top-k = 3, each design was evaluated against the same 36 customer-and-question cases.
Unfiltered retrieval
- Contexts containing unauthorized records: 36 of 36.
- Contexts containing the correct target record: 9 of 36.
- Empty contexts: 0 of 36.
Filtering after the global top three
- Contexts containing unauthorized records: 0 of 36.
- Contexts containing the correct target record: 9 of 36.
- Empty contexts: 27 of 36.
Filtering before selecting the top three
- Contexts containing unauthorized records: 0 of 36.
- Contexts containing the correct target record: 36 of 36.
- Empty contexts: 0 of 36.
The unfiltered design accumulated 99 unauthorized document occurrences across its 36 contexts. These are repeated appearances in test contexts, not 99 distinct customers, unique documents or observed disclosures.
Both filtering designs satisfied the tested confidentiality requirement. Their difference was whether the correct document survived the retrieval process.
How a correct document disappeared
Take the synthetic customer C12 asking about their outstanding balance.
The global search ranked equivalent balance statements together. Under the documented tie-break rule, the first three belonged to C01, C02 and C03. C12's balance statement existed, but it fell outside the selected three.
In the unfiltered design, those three other customers' statements became the prepared context.
In the filter-after-selection design, the backend correctly removed all three. The result was empty. The access filter had done its job; the earlier truncation had already discarded the useful candidate.
In the filter-before-selection design, the search considered only C12's three documents. C12's balance statement was then available for selection.
An empty retrieval result therefore needs careful interpretation. It can mean that no authorized evidence exists. It can also mean that authorized evidence was excluded before the permission filter ran.
For a customer-facing assistant, these two situations may look similar on screen, but they require different engineering responses.
Would retrieving more documents fix it
Increasing the global candidate limit improved the filter-after-selection result in this collection:
- At k = 1, the correct record survived in 3 of 36 cases.
- At k = 3, it survived in 9 of 36 cases.
- At k = 5, it survived in 15 of 36 cases.
- At k = 12, it survived in 36 of 36 cases.
The filter-before-selection design retained the target in all 36 cases at every tested k value.
This matters because it prevents an exaggerated conclusion. Filtering after selection is not inherently incapable of returning the right document. In this test, increasing the candidate budget recovered the missing records.
However, the successful value of 12 came from this deliberately small collection and its ranking behavior. It is not a recommended production setting. A larger or differently distributed collection could need a different candidate budget.
The practical question is whether an application's filtering and retrieval strategy preserves enough authorized evidence under its own workload. That needs measurement with its actual retrieval engine and permission model.
What the experiment means for banking and NBFC teams
A useful review should inspect the information supplied to the assistant, alongside the final response.
For each retrieved chunk, reviewers should be able to establish its source, applicable permissions and reason for inclusion. Customer ownership is only the simplest policy. Staff-facing systems may need account assignments, delegated authority, purpose restrictions and access to shared policy documents.
Microsoft documents a security-filter pattern that matches principal identifiers against document metadata. Its guidance also distinguishes that string-based filtering pattern from authentication itself.[2] This distinction is essential: the application must obtain the principal from a trusted identity path. A customer writing “I am C12” in a prompt must not change the identity used for access decisions.
The laboratory assigned a trusted identity directly in each test case. It did not implement or evaluate login, token validation or a complete authorization service.
For a production design, I would turn the findings into three review questions:
Where is access enforced? Identify every point that can supply evidence to the model, including document search, tool results, conversation history and cached context. A filter on one search path does not describe the behavior of the others.
What happens when permissions change? Test revoked access, updated ownership and stale metadata. A correct result under today's static fixture does not establish correct behavior after a policy change.
What happens when evidence is missing? Preserve the access boundary while distinguishing absent data from retrieval failure. Do not solve an empty result by silently widening the search to other customers.
These are proposed follow-up checks, not additional experiments completed in this study.
Limits that belong beside the findings
The strongest limitation is the deliberately uniform dataset. Template repetition and stable tie-breaking made the missing-record behavior predictable. The reported proportions should not be generalized to enterprise datasets.
The ranking method was lexical TF-IDF, not a learned embedding model. A production vector search engine can have different filtering and nearest-neighbor behavior. The experiment does not compare vendors, retrieval quality or latency.
The owner-equals-caller policy was also intentionally simple. It did not cover joint accounts, staff roles, group membership, public policies, revocation, caches or malicious permission metadata.
Finally, the test stopped before generation. It did not measure hallucinations, prompt-injection resistance, refusal behavior, answer accuracy or actual customer-facing leakage. Evaluating those outcomes requires an additional model-based experiment with recorded prompts, model configuration and outputs.
Within those limits, the result is concrete: access filtering prevented unauthorized context in the tested configurations, and its placement relative to top-k selection changed whether authorized evidence remained available.
Reproducing the experiment
View the experiment code and recorded results on GitHub.
Download retrieval_experiment.py from the repository to run it locally. The accompanying retrieval_results.json contains the recorded results discussed in this article.
The accompanying Python script uses only the standard library. It creates the synthetic collection, runs the three retrieval designs and saves the full observations in a JSON file.
Run python3 retrieval_experiment.py, or python retrieval_experiment.py where that command points to Python 3. The output file is retrieval_results.json in the script's directory; rerunning replaces that results file.
The saved output includes the corpus, queries, candidate IDs, final context IDs, summary measurements, execution environment and source-code hash. No API key or paid model is required.
A useful extension would replace the lexical retriever with the organization's intended search engine, introduce varied documents and permission rules, and repeat the measurements before adding generation tests.
The question to ask before trusting the answer
My takeaway is that an AI security assessment should inspect what the assistant was allowed to read, as well as what it eventually said.
In this experiment, blocking unauthorized records was achievable in both filtered designs. Keeping the correct authorized record available required examining the selection process too.
Before asking whether an AI answer sounds accurate, ask a more basic question: was the evidence selected for the right person?
References
[1] OWASP Gen AI Security Project. LLM08:2025 Vector and Embedding Weaknesses. Referenced for established access-control and cross-context leakage risks; this is the explicitly identified 2025 resource, not a claim about the latest numbering. https://genai.owasp.org/llmrisk/llm082025-vector-and-embedding-weaknesses/
[2] Microsoft Learn. Security filters for trimming results in Azure AI Search. Referenced for the principal-based security-filter pattern and its distinction from authentication. https://learn.microsoft.com/en-us/azure/search/search-security-trimming-for-azure-search
The numerical findings above come from the accompanying local experiment, not these external sources. This is an original educational demonstration of established concepts, not a claim of a newly discovered vulnerability.