Start by drawing the trust boundary
Before testing anything, write down every path by which text reaches the model's context. Typically: the user's message, retrieved documents, tool and API results, system prompt, conversation history, and any file the application parses.
Everything on that list except the system prompt is untrusted. Teams routinely miss tool results and retrieved documents, which is why indirect prompt injection keeps working: the attack arrives through a channel nobody classified as input.
Prompt injection, direct and indirect
Direct injection is the user typing an instruction to override behaviour. It is the easier case and the less interesting one, because the attacker only reaches their own session.
Indirect injection is the instruction planted in content the application will fetch later: a web page the agent summarizes, a resume it screens, a ticket it triages, a repository it reads. Here the attacker reaches whatever the agent can reach on behalf of someone else, which is the version that produces real incidents.
The real severity lives in the tool list
A model that can only emit text has a bounded worst case. A model wired to tools has a worst case equal to the union of what those tools can do.
So the test enumerates tools and the authority behind each one. An injected instruction that makes the model say something rude is a content issue. The same instruction making it call a tool that sends mail, moves money, or reads another tenant's records is a security incident, and the difference is entirely in the permission model.
- Every tool, function and MCP server the model can invoke
- The credential behind each one, and the scope that credential actually carries
- Which tool calls are irreversible or externally visible
- Whether any human approval sits between the model's decision and the effect
- Whether tool calls are logged with enough detail to reconstruct an incident
Output handling is a classic bug in new clothing
Model output is untrusted input to whatever consumes it. If the response is rendered as HTML you have a cross-site scripting path; if it is passed to a shell, a command injection path; if it is interpolated into SQL, an injection path.
This one is reassuring, because the fix is the fix you already know. Escape on output, parameterize queries, never hand model output to an interpreter. The novelty is only that the untrusted string arrived from your own model.
What a finding looks like
A useful finding names the injection path, the tool that fired, and the data or action reached. "The model can be jailbroken" is not actionable. "Content in a fetched page caused the agent to call this tool with these arguments, returning this record" is a ticket with an owner.
CyberArc's AI and agent security service covers this work, and the supporting tools are public: agent-shield for prompt-injection detection and agent-permission-graph for mapping what agents and MCP servers can touch.

