Skip to main content
An agent reads a document. The document contains a sentence addressed to the agent. The agent does what the sentence says. That is the whole attack, and it keeps working because language models have no channel separation. Your system prompt, the user’s request and the retrieved document are the same kind of thing by the time they reach the weights.

The question that has an answer

Do not try to decide whether a document is malicious. You cannot do it reliably, and every hour spent there is an hour not spent on the question that is answerable:
Which parts of this trajectory came from somewhere we do not control, and does the action about to happen require that they had not?
Provenance is a fact about your architecture, not a judgement about content. You know whether a document arrived from a customer upload or from your own curated store.

Marking it

Retrieval and tool steps carry a trusted flag, and it defaults to false. The default matters: an integration that forgets to think about provenance records the safe answer rather than the convenient one.

Taint propagates forward

If step 0 read an unverified document, step 4 is downstream of unverified content whether or not it looks like it. Once that is tracked, a policy becomes expressible without detecting anything:
This agent may summarise a document containing untrusted content. It may not initiate a payment in the same trajectory.
A grant sets that requirement in conditions. When the action arrives, gate 5 checks it and returns gated if the trajectory is not clean.

Not every action needs a clean context

Treating all taint as fatal is the mistake in the other direction. Reading a customer’s email in order to answer it is the job, and refusing on those grounds is refusing to work. The requirement belongs on the action, not on the agent. Summarising is fine. Moving money, changing configuration, disclosing data, closing a case without review: those are where the question deserves an answer before rather than after.

Discharging taint

Where a sanitisation step genuinely removes the risk, record it:
This is a claim you are making, and it is recorded as yours. It does not launder the provenance; it states that a named control ran, so a reviewer can judge whether it was sufficient.

What the record says

Three fields, and the third is the one teams forget:
  • which content was untrusted, by source, not a boolean on the trajectory
  • where it entered, by step index
  • whether the decision downstream of it was allowed to stand
The third is the list you review when something turns out to have gone wrong. Here is a real one from the demo: