By late 2025 the security community stopped treating indirect prompt injection as merely theoretical. After two years as tidy lab demonstrations, attacks began hitting production systems: the OWASP Top 10 for LLM applications now ranks prompt injection as the number-one risk, NIST called indirect injection "generative AI’s greatest security flaw," and academic researchers demonstrated that a single poisoned email could coerce a model into exfiltrating SSH keys in up to 80% of trials with zero user interaction.
These attacks require no malicious binary, no phishing click, and no anomalous login. The agent simply reads attacker-written content and takes action, exactly as designed.
A clarifying incident: ForcedLeak (Salesforce Agentforce)
In September 2025 researchers at Noma disclosed a critical vulnerability chain (CVSS 9.4) in Salesforce’s Agentforce platform. An attacker embedded malicious instructions in the description field of an ordinary Web-to-Lead form. The text sat harmlessly in the CRM until an employee later asked the AI agent to process that lead; at that point the agent executed both the legitimate query and the hidden payload, exfiltrating sensitive CRM data to an external server. Alarmingly, the exfiltration destination was a domain that remained on Salesforce’s trusted allowlist but had expired — the researchers re-registered it for about five dollars. All security controls saw legitimate traffic to a trusted domain; nothing looked wrong.
Why the input layer is the wrong perimeter
Many enterprise AI teams protect agents by sanitizing inputs: hardening system prompts, running classifiers over incoming content, and using training-time defenses such as instruction hierarchies and reinforcement learning to make models resistant to injection. Those are worthwhile, but they all share a blind spot: they try to stop the model from being fooled.
Prompt injection is not a bug a future model will simply lack; it’s a structural property of how language models work. At inference the model consumes a single undifferentiated stream of tokens: system instructions, retrieved documents, tool outputs, and freshly fetched web pages collapse into one context. There is no hardware-enforced boundary between "trusted instruction" and "untrusted data" like there is between kernel and user space.
When an agent becomes agentic — retrieving from the open web, reading email, querying databases, calling APIs — the attack surface explodes: any of those sources can carry an instruction. Researchers studying real agent ecosystems have already found hundreds of malicious third-party extensions performing data exfiltration and silent injection without user awareness. These are production realities, not lab curiosities.
If you cannot guarantee the model will never be fooled — and you can’t — then an architecture that assumes it won’t is fragile. Distributed systems engineers have long relied on a second principle:
Verify, then trust
The principle is: an agent’s proposed action must be validated against an external, deterministic policy before execution, regardless of why the agent proposed it. The validator does not try to determine whether the instruction that produced the action was legitimate or to detect the injection. It asks a narrower, decidable question: is this action, on its face, permitted?
This changes the attack surface. Detecting a cleverly disguised malicious instruction is open-ended; checking whether a wire transfer exceeds a fixed dollar limit is closed and answerable. Move the security decision from where an attacker has infinite freedom to where they have almost none.
Crucially, the check must be deterministic code, not another LLM asked to "judge" danger. Invoking a second LLM reintroduces the exact vulnerability one layer down. The enforcement layer should be conventional, boring, auditable software.
In practice this means the agent’s output must be structured. For example, an agent managing procurement proposes approve_invoice(vendor='Acme', amount=1200000). A runtime contract evaluates the call and rejects it when it violates policy (e.g., max_amount_usd = 50,000), discards the action, notifies a human, and prevents any API call. The injected instruction at 2:14 a.m. becomes irrelevant because the deterministic gate stopped the transfer.
Structure is a precondition because if the agent emits prose — "please approve the Acme invoice" — parsing that prose requires another LLM, reintroducing indeterminacy. Therefore, consequential actions must cross the boundary as typed tool calls, never as free text. When input is unavoidably natural language (an email that says "Wire them their balance"), let the model extract a structured value but do not let that extraction self-authorize the action: the gate must check the proposed amount against the system of record and policy, not against the number asserted by the email. Extraction is probabilistic; validation stays deterministic.
Some judgments are inherently non-parametric (e.g., "Is this email phishing?"). There the model remains in the loop, but you bound consequences with reversibility and human review above a defendable threshold. Contracts protect parameterizable actions; unparameterizable judgments fall back to containment.
Architectural commitments implied by the action layer
Accepting that security lives at the action layer implies three commitments familiar to hardened distributed systems:
-
Least privilege for agents, scoped to the action rather than the agent. Rather than granting an agent a standing credential that can move money, require the agent to request narrow, transient elevation per action that the deterministic gate approves. The dangerous credential should exist only for the instant the action is permitted and then be revoked. Note: gating execution of code the agent writes is separate — execution still needs sandboxing, containment, and egress controls.
-
Zero trust for machine identities. Authenticate and authorize every agent action as if it came from an untrusted actor; internal origin is not grounds for implicit trust.
-
Capability contracts at the boundary. Every consequential action passes through a deterministic gate encoding what is allowed: dollar limits, rate limits, allowlisted destinations, and mandatory human review thresholds. Contracts must be version-controlled, auditable, and live entirely outside the model.
The trap of normalized deviance
A quieter organizational danger is the slow accumulation of false confidence from connecting insecure agents to real systems and observing no immediate harm. Researchers have warned about indirect injection for years, but many deployments have "gotten away with it" for a while. Each uneventful day normalizes the risk until eventually a catastrophic failure occurs. Teams that will survive the coming wave of incidents are not those with the cleverest input filters; they are the ones who assumed compromise from the start and built the boring enforcement layer that stops irreversible actions.
Practical starting points
You do not need to rearchitect everything overnight. Start by inventorying agent actions and sorting them by blast radius: what is the worst outcome if this action fires erroneously? For each high-blast-radius action, write a deterministic contract to gate it, and require human review above a defensible threshold. Only after this, continue hardening inputs.
Prompt injection cannot be solved at the input layer alone, but it can be made survivable at the action layer, where deterministic code gets the final word. The model’s job is to be useful; your architecture’s job is to ensure that when the model fails — or is turned against you — the failure stops at the gate.



