AI Application Security Testing: The Authorisation Boundary

Many teams shipping AI features this year have added a retrieval path. A user asks a question, something resolves the documents that user is allowed to see, and a model answers from them. The feature may be new. The question underneath it is not: can this user reach that record.
That question has an established answer in the rest of your application, enforced by code you wrote and can point at. In the retrieval path it is often enforced somewhere newer, thinner, and harder to see, because the thing doing the resolving was added in a hurry and the identity of the person asking has to travel further to reach it.
The major risk lists address this problem, but they leave the application-specific implementation and testing to you. This is a guide to testing whether that boundary actually holds.
What the LLM risk lists cover, and what they hand back
OWASP publishes a Top 10 for LLM and generative-AI applications, currently the 2026 edition. It is worth reading properly rather than skimming, and it is worth being precise about its scope, because the scope is where the gap sits.
Two of its ten entries touch the question above. LLM03:2026 Excessive Agency deals with systems granted the ability to call tools or interact with other systems, including the risk of using an over-privileged identity on behalf of a user. LLM09:2026 Vector and Embedding Weaknesses is the closer one for retrieval, covering risks including cross-tenant leakage when similarity search runs across shared data before access control is applied.
The 2026 entry explicitly covers cross-tenant leakage through shared similarity search. OWASP describes a scenario where similarity search runs across a shared index before access control is applied, allowing an attacker to infer information about another tenant's data even when the documents themselves are not returned.
The current guidance is more specific. OWASP recommends enforcing tenant scoping inside the index query rather than applying it after retrieval, validating that scope server-side, and using physically separated indexes for high-sensitivity workloads.
Now notice what that guidance is and is not. It names a required property: retrieval should be permission-aware and partitioned. The current guidance goes further: OWASP recommends authorising before retrieval, enforcing document- and chunk-level authorisation inside the index query rather than after retrieval, and using separate indexes for high-sensitivity workloads. The exact authorisation model and implementation still depend on the application. It also says nothing about how the end user's identity should propagate through to the retrieval layer in the first place.
That is not a criticism. A risk taxonomy is supposed to name the property and leave the implementation to the people who know the system. But it does mean the list can be worked through carefully and still leave one question open, and it is an important question for avoiding a cross-tenant read: in your application, at what point is the requesting user's identity applied to the retrieval, and does the answer hold on every path that reaches it.
Why the boundary is easy to lose in this particular architecture
Three things about a retrieval path make the authorisation decision drift, and none of them are about the model.
The identity has further to travel. In a conventional request the handler that loads a record usually sits close to the authenticated session. In a retrieval path there is often a service between them: a query is embedded, a store is searched, results come back, and only then does the model see them. Each hop is a place where the user's identity can be dropped in favour of a service credential with broader access than the user should have. OWASP specifically identifies this use of generic, high-privileged identities as an Excessive Agency risk.
The filter is a parameter rather than a check. Permission-aware retrieval can be implemented by passing a tenant ID, classification or set of allowed document IDs into the search, but the security property depends on that scope being enforced by the trusted retrieval layer rather than simply supplied by the caller. A filter supplied by the caller is a different security property from a check performed by the resource. It works only if every caller supplies the correct scope and the retrieval layer fails safely when that scope is missing or invalid; otherwise, an omitted filter can become a broader search.
Ingestion and retrieval are written by different people at different times. Whatever labels the retrieval filter depends on were attached when the document was indexed. If a re-index, a migration or a new source populates that metadata differently, the filter still runs and still returns results, and nothing in the request looks wrong.
The common shape of all three is that the failure is silent. A broken tenant boundary in a retrieval path does not throw. It answers.
The choice OWASP declines to make for you
Because the guidance names the property and not the placement, it is worth being explicit about the two ways teams actually satisfy it, since they fail differently and the choice determines what you have to test.
Filtering at query time keeps one index and attaches a constraint to every search: this tenant, these document ids, this classification. It can avoid the operational overhead of maintaining separate indexes, but its security depends on the retrieval layer enforcing the constraint on every search. OWASP recommends enforcing tenant scoping inside the index query and using physically separated indexes for high-sensitivity workloads. Its weakness is that correctness lives in every call site. The index itself holds everything, so a caller that forgets the constraint, builds it from the wrong field, or hands over a service identity instead of the user's gets a broader search rather than an error. What you have to test is therefore every caller, not the store.
Separating the data gives each tenant its own index, collection or namespace, so a query issued in the wrong context has nothing to match. The boundary depends less on each caller supplying the correct filtering parameter, which can reduce the risk and blast radius of application-layer filtering mistakes. The main trade-offs are operational, but the partitioning and routing themselves still need to be secured: more indexes have to be provisioned and migrated, shared data becomes harder to handle, and changes to the partition scheme can require re-indexing.
Neither is wrong, and plenty of systems end up with both, physically separating the largest tenants and filtering within a shared index for the rest. The reason to decide it deliberately is that the two arrangements move the test surface: in the first, an authorisation test has to enumerate callers; in the second, it has to enumerate partitions and the routing between them.
What is worth avoiding is the arrangement where nobody can say which one is in force. If the answer to "how is this partitioned" is that a filter is passed in most places, that is query-time filtering with unknown coverage, and the unknown coverage is the finding.
How to test it
The useful property of this bug class is that many instances can be tested from outside with accounts representing different access scopes, because the thing you are checking is an answer that should not have been possible.
Start from the invariant, not the endpoint. Write down what must never happen in one sentence: a user in organisation A must never receive content derived from a document belonging to organisation B. That sentence is the test oracle, and it holds regardless of how the retrieval is implemented.
Ask as one tenant for something only the other has. Seed a document in tenant B with a distinctive string that appears nowhere else. Then, as a fully legitimate user of tenant A, ask questions designed to surface it. Ask directly, ask obliquely, ask for a summary of everything available on the topic, ask it to list its sources. You are not trying to trick the model. You are checking whether the retrieval reached across the boundary, and the distinctive string is how you know unambiguously rather than by interpretation.
Test the paths that skip the front door. The chat surface is usually the best-guarded route. The same retrieval often backs an API endpoint, a scheduled digest, an export, a webhook or an internal admin view, and those were added later with less scrutiny. Enumerate every caller of the retrieval service and ask which of them applies the user's identity and which supplies a filter of its own choosing.
Check what happens when the filter is absent or malformed. If a tenant filter is a parameter, find out what the store does when it is missing, empty, null, or a value that matches nothing. If the system fails open, a missing or invalid scope can result in an unfiltered search; over a shared index, that can create a cross-tenant data exposure.
Test after ingestion changes, not only after code changes. Because the filter depends on metadata written at index time, a change to how documents are ingested can break the boundary without touching the authorisation code at all. That is a case a code review alone may not catch, particularly when the metadata change is introduced through a separate ingestion or data pipeline.
Include the agentic paths if you have them. Where the feature can take actions rather than only answer, the same question applies to each tool it can call: does that tool re-check the requesting user's authority, or does it inherit the agent's. This is the ground OWASP's LLM03:2026 Excessive Agency covers, and it is worth reading alongside your own tool definitions.
Comparing the approaches
| Approach | What it reaches | Where it does not | Limitation to keep in mind |
|---|---|---|---|
| Prompt-injection and jailbreak testing | Model-layer manipulation, unsafe output, instruction override | Whether the retrieval returned data this user was never entitled to | A model that refuses politely can still have been handed another tenant's document |
| Model evaluations and guardrail suites | Output quality, refusal behaviour, regression on known prompts | Authorisation, because the harness usually runs as one identity | Passing an eval set says nothing about a boundary the eval never crossed |
| Working through the LLM Top 10 as a checklist | A genuinely broad map of model-layer and supply-chain risk | Enforcement placement and identity propagation, which it leaves to you by design | Completing the list is not the same as having tested your own boundary |
| SAST and dependency scanning | Known-pattern flaws and vulnerable packages in the surrounding code | A filter that is applied correctly in code but populated from wrong metadata | Cannot see whether the runtime data actually honours the intended partition |
| Exercising the running application as two tenants (what Borg does) | Whether a cross-boundary answer is actually reachable, with the steps to reproduce | Dedicated model-layer and embedding-specific risks, which are outside the scope of this particular test | Only covers the paths that were exercised, so absence of a finding is not proof of a boundary |
Where the model-layer work is the right thing to do instead
The case above is about one question, and a team that read this and dropped their model-layer work would be making a mistake.
Prompt injection is real, it is the first entry on OWASP's list for good reason, and nothing in an authorisation test addresses it. The same goes for improper output handling, where model output reaches a downstream component without sufficient validation, and for system prompt leakage. Those are distinct AI and application-security properties, and they need the appropriate testing and controls built for them.
The honest division is that model-layer testing asks whether the system can be made to behave badly, and authorisation testing asks whether a boundary you rely on actually holds. Both are worth doing, they fail differently, and a team doing only one of them has a gap either way. What does not work is treating a completed LLM risk checklist as proof that the tenant boundary actually holds, because guidance about the required controls is not the same thing as testing those controls against your running application.
Where this fits in a delivery process
Two practical notes, because a test that runs once is a snapshot.
The retrieval path changes more often than the authorisation code around it, so the trigger for re-testing should include relevant ingestion and indexing changes, rather than only changes to auth. A pull request that adds a document source is a pull request that can move the boundary, even though it touches nothing that looks like a permission check.
And the code-level and running-system views answer different halves. Reading the diff tells you whether a new caller of the retrieval service applies the user's identity; that is the question code-aware review on a pull request is shaped for. Whether the boundary actually holds in the deployed system, with the real index and the real metadata, is best established by exercising the system with different tenant identities and seeing what comes back. Neither substitutes for the other, and a finding from the second is the one that comes with proof.
What this does not solve
The limit of everything above is the same limit that applies to any testing: it covers the paths that were exercised. An authorisation test over the chat surface and two API routes says nothing about the export job nobody mentioned, and a boundary can hold on every path you tried and fail on the one you did not enumerate. So the enumeration is the load-bearing step, and it is worth redoing whenever a new consumer of the retrieval service ships.
The second limit is that this is a confidentiality question and not the whole of AI application security. Nothing here addresses whether the model is accurate, whether it can be made to produce harmful output, or whether the data it was trained or fine-tuned on was handled correctly. Those matter, and they are separate work with separate instruments.
Ofte stilte spørsmål
- Does working through the OWASP LLM Top 10 cover the tenant boundary in our retrieval path?
- The current list addresses this under LLM09:2026 Vector and Embedding Weaknesses, including cross-tenant leakage through shared similarity search. OWASP recommends enforcing tenant scoping inside the index query rather than applying it after retrieval, while the exact way a user's identity is propagated through your application remains an architectural decision. Those two decisions belong to whoever knows the system, which is appropriate for a risk taxonomy and also means the checklist can be completed honestly while the boundary itself has never been exercised. Treat the list as guidance on the relevant risks and controls, but don't mistake working through the checklist for evidence that your particular tenant boundary has been exercised against the running system.
- Where should the permission check sit in a retrieval pipeline?
- Two common approaches are to constrain queries against a shared index or to isolate each tenant's data, and each changes where the security dependency sits. A shared index can avoid the overhead of separate storage, but the security property depends on the retrieval layer consistently enforcing the tenant constraint; if that scope is missing or incorrectly applied, the search can become broader than intended. Giving each tenant its own index or namespace reduces the dependency on query-time filtering, because a correctly isolated index has no other tenant's documents to return, at the cost of provisioning, migration and awkward handling of genuinely shared material. Mixed arrangements are common. The position worth avoiding is not being able to say which one is actually in force.
- How do you prove a cross-tenant read actually happened rather than suspecting it?
- Plant something unmistakable. Put a string in one tenant's documents that exists nowhere else in the system, then ask as a legitimate user of a different tenant and see whether it comes back. That turns a judgement call about whether an answer looked too well-informed into a fact anyone can re-run, which matters because the failure here is silent: nothing errors, the system simply answers. It also gives the engineer receiving the report something to reproduce rather than a description to argue about.
- Will prompt-injection testing find this class of flaw?
- It addresses a different failure and both are worth doing. Injection work asks whether the model can be induced to behave in ways it should not, which is a real risk and the first entry on OWASP's list. An authorisation flaw does not require manipulation of the model: a well-behaved model that politely answers a reasonable question can still have been handed a document belonging to someone else, because the mistake happened before the model saw anything. So a clean injection result tells you nothing about the boundary, and a held boundary tells you nothing about injection.
- What should trigger a re-test of the retrieval boundary?
- Changes to ingestion as well as changes to authorisation code, because the retrieval boundary can depend on metadata established before a query is made. Where a query-time constraint depends on metadata attached at index time, a new document source or a migration that populates that metadata differently can weaken or break the boundary without touching a single permission check. Any new consumer of the retrieval service belongs on the same list, because the surfaces added after the main one tend to get less scrutiny and often carry their own identity rather than the user's.



