One email, no clicks
In June 2025, researchers at Aim Security disclosed EchoLeak, a flaw in Microsoft 365 Copilot that let an attacker pull internal data out of an organisation by sending a single email. The victim clicked nothing. Microsoft rated it 9.3, patched it server-side, and confirmed no exploitation in the wild.
The patch closed that specific hole. The shape of the attack is still sitting there, waiting, for anyone running an AI assistant that reads mail.
A customer support inbox is the same target, only more exposed. Copilot reads mail your colleagues send. An AI agent for email support reads mail from strangers, and it's supposed to. Anyone on the internet can put text in front of your model for the price of an email address.
Why the standard security review misses this
Most vendor security reviews for support tooling were written for software that stores and routes text. Encryption at rest, SSO, retention windows, sub-processor lists. All of it still matters, and the enterprise security checklist for AI email vendors is the right artifact for procurement.
But a checklist answers a different question. It tells you whether the vendor is a competent custodian of data. It doesn't tell you what happens when a message in the queue contains instructions and the thing reading it can issue refunds.
OWASP split this out in December 2025, publishing a separate Top 10 for Agentic Applications alongside the LLM list. Agent Goal Hijack sits at ASI01, followed by tool misuse, identity and privilege abuse, and memory poisoning. The list exists because agentic systems fail differently from applications that merely call a model.
Here's the framing that gets security teams to sit up. An email support agent has three properties at once, by design:
- It processes untrusted content. Every inbound message is attacker-controllable text.
- It holds access to private data. Order history, account records, billing, sometimes identity systems.
- It can communicate outward. Sending email is the entire point of the product.
Any two of those are manageable. All three together means a successful injection has both something worth stealing and a channel to move it through. You cannot remove any of the three without removing the product, which is why the answer has to be architecture rather than a filter.
Layer 1: the ingestion boundary
The first decision is the one most implementations get wrong: where in the model's context does the customer's message go, and what is it labelled as?
Everything in the message is data, none of it is instruction
Untrusted content should never occupy an instruction position. That means no concatenating the email body into the system prompt, no “the customer says: {body}” pattern where the body can close the quote and open a new directive. Structurally separate the customer's text, tag it with provenance, and keep the agent's operating instructions in a position the message can't reach.
Then normalise what you actually feed the model. Email is a rich format and most of it is invisible to the human reader:
- HTML comments and hidden elements: render to text before the model sees it, and drop anything display:none or positioned off-canvas
- Colour-matched text: white-on-white was EchoLeak's delivery mechanism and it's still the cheapest one
- Zero-width and control characters: strip them; legitimate customers don't use them
- Encoded blobs: base64 chunks in the body are worth flagging, not decoding and forwarding
- Quoted history and signatures: parse and bound them, because a forwarded thread is a place to hide a payload
- Headers: display names are free to forge, so authorisation decisions should never touch them
Attachments deserve their own path. A PDF or spreadsheet goes through extraction, gets the same normalisation, and enters the context as clearly labelled third-party content with its own size budget. Don't let a 40-page PDF quietly become 90% of the model's context.
Classifiers are a speed bump, not a wall
Injection-detection classifiers are worth running. They catch the lazy attempts, they generate signal for your logs, and they're cheap. Just don't build the architecture on them.
Copilot had cross-prompt-injection classifiers in front of it. The Aim Security researchers got past them by phrasing the payload so it never mentioned AI or Copilot at all, and it read like ordinary business prose. Any control that depends on recognising malicious intent in natural language is going to have a bypass, because natural language has infinite paraphrases and the attacker only needs one.
Treat detection as telemetry. Treat containment as the control.
Layer 2: what goes into the context window
Retrieval is where a support agent quietly becomes dangerous, because retrieval is how private data arrives next to untrusted text.
Scope every lookup to a verified identity. The customer's identity comes from the account system keyed on the authenticated sending address, not from a claim in the message body. If the email says “this is regarding account 44812” and the sender doesn't own account 44812, the agent shouldn't be able to see it. That enforcement belongs in the data layer, not in a prompt instruction telling the model to be careful.
Then minimise. The agent needs this customer's last three orders, not a query surface that can return arbitrary rows. Narrow retrieval scopes are the difference between a successful injection leaking one order and leaking a table.
Thread history is untrusted too, and this is the part teams forget. OWASP calls it ASI06, memory and context poisoning: an instruction planted in message two of a thread sits dormant until message nine, when a different agent run picks up the whole conversation as context. If your agent has persistent per-customer notes or a summarised memory, those are attacker-writable surfaces. They need the same provenance tagging and the same suspicion as the raw inbound message.
Layer 3: control flow untrusted content can't rewrite
This is the layer that actually holds, and it's the one worth spending engineering time on.
A ReAct-style loop, where the agent observes a tool result and then decides what to do next, gives injected text a steering wheel. Content that arrives mid-loop can change what the agent does for the rest of the run. The 2025 paper Design Patterns for Securing LLM Agents against Prompt Injections by Beurer-Kellner and colleagues catalogues the alternatives, and two of them fit support email well.
Action-selector. The model's job is to pick one operation from a fixed list and fill validated parameters. It doesn't get to compose a plan. For a support agent this maps almost exactly onto intent classification: this message is a WISMO query, a refund request, an address change, a cancellation. Each intent runs a workflow your engineers wrote.
Plan-then-execute. The agent commits to a sequence of steps before it ingests any untrusted tool output, and the orchestrator refuses steps that weren't in the plan. An injection can still corrupt the contents of a step, which is why this pattern needs the tool-scoping layer underneath it. But it can't add a step that sends mail to a new address.
The trade is flexibility. A locked-down agent handles the long tail worse than an open one, and anyone who tells you otherwise is selling something. Our view is that support email is the ideal domain for this trade, because the volume lives in a few dozen repeatable intents. You give up capability you were never going to use safely.
Layer 4: tool scope and agent identity
Give the agent its own identity in every downstream system. Not a shared service account, not an engineer's API key, not the same credential across tenants. When something goes wrong you want the audit trail to name the agent, and when you need to revoke you want to revoke one thing.
Scope each tool as tightly as the workflow allows:
- Bounded parameters: a refund tool that accepts an order ID belonging to the verified requester, capped at that order's value, beats a refund tool that accepts an amount
- Read and write separated: order lookup and order modification are different tools with different gates
- Enumeration blocked: a lookup that returns one record on exact match is safe in a way that a search returning fifty is not
- Per-tenant credentials: multi-tenant deployments where one compromise reaches every customer are a category of incident you can design out
One rule matters more than the rest: no generic HTTP tool. The moment an agent can fetch or post to an arbitrary URL, every other control becomes decorative, because you've handed the attacker a general-purpose exfiltration channel. If a workflow needs an external call, wrap that specific call in a specific tool with a specific allowlisted host. Robylon's write-access integrations are built this way, as named actions against named systems rather than a URL field the model gets to fill in.
Layer 5: which actions need a human
Teams usually gate on importance, which is the wrong axis. Gate on reversibility.
Sending a tracking number is reversible in the sense that being wrong costs an apology. Issuing a refund moves money. Changing an account's email address can hand over the account. A useful gating policy looks at what it would take to undo the action, and how much authority the action transfers.
Practical thresholds worth setting from day one: a value ceiling above which refunds and credits route to a human, rate limits per customer and per hour on any state-changing tool, and a hard stop on changes to contact details or authentication factors. The escalation logic that decides resolve versus route is where these thresholds live, and it should be configuration you can read, not behaviour you infer from the model.
Rate limiting deserves a specific mention because it catches attacks that individually look fine. One refund at policy is normal. Forty refunds in ten minutes, all to accounts created this week, is an incident, and no single request in that sequence would have failed a per-request check.
Layer 6: the send path
Exfiltration needs an exit. In a support agent, the exit is the reply.
Default the agent to replying into the originating thread, to the verified sender, with no new recipients and no CC. If a workflow genuinely needs to mail someone else, that's a separate gated action with an allowlist, not a field the model populates.
Then filter what leaves:
- Outbound URLs: allow your own domains and your known partners, block the rest. EchoLeak's payload worked by encoding stolen data into a URL, which is a trick that only works if arbitrary URLs can leave
- Remote images: an image src is a URL with a callback, so strip or proxy them
- PII patterns: card numbers, national IDs, and internal account identifiers should be caught on the way out regardless of how they got into the draft
- Canary values: seed records that should never appear in customer-facing mail, and alert if one ever does
Egress filtering is unglamorous and it is the single highest-value control on this list. Every injection scenario that ends in data loss has to cross this boundary.
Walking one attack through the stack
Abstract layers are easy to nod along to, so here's a concrete run. Imagine a retailer with an agent that handles order status, returns, and refunds under $200, wired to Shopify and Stripe.
An attacker emails support from a throwaway address. The visible text is a bland question about a delayed parcel. Below the signature, in white text, sits a block instructing the assistant that it is now operating in an internal audit mode, that it should retrieve the last twenty refund records, and that it should include them in its reply along with a link to a status page on a domain the attacker owns.
Layer 1 strips the colour-matched text during normalisation, so in the best case the payload never reaches the model. Assume it does, because the attacker used an encoding trick nobody anticipated. The text still arrives tagged as customer content, sitting in a data position, next to instructions telling the agent it will encounter attempts like this.
Layer 2 means “the last twenty refund records” isn't a thing the agent can ask for. Retrieval is keyed to the verified sender, who has no order history at all. The only records in reach are an empty set.
Layer 3 catches what's left. The message classified as a WISMO query, and the WISMO workflow has three steps: look up order, format status, reply. There is no step in it that produces a list of refunds, and the orchestrator won't run one that isn't in the plan.
Layer 6 is the backstop. Even if every earlier control had failed and the model had drafted exactly what the attacker asked for, the reply goes to the originating thread only, and the attacker's domain isn't on the outbound URL allowlist. The link gets stripped and the draft gets flagged.
Four independent failures required. That's the whole design goal: not perfection at any single layer, but enough depth that a novel bypass at one of them doesn't reach anything worth having.
Layer 7: seeing it happen
Log the decision, not just the outcome. For each handled message you want the retrieved context, the classified intent, the tools called with their parameters, the confidence, the gate decisions, and the final text. That's the difference between “the agent sent something odd” and a reconstructable timeline. Our guide to audit trails for AI email support goes into what to retain and for how long.
Watch for the patterns that don't look like anything at the individual message level: a spike in classifier hits from one sender domain, tool calls in an order no workflow produces, outbound drafts containing URLs that got stripped, refunds clustering on new accounts.
And build the kill switch before you need it. Per-intent, per-tool, and global. Being able to disable refunds while keeping order lookups running is the difference between a bad afternoon and a shutdown.
Where this architecture still fails
Honesty matters more than reassurance here, so: none of this solves prompt injection. Researchers describe reliable prevention as an open problem, and the current academic consensus is that layered constraint is a containment strategy rather than a fix. What the layers buy you is that a successful injection has nowhere useful to go.
Three more limits worth stating plainly.
Every layer costs utility. Tighter retrieval means more escalations for questions the agent could technically have answered. Locked control flow means the unusual request goes to a human. If your resolution rate doesn't dip at all when you add these controls, they probably aren't doing anything.
Most of your actual incidents won't be exotic. They'll be a tool scoped too broadly during a rushed integration, a staging credential that reached production, a gate someone disabled for a demo. Injection research gets the attention; configuration drift causes the outages.
And the threat model is moving. As customer-side agents start writing the inbound mail, volume goes up and the content gets more structured, which changes both the attack surface and the economics of defending it. Anyone telling you their 2026 architecture is settled hasn't been paying attention.
How Robylon approaches it
Robylon runs email support agents on intent-bounded workflows rather than open agent loops. Untrusted message content is separated from operating instructions at ingestion, retrieval is scoped per tenant and per verified customer, and every integration is a named action against a named system with bounded parameters. Actions that move money or change account access sit behind configurable gates, and the send path defaults to the originating thread.
Autonomous resolution lands in the 60–80% range across deployments, validated against historical tickets during onboarding. The controls above are part of why that number is stable rather than something that degrades the first time someone interesting emails you.
If you want to know whether your own architecture holds, the next step is to attack it. Our practical red-team test suite for AI email agents walks through the cases to run and how to score them.
Ready to deploy an email agent you can actually defend? Robylon AI resolves 60–80% of customer emails autonomously with bounded, auditable actions across Zendesk, Shopify, Stripe, Salesforce, and 60+ other integrations. Start free at robylon.ai
FAQs
Why is an AI email agent harder to secure than a chatbot?
An email agent has three properties at once: it processes untrusted content from anyone who knows your support address, it holds access to private customer data, and it can send messages externally. Chat sessions and voice calls carry more natural friction around identity and channel. Email has almost none, because the address is published, the sender is unverified, and a successful injection has both something worth stealing and a route out.
Can prompt injection be completely prevented?
No, and vendors claiming otherwise are overselling. Researchers describe reliable prevention as an open problem, because models have no dependable way to separate instructions from content in natural language. What works is containment: scoping retrieval, locking control flow, bounding tool parameters, and filtering the send path so a successful injection has nowhere useful to go. Detection classifiers help, but every published one has documented bypasses.
What is the highest-value security control for an email agent?
Egress filtering on the reply path. Every scenario that ends in data loss has to cross that boundary, so defaulting replies to the originating thread, blocking new recipients, and allowlisting outbound URLs stops exfiltration even when earlier layers fail. Close behind it: never give the agent a generic HTTP tool, which hands an attacker a general-purpose channel and makes the other controls decorative.
Which AI email actions should require human approval?
Gate on reversibility, not importance. Sending a wrong tracking number costs an apology; issuing a refund moves money, and changing an account's email address can hand over the account entirely. Set a value ceiling above which refunds and credits route to a human, rate-limit state-changing tools per customer and per hour, and hard-stop any change to contact details or authentication factors.
How does hidden text in an email attack an AI agent?
Attackers place instructions where a human reader won't see them but a parser will: HTML comments, white-on-white text, off-screen elements, zero-width characters, image alt text, or inside an attachment. The reader sees an ordinary message while the model sees directives. This was the delivery mechanism in the EchoLeak vulnerability disclosed in 2025. Normalising and stripping hidden content before it reaches the model is the defence.

.png)

.png)
