RAG Puts Untrusted Documents Inside Your Trusted Prompt.
Retrieval augmentation was supposed to ground the model in your data. It also quietly turned every document in your index into something that can issue instructions to your agent.
Retrieval-augmented generation is usually introduced as a fix for a different problem. The model does not know your data and sometimes invents answers; so you give it a retriever, ground each response in your own documents, and the hallucinations recede. That part is real. What goes unsaid is that you have also opened a new channel directly into the model's context — and you have pointed it at a pile of documents you may not fully control.
The comfortable mental model is that retrieval fetches facts. It does not. It selects text and pastes that text into the prompt, in the same undivided context window that holds your system instructions. Every document in your index is now something that can speak to your model in the model's own language. Some of those documents were written by people who are not on your side.
Retrieval is an input channel
The mechanism is worth stating plainly, because the risk follows from it. A query is turned into a vector; the store is searched for the nearest chunks; the top matches are concatenated into the prompt as context; the model answers over the combined text. The retrieved chunks and your instructions occupy the same window, with no durable marker distinguishing one from the other.
That is an input channel, and it runs from your data store straight into the model. Teams rarely treat it as one. Retrieved content is thought of as inert reference material — background the model may consult — rather than as live input arriving from a source of variable trust. The moment you accept that retrieval is input, every lesson about untrusted input applies to it, and most RAG systems were not built with those lessons in mind.
Retrieved text is untrusted instruction
Because the model cannot reliably separate instruction from data, any retrieved chunk that happens to contain instructions is a candidate command. This is indirect prompt injection, delivered through your own index. The attacker does not need access to the user or the session. They need their text to end up among the documents that get retrieved, and then they wait for a relevant query to pull it into context.
A document that reads, in the middle of otherwise ordinary prose, as a direct instruction to the assistant becomes exactly that instruction the moment it is retrieved and placed in the prompt. Any corpus that ingests user-generated content, support tickets, uploaded files, crawled web pages, or collaboratively edited pages is a delivery mechanism for this. The index does not vet meaning. It stores and returns whatever was put in.
Poisoning the index
An attacker who can influence the corpus can do better than hope their payload is retrieved; they can engineer it to be. If content can be added to the index — through a public submission form, a crawled site, a shared wiki, a ticketing queue — then it can be shaped to rank highly for the queries the attacker cares about. Craft the text so its embedding sits close to likely questions, and the retriever itself becomes a reliable delivery channel, surfacing the planted document whenever a targeted query arrives.
The retriever's entire job is to find the most relevant text. Index poisoning turns that strength into the attack: relevance, optimized by the attacker, is what carries the payload into the prompt. Nothing about the search is malfunctioning — it is doing exactly what it was built to do, on content chosen to exploit it.
The access-control collapse
The quieter failure has nothing to do with injected instructions and everything to do with who is allowed to see what. A vector store, by itself, has no concept of per-user permissions. To maximize recall, teams load every document into one index and let similarity search do its work. At query time, the retriever returns the most relevant chunks regardless of who is asking.
So a question from one user can surface another user's confidential document, or a restricted record, purely because it was the closest match. The embedding has no idea the content is off-limits to this requester; relevance and permission are unrelated dimensions, and nothing in a bare vector search reconciles them. A feature built for better answers becomes a confidentiality breach that nobody explicitly designed and that leaves little trace behind it.
Per-user retrieval, not per-corpus
The fix is to make identity a first-class part of retrieval, so that the store can only ever return what the current requester is entitled to see.
- Filter per identity, before ranking — restrict the candidate set to documents this user may access first, then rank within it, rather than ranking the whole corpus and hoping to trim afterward.
- Carry permissions on the chunk — record each chunk's access scope in its metadata so the retriever can enforce it at query time instead of guessing.
- Never share one unfiltered index across tenants — separate stores or hard partition keys, so a nearest-neighbour search cannot physically cross a tenant boundary.
- Re-evaluate on every request — the identity retrieving is the identity that must hold permission; a background job or cached context must not silently widen the scope.
- Treat permitted chunks as untrusted anyway — permission to read a document is not permission for that document to command the model; the injection risk survives the access check.
Provenance and citation as controls
Citations are often treated as a user-experience nicety. They are also a security control. If the system must attribute each claim to the source chunk that produced it, you gain a surface on which to reason about trust: you can see which document drove an answer, notice when an unexpected or low-trust source influenced the output, and give the reader the means to judge for themselves.
Provenance can go further than display. Tag each chunk with its source and a trust level, and the pipeline can weight trusted sources above anonymous ones, or exclude low-trust content from sensitive queries entirely. A system that cannot say where an answer came from cannot defend the answer, and cannot detect the day a poisoned document started shaping its responses.
Retrieval does not fetch facts; it selects text and places it inside the prompt — which makes every document in your index both a potential instruction to your model and a potential leak to whoever happens to be asking.
A defensible RAG boundary
A defensible RAG design draws a clear boundary at the retriever and enforces it there. Retrieval is treated as untrusted input, not as trusted reference. Document permissions are applied per identity at query time, so the store can only return what the requester is cleared to see. Tenants are isolated rather than blended for convenience. Retrieved chunks are handled as content that may try to issue instructions, and answers carry provenance so their sources can be inspected.
When 6QURE reviews a retrieval system, the boundary at the retriever is where the attention goes: whether permissions are enforced per identity or merely assumed, whether anyone can place content into the index, whether the context the model finally sees can be traced back to its sources, and what a single planted or over-privileged document could do once it arrives in the prompt. Retrieval was meant to ground the model in your data. The work is making sure it does not also ground it in everyone else's.
Facing something like this in your own environment?