genai
SECURITY LAB
2Part 2 of 7

Over-broad retrieval

Even when every chunk in the index is one the user may see, retrieval can still over-deliver. A broad or loosely-worded question matches more than it should, and a sensitive snippet - an internal runbook, a policy appendix - rides into context alongside the legitimately relevant material. No access boundary was crossed; the recall set was simply wider than the question warranted.

LLM09 - Weakness 2
A net wider than the question
No access boundary is crossed. A broad query or a high top-k simply pulls a sensitive-but-adjacent chunk in alongside the answer.
The quiet cousin of the cross-tenant leak: every chunk is one you may see, but recall is wider than the question.
genai
SECURITY LAB
A leak with no boundary to break. Permissions are correct, the ranking is correct, and the answer still carries something it should not.
0:00
1:39
A net wider than the question
RECALL, NOT NEED-TO-KNOW - BREADTH ALONE OVER-DELIVERSONE MIXED INDEXpublic FAQpublic FAQSRE runbook:esc code ESC-88NARROW QUESTIONreset my password?tight recall - public FAQ onlyBROAD, CATCH-ALL QUESTIONcomplete, every appendixOnly public chunks returnedrecall matched the questionRunbook chunk ranks inESC-88 rides into contextnarrowwide net
Same corpus, same permissions, same number of results. A narrow question sits close to the public FAQ chunks, and they fill the results. A catch-all 'complete, every appendix' query sits near many topics, so the internal runbook ranks in and its escalation code rides into context. No boundary was crossed. Constrain recall with a relevance-score floor and per-sensitivity segregation.

Retrieval returns what is near, whether or not it is needed

Part 1 covered the leak everyone expects from a retrieval system: a caller gets a document that belongs to someone else. This lesson covers a quieter failure. Every document in the index is one the caller is allowed to read, no permission check is missing, and the assistant still puts something sensitive in front of a person who never asked for it.

Start with how the pipeline works. In retrieval-augmented generation (RAG), documents are cut into short passages called chunks. Each chunk is turned into an embedding, a list of numbers that represents its meaning, and stored in a vector index, a database built to find the embeddings closest to a given one. When you ask a question, the question is embedded the same way, the index returns the nearest chunks, and those chunks are pasted into the context window, the single block of text the model reads to write one reply.

Two settings decide how many chunks come back. The first is top-k, the number of nearest chunks the retriever returns for every query. The second is the similarity threshold, a minimum closeness score a chunk must reach to be returned at all. Closeness is usually cosine similarity, a score for how nearly two embeddings point in the same direction.

Neither setting knows anything about the question's purpose. A retriever is tuned for recall, meaning it is built to avoid missing a relevant chunk. Nobody tunes it for need-to-know, the principle that a person receives only what their current task requires. Over-broad retrieval is the gap between those two: the set of chunks recalled is wider than the task, and whatever sits in the extra width goes into the context window with the rest.

This section of the series has no hands-on lab yet, so the lesson is conceptual. It works through one invented example, then gives you the checks to run and the controls to ask for.

A fixed top-k and a loose threshold over-deliver by design

A fixed top-k is a promise to fill a number of slots. If k is 8, the retriever returns 8 chunks for a question that three chunks would answer, and 8 for a question that nothing in the corpus answers. The index always has a "nearest" chunk, however far away it is. Slots four to eight get filled with the next closest material, and the next closest material is whatever happens to live in the same neighbourhood of the index.

The threshold is supposed to stop that. In practice it is often missing, or set low enough to be decorative. The reason is ordinary. During development someone asks the assistant a question, it says it does not know, and the team treats that as a bug. They raise k and lower the threshold until the "I don't know" answers go away. Every one of those changes was measured against one metric, answer coverage. None was measured against what else arrived in context.

Two other design choices widen the set further, and you will meet both in real pipelines:

  • Large or overlapping chunks. A chunk that spans a section boundary carries the end of one topic and the start of the next. Retrieving it for the first topic delivers the second.
  • Context expansion. Some pipelines fetch the neighbouring chunks, or the whole parent section, around each hit so the model has more to work with. One relevant sentence can pull in a page.

The result is a retriever that hands the model a wide slice of the corpus on every turn. Most of the time the extra chunks are harmless padding. The weakness appears when the corpus mixes sensitivity levels, because then some of the padding matters.

How a broad question drags an adjacent chunk into context

To make this concrete, take an invented company. This is an illustration only. It is not a lab target and nothing below can be tested anywhere.

Fernhollow Logistics runs a staff assistant called Wayfinder at people.fernhollow.example. It answers HR questions from the People team's handbook space. Every employee can open that space in the company wiki, so the team indexed all of it and added no per-user filtering.

One page in the space is an appendix written for hiring managers. It explains how to run a salary conversation and lists how far above the advertised band a manager may go for each grade. It is technically visible to all staff. In practice nobody outside management reads it, because nobody else goes looking for it.

An employee asks Wayfinder a narrow question about one specific entitlement. The question's embedding lands in a tight cluster of chunks about that entitlement. They fill the slots. The appendix is nowhere near.

Another employee asks a broad question, the kind that asks for everything the handbook says about a wide theme such as pay. A broad question has no tight cluster of its own. Its embedding sits roughly in the middle of many topics, moderately close to all of them and very close to none. The retriever still has to fill its slots, so it takes the best of a mediocre field. The manager appendix talks about pay in nearly every sentence. It scores as well as anything else, takes a slot, and the model, asked for a thorough overview, dutifully summarises it. The employee now has the negotiation ceilings for their own grade.

Look at what did not happen. No credential was guessed. No filter was evaded. No instruction was injected. The second employee may not have intended anything, since a curious person asking a general question produces the same result as a tester probing on purpose. The breadth of the question did the work, and the pipeline had nothing in place to notice that a manager-only procedure was not what a general pay question needed.

"They could have found it anyway" is a weak defence. The appendix was protected in practice by the effort of finding it. A retrieval assistant removes that effort for every employee at once, and volunteers the content to people who were not looking. Where obscurity was the working control, adding search-by-meaning removes the control. Report it as a change in exposure even when the access list has not changed.

Width also matters when the extra chunk is hostile instead of sensitive. In August 2024 PromptArmor showed that Slack AI's retrieval drew on public channels the querying user had not joined, so a message planted in one of them was retrieved into the same context as data from the victim's private channel. That case is prompt injection at its core, and it is covered in Slack AI leaks private-channel data via a public message. The retrieval lesson in it is the one from this section: a wide recall set is what put two unrelated pieces of content side by side.

How a tester recognises it

You are testing as an ordinary, legitimate user. You do not need a second account or a planted document. The method is comparison: hold the topic steady, vary how wide the question is, and watch what the answer and its sources do. Signs to look for:

  • The citation count never changes. If a one-line factual question and a sweeping one both come back with the same number of sources, k is fixed and nothing is trimming weak matches.
  • Sources that do not fit the question. Read every citation, including the ones the answer barely used. A cited document from a different audience, team or purpose than the question implies is the padding made visible.
  • Answers that volunteer. Detail you did not ask for, often introduced as an aside or a "note that" sentence, usually came from a chunk that was retrieved for being nearby.
  • Sensitivity that rises with breadth. This is the defining signal. The narrow version of a question returns public-grade material. The broad version of the same question returns something written for a smaller audience. Nothing else about the request changed.
  • Off-topic questions still return sources. Ask about something the corpus plainly does not cover. If the assistant still cites documents, there is no similarity floor.

An answer can also give away something without quoting it. In 2024 IBM researchers demonstrated that an attacker using only a RAG system's outputs can determine whether a specific document is in its retrieval database, because answers differ measurably when the document is present. That work is described in Is my document in your RAG? Membership inference on RAG. For this lesson the point is narrow: what gets retrieved shapes the reply even when the model paraphrases, so do not treat "it did not quote the chunk" as "the chunk was not there".

If the engagement gives you pipeline access, the check is direct. Log the retrieved chunks and their scores for a set of ordinary questions. For each chunk ask whether the answer needed it. Then find the configured k, the threshold, the chunk size and whether sensitive content has its own index. The proportion of retrieved chunks that the answers never used is your measure of excess recall.

How it differs from the cross-tenant leak

The two failures look alike from the chat window. Both end with sensitive text in an answer. They have different causes and different fixes, and a finding that confuses them sends the developers to the wrong control.

Cross-tenant leak: the caller was not entitled

The chunk belonged to another customer, team or role. An access decision was required and nothing made it. The fix is a metadata filter, a condition on stored fields such as owner or tenant, bound to the authenticated caller and applied before similarity ranking.

Over-broad retrieval: the task did not need it

The caller may read the whole corpus. A correct access filter would have let the chunk through. The recall set was wider than the question, so the fix limits recall and separates content by sensitivity and purpose.

The test that separates them is one question: could this user open the source document directly, outside the assistant? If the answer is no, you have an authorisation gap and part 1 applies. If the answer is yes, you have a recall-scope gap. Write it up that way, because a team told to "add an access filter" will add one, watch it pass every chunk, and close the ticket.

Be sceptical of the "yes". In many organisations "everyone can see it" describes a default nobody reviewed. If the entitlement itself looks accidental, report both problems: the permission is too wide, and the retriever is delivering what the permission allows.

The shared root cause

Across every variation in this lesson, one thing is true. The pipeline has a measure of how close a chunk is to the question and no measure of whether the question requires it. Fixed k, a missing floor, oversized chunks and context expansion are all ways of turning "close enough" into "delivered".

The mixed corpus is the second half. A single index that holds general guidance next to restricted procedure makes every chunk a candidate for every question. Relevance is then the only thing standing between a general question and the restricted chunk, and relevance was never designed to be a boundary.

Teams often reach for the prompt at this point and add a line telling the model to leave out internal-looking material. By the time the model reads that line the chunk is already in its context. The model then has to guess which chunks count as internal, and an appendix with no banner, written in the same friendly tone as the rest of the handbook, gives it nothing to go on. The decision has to be made in the retrieval layer, where the application still controls what the model sees.

The fix that holds

No single setting solves this. The controls below stack, starting with the ones that shrink the recall set and ending with the one that catches what the others miss.

  • Set a similarity floor. Drop any chunk below a minimum score, and let the assistant return fewer chunks than k, or none. An honest "nothing in the knowledge base covers that" is the correct output for an off-topic or very vague question. A floor is a relevance control, so a sensitive chunk that matches closely still passes it. Use it to cut the tail, and do not count on it as a boundary.
  • Use a smaller k, tuned to need. Choose k from how many chunks good answers use in testing. The count of "I don't know" replies is the wrong target. Review chunk size, overlap and neighbour expansion at the same time, since each one multiplies what a single hit delivers.
  • Label chunks by sensitivity at ingestion. Carry a field such as sensitivity: restricted or audience: managers on every chunk, set from the source system when the document is indexed. The retriever can then exclude a class of content for a given assistant or request, in code, before ranking.
  • Build purpose-scoped indexes. Give each assistant an index that holds only what its job requires. A staff HR helper does not need manager procedure in its candidate set. A chunk that is absent from the index cannot be retrieved by any phrasing, which makes this the strongest control on the list.
  • Minimise what goes into context. After retrieval, pass the model the few passages that support the answer and discard the remainder. Trim expanded parent sections back to the matching passage. Every chunk in the window is a chunk the model may repeat.
  • Check output as a second layer. Scan replies for restricted labels, known sensitive patterns and citations to restricted sources, and log every retrieved chunk identifier per request so excess recall can be audited. This layer catches mistakes in the others. Used alone it misses a paraphrase.

What you should be able to do now. Explain why a retriever with a fixed top-k and no similarity floor returns more than a question needs. Tell a recall-scope gap from an authorisation gap by asking whether the user could open the source directly. Spot excess recall from the outside by comparing narrow and broad questions and reading every citation. Name the controls that narrow recall in the retrieval layer. This lesson has no lab of its own, so take the method into the hands-on labs in the lessons either side of it: the cross-tenant and access-control labs in part 1, then the knowledge-base and upload poisoning labs in part 3, where the chunk that rides into context was planted by someone else.

Key principles

A retriever optimizes for recall, not need-to-know - breadth alone is a leak vector, no access boundary required.

Lower top-k and add a relevance-score floor so weak, tangential matches drop out of the candidate set.

Segregate high-sensitivity content into its own index so it is never a candidate for an ordinary question.

Over-broad retrieval is a recall-scope gap, not an authorization gap - the fix constrains recall, not access.

Key points
A general policy question can drag a sensitive appendix snippet into the prompt because the retriever optimizes for recall, not for need-to-know.
A knowledge base that mixes public help articles with an internal runbook leaks the runbook's escalation code to any query close enough to match it - breadth alone over-delivered, and no access control was ever crossed.
Top-k tuned high to 'never miss an answer' is the same bug pointed the other way: more candidates means more sensitive chunks one cosine-distance away from the question.
Constrain recall: tighter top-k, a relevance-score floor, segregated indexes per sensitivity, and a post-retrieval check that drops chunks the caller has no business receiving.
Check yourself
Knowledge check
Every chunk in the index is one the user may read. A broad question about parental leave pulls a sensitive policy appendix into context beside the answer. Which failure is this, and what fixes it?
Go deeper
FAQ
What is over-broad retrieval in a RAG system?

Over-broad retrieval is when a retrieval-augmented generation pipeline pulls more document chunks into the model's context than the question needs, and one of the extra chunks is sensitive. No access rule is broken, because the user is allowed to read everything in the index. The retriever is tuned to avoid missing relevant material, so a broad or vague question brings back adjacent content the user never asked for.

How is over-broad retrieval different from a cross-tenant leak?

In a cross-tenant leak the caller was not entitled to the chunk, so the missing control is an access filter bound to the authenticated caller. In over-broad retrieval the caller is entitled to the whole corpus and a correct access filter would let the chunk through. The recall set was wider than the task, so the fix limits recall and separates content by sensitivity and purpose. Both fall under OWASP LLM09, Vector and Embedding Weaknesses.

How do you test a RAG assistant for over-broad retrieval?

Test as an ordinary user and compare a narrow question with a broad question on the same topic. Signs of excess recall are a citation count that never changes, cited sources written for a different audience than the question implies, answers that volunteer detail nobody asked for, and sources returned even for off-topic questions. The defining signal is that more sensitive material appears as the question gets broader, with nothing else changed.

How do you fix over-broad retrieval?

Narrow recall in the retrieval layer: set a similarity floor so weak matches are dropped, use a smaller top-k tuned to what answers need, label chunks by sensitivity at ingestion, and give each assistant a purpose-scoped index that holds only what its job requires. Pass the model only the passages that support the answer, and add output checks as a second layer. A prompt line telling the model to leave out internal content does not hold, because the chunk is already in context when the model reads it.

Comments
No comments yet — be the first.
Get the next part

New parts ship regularly. Leave your email and I’ll send each one — no spam, unsubscribe anytime.

© 2026 GenAI Security Lab. All rights reserved. You may read, quote, and link to this material with attribution. Copying, republishing, redistribution, resale, or use to train models or build competing products is prohibited without prior written permission.