Guide · Implement

How to defend an agent against prompt injection

There is no reliable way to make a model distinguish instructions from data, because both arrive as text in the same context. So the goal is not prevention. It is making a successful injection not matter very much.

before you start

  • A list of every untrusted input path into the agent's context.
  • The agent's actual credential scopes, not the intended ones.
  • Acceptance that you are bounding blast radius, not eliminating a vulnerability.

Why the obvious approach does not work

Prompt injection persists because it is not a bug in an implementation — it is a property of how instruction-following models work. Everything in the context window is text, and the model has no trustworthy channel that marks some text as instruction and the rest as inert data. Filters, delimiters and 'ignore any instructions in the following content' prompts all raise the cost of an attack without closing the class, and treating any of them as a fix is the mistake that leads to over-broad agent permissions.

Once you accept that, the engineering problem becomes tractable and familiar: it is a blast-radius problem. An agent that can only read public data and produce text is a nuisance when injected. The same injection against an agent holding a production database credential and a shell is an incident. The variable you control is not the model's judgement — it is what the agent can do when its judgement has been subverted.

Agents make this sharper than chatbots for two reasons. They pull untrusted content continuously and without a human reading it first — a fetched web page, an issue comment, a code comment, a tool result, an MCP tool description — so the injection surface is wide and unattended. And they act on conclusions autonomously, so there is often no human between the injected instruction and the write it causes.

The lethal trifecta is a useful shorthand for triage: access to private data, exposure to untrusted content, and a way to communicate externally. An agent with all three can be made to read your secrets and send them out. Break any one leg and the exfiltration path closes, and separating those legs is usually cheaper and more reliable than trying to filter the input.

The procedure

  1. Enumerate every untrusted path into context

    List everything that can put text in front of the model without a human reading it first: fetched pages, file contents, tool results, retrieved documents, issue and PR comments, code comments, email, calendar entries, and MCP tool descriptions. Teams routinely miss the last one — a tool description is attacker-controlled text loaded at connection time, before any user input exists. You cannot bound what you have not listed.

    you now haveA complete list of untrusted input paths, including tool metadata.

  2. Break the lethal trifecta wherever you can

    For each agent, check whether it simultaneously has private data access, untrusted content exposure, and an external communication channel. Remove one leg. Usually the cheapest to remove is egress: an agent that cannot make arbitrary outbound requests cannot exfiltrate what it reads, no matter how thoroughly it is persuaded. This single control buys more than every input filter combined.

    you now havePer-agent trifecta assessment and a named leg removed for each.

  3. Scope credentials to the task, not to the user

    Agents inherit developer credentials by default, which is how a code assistant ends up able to delete a production bucket. Issue dedicated service identities with the narrowest scope the task needs, and separate read from write into different identities where the agent needs both. The question to answer for each credential is what a single subverted invocation could do with it.

    you now haveDedicated agent identities with least-privilege scopes, and read/write split where possible.

  4. Put a human in front of irreversible actions

    Classify the agent's tools by reversibility, then require confirmation on the irreversible ones: deploys, deletions, payments, outbound customer contact, credential changes. Confirmation prompts have a real cost — approve them enough times and people stop reading — so spend them only where undo does not exist. Rate limits and quotas serve the same purpose without the attention cost.

    you now haveA reversibility classification with confirmation gates on the irreversible set.

  5. Review the artefacts that shape behaviour

    Injection does not only arrive at runtime. A poisoned MCP tool description, a skill that tells the agent to ignore a check, or a rules file with an appended instruction are all persistent injections that load every session. This is the highest-yield unglamorous control, because a runtime injection affects one session while a poisoned artefact affects every session on every machine that has it.

    you now haveReviewed and pinned artefacts, with tool descriptions specifically read as untrusted input.

  6. Log tool calls so you can reconstruct a session

    Record what the agent did — every tool call with its arguments, the content it retrieved, and which artefact versions were loaded. You will not detect most injections live; you will find out afterwards, and then the only question that matters is what happened and how far it reached. An agent whose actions cannot be reconstructed cannot be investigated.

    you now haveTool-call and retrieval logs sufficient to replay a session after the fact.

Injection blast-radius worksheet

One per agent configuration. If the trifecta line is three yeses, stop and fix that before anything else on the sheet.

Injection blast-radius worksheet

AGENT / CONFIGURATION: ____________________   OWNER: ________   DATE: ________

THE TRIFECTA
   Access to private data?              [ yes | no ]   what: ______________
   Exposed to untrusted content?        [ yes | no ]   from: ______________
   Can communicate externally?          [ yes | no ]   how:  ______________
   >> Three yeses = exfiltration path. Which leg are you removing? ___________

UNTRUSTED INPUT PATHS  (tick every one that applies)
   [ ] fetched web content        [ ] file contents
   [ ] tool / API results         [ ] retrieved documents
   [ ] issue + PR comments        [ ] code comments
   [ ] email / calendar           [ ] MCP tool descriptions   <- frequently missed

CREDENTIALS
   Identity used:            [ dedicated service | a person's own ]
   Scopes held: ____________________________________________
   Minimum scopes that work: _______________________________
   Read and write split into separate identities?   [ yes | no ]

IRREVERSIBLE ACTIONS AVAILABLE
   [ ] deploy    [ ] delete data    [ ] payments    [ ] customer contact
   [ ] credential or permission changes    [ ] force-push / history rewrite
   Confirmation gate on each of the above?          [ yes | no ]
   Rate limit or quota in place?                    [ yes | no ]

PERSISTENT INJECTION SURFACE
   MCP tool descriptions read as untrusted input?   [ yes | no ]
   Skills and rules files reviewed?                 [ yes | no ]
   All artefacts version-pinned?                    [ yes | no ]

RECONSTRUCTABILITY
   Tool calls logged with arguments?                [ yes | no ]
   Retrieved content logged?                        [ yes | no ]
   Artefact versions per session recorded?          [ yes | no ]

WORST SINGLE SUBVERTED INVOCATION (write it out): ________________________

Where this goes wrong

  • Treating a filter or a guard prompt as a fix

    They raise attack cost and are worth having, but they do not close the class. The real damage comes from believing they did and then granting the agent permissions you would not otherwise have granted.

  • Forgetting that tool descriptions are untrusted input

    They load before any user input and persist for the whole session. A poisoned description is an injection that arrives through your configuration rather than your traffic, and input filtering never sees it.

  • Letting the agent run as the developer

    Convenient and the single largest amplifier of blast radius. The agent gets SSH keys, cloud profile and repository write access it has no need for, and every one of those is available to an injection.

  • Confirmation prompts on everything

    Attention is finite. Prompt on every action and approvals become reflexive, which removes the protection precisely where you needed it. Reserve gates for the irreversible set.

Doing this without us

Every control here is configuration and engineering you own — credential scoping and egress restriction in particular are among the highest-value security work available right now and need nothing from us. Where a control plane helps is the persistent surface in step five: reviewing and pinning artefacts across every surface and machine, so a poisoned tool description or skill is caught once rather than per laptop. Vincosha Assay scans that layer; Registry pins it; Ledger records which versions were live in a given session.

Frequently asked

Is prompt injection actually solvable?
Not as a class, on current architectures — instructions and data share one channel and the model has no trustworthy way to tell them apart. Individual attacks get mitigated and defences genuinely improve, but a design that assumes injection will eventually succeed and bounds the consequence is the only approach that holds up.
Does a system prompt telling the model to ignore injected instructions help?
Marginally, and it is worth including. It is itself just text in the same context the attacker is writing into, so a sufficiently well-crafted injection overrides it. Treat it as raising cost, never as a control you can rely on when deciding permissions.
What single control buys the most?
Removing the egress leg of the trifecta, in most architectures. An agent that cannot make arbitrary outbound requests cannot exfiltrate what it reads however thoroughly it is persuaded — and unlike filtering, it does not depend on recognising the attack.
How does this differ for a coding agent versus a chat assistant?
Coding agents are strictly worse on both axes: they ingest untrusted content continuously and unattended (repositories, issues, fetched docs, tool output) and they hold local execution with developer credentials. The controls are the same; the urgency of credential scoping is much higher.

Sources

Checked on 2026-07-25. Where a vendor control is changing quickly this guide describes the mechanism rather than asserting a current state — verify against the vendor's own documentation, and tell us if we have drifted.

Do these next

Concepts on this page

The same procedure, running before the artifact lands

Vincosha Assay performs this review automatically on everything your AI surfaces load and quarantines what fails; Vincosha Registry pins the approved version so what you reviewed is provably what runs. Neither replaces the judgement above — they remove the part where you do it by hand, on every machine, forever.