Link copied

Introduction

When we started seriously hardening LuumenAI, the question I kept turning over was some version of “how am I going to secure this thing.” For a while I treated it like a normal hardening problem and went looking for the control I had missed. There mostly is not one. You cannot secure a language model the way that question assumes, and the sooner that sinks in, the sooner you can work on the parts that are actually fixable.

LuumenAI is the intelligence behind our observability and automation platform for SAP and ERP environments. It is a copilot for the engineers who run those systems. It reads alerts, pulls documentation, looks at incident history, and it can interact with terminals and run commands to help carry out a fix. Doing that means reading a lot of text that originated somewhere we do not control, like monitoring output, ticket descriptions, log lines, and knowledge base articles. That is normal for this kind of product, and it is also where the trouble starts.

A language model does not have a reliable way to tell where its instructions end and outside text begins. It reads the operator’s prompt, the user’s request, and a pasted log line as one continuous stream, and it weighs them all as input. If an attacker can get text in front of the model, that text carries roughly the same authority as anything else in the window. This is not a bug we can file and fix. It is how the models work right now. You control what the model is allowed to read, and you control what it is allowed to do once it has read it. This first paper covers keeping bad data away from the model and limiting its options before and during a run. A second paper covers what happens after the model acts, where containment and sandboxing come in. That side of the problem is the less settled one across the industry, and it gets its own paper.

01 Part I: Why AISecurity Is Different

A single email that steals your file

In June 2025, researchers at Aim Labs disclosed a flaw in Microsoft 365 Copilot that they called EchoLeak (CVE-2025-32711, rated CVSS 9.3).

The attack was an email. The target did not click a link, open an attachment, or reply. The email carried instructions written for the AI rather than for the person. Later, when Copilot did its ordinary job and pulled that email into context to help the user, it read the attacker’s instructions and acted on them, gathering sensitive content and sending it back out through a path that looked legitimate. Microsoft had real defenses in the way of this, including a classifier meant to catch injected prompts, link redaction, and a content security policy, and the attack got past all of them. Aim Labs called the underlying failure an “LLM Scope Violation,” meaning the model reached past what it was ever supposed to touch because a piece of text told it to.

Microsoft fixed it on their side, and customers had nothing to do. That fix closed this one instance and did nothing about the pattern behind it, which carries over to any assistant that reads untrusted content and can act on it. Today that is most of them, ours included.

The same shape fits LuumenAI without much translation. Picture an engineer who asks the copilot to triage a monitoring problem. To do that, the copilot reads the alert the way it reads any alert, the title, the description, the affected systems, and whatever notes have piled up on the linked incident. Now suppose one of those fields is not an ordinary alert. Suppose the description of a monitoring problem in a tool like Dynatrace, the body of a ServiceNow incident, or a comment someone pasted onto a ticket contains a few sentences written for the model rather than for the engineer, telling it to pull a set of records, run a particular command, or quietly note something in its own memory for later. None of that text came from us or from the engineer. It arrived from a monitored system, a ticket queue, or whoever was able to open a ticket in the first place. When the copilot reads it in the course of doing the job it was asked to do, that instruction lands in the same window as the engineer’s request and carries the same weight. No single field here is exotic. A SAP estate emits thousands of these a day, most of them machine-written, and a copilot that helps with incidents has to read all of them. The malicious one looks exactly like the rest right up until the model acts on it.

What makes this unlike most security work is that the payload is ordinary text. Nothing is malformed. There is no buffer to overflow and no input check that got skipped. The malicious instruction can be written a thousand ways and can sit inside content that is supposed to be there, so signature matching and pattern rules do not catch it the way they catch a known exploit.

What prompt injection actually is

When you build an AI feature, you write a set of instructions for the model, usually called the system prompt. Then you feed it the user’s request along with whatever supporting data the task needs. All of it, your trusted instructions and the untrusted data, goes to the model as one block of text, and the model has no dependable way to know where your authority stops and the outside world starts.

Anyone who has done web security has seen this exact failure under a different name. SQL injection works the same way: attacker-supplied data gets folded into a command, and the database runs the part it was never meant to. Prompt injection, a term Simon Willison coined in 2022 (see Appendix A for who he is and why his work anchors so much of this), is that same mistake moved into plain language. Willison keeps drawing a line that is worth repeating. Jailbreaking and prompt injection are not the same thing. Jailbreaking is talking a model into violating its own safety training. Prompt injection is getting a model to follow instructions hidden in data it was handed to process. A team that tested for jailbreaks and thinks it is covered has tested the wrong thing.

The difference that matters is that SQL injection has a fix. You use parameterized queries, the data never gets treated as command, and the problem closes. Nothing like that exists for a language model. You can ask it nicely, you can format the untrusted text, you can train it to be suspicious, and all of that helps a little and none of it is reliable. Several research groups are working on real fixes, and none has fully landed yet. Appendix B sketches the most promising directions and links the papers.

Why “99 percent effective” is a failing grade

Most security controls are allowed to be good without being perfect. A spam filter that catches 99 percent of spam is doing its job, because spam is high volume and noisy and catching most of it removes most of the harm. Detectors get graded on a curve because the threat is statistical.

Prompt injection is not statistical in that way. An attacker is not firing a thousand random attempts and hoping. They sit with your product, rephrase, watch what gets through, and they only need the one phrasing that works. After that they reuse it. A filter that stops 99 of 100 injection attempts hands the attacker the hundredth, and the hundredth is the whole game. That is why every detection layer in the rest of this paper comes with the same caveat; it helps, but it is not the thing to count on.

02 Part II: The Lethal Trifecta

The framing I keep coming back to is Simon Willison’s lethal trifecta. An agent becomes structurally exploitable when three things are true at the same time:

  • It can reach private data.
  • It is exposed to untrusted content.
  • It can send information to the outside world.

It helps to picture a three-legged stool. Each of those is a leg, and the stool only stands when all three are under it. An agent that has all three is a stool solid enough for an attacker to climb up and sit on, and from here on I will call them the three legs of the trifecta.

EchoLeak had all three legs. Copilot could read the user’s mail and files, it ingested an attacker’s email, and it had a way to get data back out. Hold all three at once and an attacker who lands a single injection can make the agent work against its own user, using that user’s permissions. The injection in EchoLeak was not sophisticated. It did not have to be, because the three conditions were already lined up for it.

Each leg also shows up in the OWASP Top 10 for LLM Applications, the field’s running list of what goes wrong. The untrusted-content leg is the one prompt injection (LLM01) exploits. The ability to act, pushed too far, is Excessive Agency (LLM06). And an agent that can be driven to consume without limit is Unbounded Consumption (LLM10). The trifecta is a way of seeing how those named risks combine on a single agent, instead of treating them as separate findings.Each leg also shows up in the OWASP Top 10 for LLM Applications, the field’s running list of what goes wrong. The untrusted-content leg is the one prompt injection (LLM01) exploits. The ability to act, pushed too far, is Excessive Agency (LLM06). And an agent that can be driven to consume without limit is Unbounded Consumption (LLM10). The trifecta is a way of seeing how those named risks combine on a single agent, instead of treating them as separate findings.

You do not take away a leg with a better prompt, because the model still reads everything in one stream. You take one away by removing the condition or constraining it hard enough that the agent cannot be steered into using it. A stool missing a leg will not hold anyone’s weight. Different products give up different legs. You can wall off the private data, you can keep untrusted content from reaching the model, or you can cut the path data can take on the way out.

For LuumenAI this paper focuses on the first two legs. We control untrusted content where it enters, and we scope what private data the model can reach through authorization. The third leg, the outbound path, is its own subject and gets the next paper. We start here because the strongest and most reliable controls available today sit before the model runs, and that is where we have put the most work.

In late 2025 Meta published the “Agents Rule of Two,” which says an agent should hold at most two of the three legs in one session without a human in the loop. Willison endorsed it, calling it about the best practical advice available, given that there is still no dependable defense against injection. Meta was also clear that plenty of useful designs do not fit the rule and that following it is no guarantee. We use it as an operating default. An agent in LuumenAI is built to hold no more than two legs on its own, and when a task actually needs all three, the third leg is not pulled automatically. A person reviews and approves the consequential step before it runs. That checkpoint is not a fallback for when the other controls fail. It is a designed part of how high-risk actions work, because some decisions should not be handed in full to a model that a sentence can fool.

Book Your AI Workshop Today

A free half-day workshop with your team to align on goals. You leave with a ranked opportunity map showing where agents will have the biggest impact, and a clear view of what to build first
Get in Touch

03 Part III: Defending the Model, Not the Machine

Most security writing about agents is really about sandboxing the code the model produces. That work matters, and the second paper is all about it. It also solves a different problem than the one here. A sandbox stops the model from harming the systems around it. It does nothing about a malicious instruction reaching the model in the first place. We needed the second thing, and it lives in a layer that runs before the model does.

We call that layer the pre-runtime. It sits in front of whatever agent framework is doing the actual work, and the framework underneath is something we treat as replaceable. Most of our security investment goes here, because it is the last place we have full, deterministic control before the model starts making probabilistic decisions.

Authorization context

Before a request reaches the model, we attach what we call authorization context: the organization, workspace, user, and role the request actually belongs to, among other things. What matters though, is the source. It comes from the user’s authenticated session, not from anything the client puts in the request body. A user can claim to be an administrator, but the session token is what decides whether they are one.

This is aimed at the confused deputy problem, where an agent with broad legitimate access gets talked into using that access for someone else. If the agent’s sense of who it is acting for comes from a verified session rather than from text in the conversation, an injected instruction cannot quietly promote itself. The model is working inside limits that were fixed before it read a word. We also keep the sensitive parts of that identity out of the model’s context window, since anything sitting in the window can be coaxed back out.

Concretely, the model gets a small, redacted block of situational facts, enough to be useful and nothing that would matter much if it leaked. It sees the workspace it is working in by name, which features that workspace has turned on, the user’s role and timezone, the organization’s name, and a short set of remembered facts about the conversation so far. What it does not see is the part that decides anything: the session token, the raw permission and entitlement lists, the full set of client IDs the user can reach. Those stay in the authorization context that the surrounding code enforces, not in the prompt the model reads. The block the model does get is labeled for what it is, descriptive context rather than a grant of authority, so that even if the model treats it as gospel, that still cannot let it do anything its enforced scope would not already allow

Guardrails

We run a guardrails layer that pairs our own deterministic input scanning with NeMo Guardrails. The deterministic scanner runs on every request and does not get to be skipped. NeMo Guardrails sits on top of it as a configurable rail engine for the checks that benefit from being tuned per deployment, and it can be set to fail closed, so that a rail failure blocks a request rather than letting it through. Between them they check input on the way in, constrain how a conversation can proceed, govern what retrieval is allowed to return, and filter output on the way out. In practice that is a set of rules about which topics and actions are allowed and what happens when something disallowed shows up, whether it gets refused, stripped, or handed to a person.

This is where the 99 percent problem from Part I lands. Some of these checks are deterministic and some are classifier-based, and both share the same limit. A determined attacker eventually finds the phrasing that no rule and no classifier has seen yet. We use this layer to catch the loud, known-bad cases cheaply and consistently. We do not, however, let it be the only thing standing between an attacker and anything that matters.

At the category level, the rails cover a handful of recurring shapes rather than a list anyone could read off and route around. On the way in we look for the obvious manipulation patterns: text trying to override the operator’s instructions, text fishing for the system prompt or the names of internal tools, text trying to talk its way past an approval gate, requests to read secrets or credentials, and the small set of catastrophically destructive commands that have no place in a chat turn. We also flag attempts to forge approval or audit state, because a convincing fake that claims something was already approved is its own kind of attack. Tool and terminal output gets its own handling and is marked untrusted on the way back. The rest of the rails sit at the other edges of the conversation, governing what retrieval may return, how a conversation is allowed to proceed, and what the final response is checked for before it leaves. Whatever is caught here is caught cheaply, and nothing downstream is allowed to assume the catch happened.

Content wrapping and risk levels

When untrusted content has to go to the model, we do not paste it in raw. We mark it as untrusted, tag it with its source, and attach a risk level that the rest of the system reads when it decides how to handle the content. Marking it does not force the model to behave. It tilts the odds, and it gives everything downstream of the model a clear signal that this block is not to be trusted on its own.

We got this idea from Microsoft’s research on spotlighting, which describes a family of techniques for exactly this: delimit untrusted content clearly, mark it so the model can tell it apart, and transform it so embedded instructions are less likely to read as commands. It builds on the older dual-LLM pattern, where the model that can take actions never sees raw untrusted text and a separate, walled-off model does the reading. What spotlighting and the dual-LLM pattern have in common is that they stop expecting the model to sort trusted from untrusted on its own, and do that sorting for it instead.

Intent routing

We route requests by intent, and routing decides which tools are even on the table for a given request. A request to read documentation does not get handed the tools that change a system. Shrinking the set of reachable tools shrinks the set of actions an injection could trigger if it slipped through everything upstream. Routing usually gets discussed as a way to save cost or improve quality. The security value is that it keeps the model’s hands off tools it has no business using for the task in front of it.

Contracts

Every path that carries data toward the model goes through a contract. By that I mean a validated, structured interface, not an open pipe. There is a contract on the input before the model runs, and there are contracts on retrieval and on memory, so that what comes back during a run has a known shape and known limits. A retrieval call does not get to dump whatever it matched straight into the window.

This is the piece most specific to how we built LuumenAI, so I will be plain about it. The authorization context, the guardrails, the content wrapping, the intent routing, and the contracts are not separate features that happen to sit near each other. They are one layer, which we call the pre-runtime, and its job is to settle everything that ordinary code can settle before the model gets to make any call of its own. By the time a request reaches the model, that layer has already worked out who the request belongs to and what they can touch, which tools are even on the table, what the model may retrieve and remember, and whether the step at the end is going to need a person to sign off.

We did not start here. The first version of LuumenAI handed the model a large pile of tools and a lot of latitude and trusted it to make good choices, which is roughly what every early agent does. Walking that latitude back, a piece at a time, is most of how we arrived at the pre-runtime. The model no longer gets an open connection to our systems. It gets a prepared run with the risky options already taken off the table, and the agent framework underneath is something we treat as swappable.

The reason almost all of the investment goes into this one layer is timing. It is the last point in a request where the behavior is fully deterministic. Everything past it runs through a model that a well-written sentence can talk into the wrong thing, and the move that has held up for us is making sure the options that would do real damage are gone before the model ever gets the chance.

04 Part IV: Controlling What the Model Can Do

Keeping bad input out is half of this paper. The other half is limiting what the model can do once it is running, because a model that has not been injected at all can still be talked into a bad call.

Tool calling

The worst version is the model being steered into calling a tool destructively, or into pulling sensitive records and sending them somewhere. Willison’s framing, which I think is right, is that agent security is mostly API security. The protections you can rely on are the ones enforced at the tool’s own boundary: each tool scoped to the narrowest permissions that let it do its job, and the expensive or irreversible calls gated behind a human. You do not get those guarantees by trusting the model to be careful.

MCP

The Model Context Protocol is mostly a way to hand tools to a model, so every tool-calling concern above applies to it directly. It also introduces a new one. Because a tool’s definition, its description and parameter docs, is text the model reads, the definition itself becomes a place to hide an injection. Researchers have shown that every field of a tool schema can carry an instruction. On top of that, third-party MCP servers are a provenance question, and a tool definition can change after you approved it. In LuumenAI we keep this surface small on purpose. We register MCP servers only from an explicit allowlist, which today holds a single read-only documentation source, and we filter the tools those servers expose down to read-only, so write and destructive operations are excluded before the model ever sees them. The tool definitions we do accept we treat as untrusted until checked, prefer pinned and signed, and vet the way we would vet any other dependency we pull in.

Sub-agents

A sub-agent is not just another tool, and treating it like one causes trouble. It is a second model with its own context and its own access. The new risk is that one agent’s output becomes another agent’s input, and the receiving agent tends to trust it because it came from inside the system. If the first agent was processing untrusted content, an injected instruction can ride its output into the next agent wearing a trusted badge. We treat inter-agent output as untrusted by default, and we scope each subagent to only the access it needs, so that compromising one of them does not give an attacker the others

Memory

Memory makes all of this last longer. A normal injection is gone when the conversation ends. A poisoned memory entry can sit for sessions and fire later under the right prompt. This is not hypothetical. The security researcher Johann Rehberger has demonstrated persistent memory poisoning against shipping assistants, including planting instructions in ChatGPT’s long-term memory and in Google’s Gemini that survived across separate conversations. Because we let the model use memory, memory goes through a contract too, with rules on what can be written and read back, so the long-term store does not become a place to leave instructions for a future run.

05 Part V: Cleaning What Goes In

Everything above assumes the data reaching the model was not poisoned at the source. Files need their own handling, because a document is an easy place to hide an instruction.

When LuumenAI takes in a document, it does not trust it on arrival. The file goes through processing before any of its content can reach retrieval or the model, and the higher-stakes material gets both automated checks and human review. This is the data poisoning problem OWASP lists as LLM04, along with the vector and embedding weaknesses listed as LLM08. The attack is quieter than a live injection. Someone who gets a malicious document into your knowledge base does not need to touch a conversation, because the model will retrieve the poison later and treat it as trustworthy. PoisonedRAG, a 2025 research result, showed that planting a handful of crafted documents in the collection of files a system later searches is enough to bend the model’s answers. So, the checking happens at ingestion, before anything joins that collection and inherits the trust the model gives its own retrieved knowledge.

Here is what that processing means in practice. A file does not get to skip the line. Before any of its content can be embedded into the searchable collection or shown to the model, it has to come back clean from a malware and integrity scan, and until it does, the content is held back. The model is told only that a file exists, with explicit instructions not to guess at what is in it. Alongside that, we record provenance for every ingested document: where it came from, a checksum of its contents, a version, and the workspace and roles allowed to retrieve it. The document then moves through visible ingestion states, parsing, then chunking, then embedding, then indexing, rather than appearing in the index all at once, and higher-stakes material gets human review before it is allowed to join the collection at all. None of this is a content filter that reads the document and decides whether the prose is trustworthy, which is exactly the judgment we already said the model cannot reliably make. It is a set of gates on provenance and handling, so that the trust a retrieved document inherits is trust we can account for.

06 Part VI:Securing the User, the Workspace, and the Organization

The pre-runtime pays off most visibly for an enterprise or a managed service provider. Because every request is anchored to a verified authorization context, the same enforcement runs at three nested levels: the individual user, the workspace, and the organization that contains them. For an MSP, a workspace is usually one of their clients.

An MSP running many clients on one platform cannot have one client’s data or actions leak into another’s, and trusting the model not to confuse them is not a control. Because identity and scope are settled before the model runs and then enforced through contracts and least privilege, the separation holds at each boundary rather than only at sign-in.

A useful side effect is that the same design creates the evidence an auditor needs. The authorization context is access-control evidence. The contracts, guardrail policies, and audit logs cover most of the data-handling and change-management questions a SOC 2 Type II or ISO 27001 review will raise, and they come out of how the system runs rather than from a document written afterward.

The short version on certification is this: the architecture is built to produce the evidence these regimes ask for, while the certifications are a platform and program matter rather than something the AI runtime claims on its own. The runtime emits structured audit events for the decisions auditors care about: who a request was authorized as, which tools were selected, what was approved or rejected, and what was retrieved and cited. The authorization context doubles as access-control evidence. Mapping those signals to specific SOC 2 Type II or ISO 27001 controls, storing them durably, and producing auditor-facing reports belongs to the broader Luumen platform and its compliance function, not to the model-facing code. For federal work, the design carries a distinct deployment edition and is built to extend toward a FedRAMP-oriented posture, enforcing the same identity, scoping, and approval boundaries at a higher bar. That is a direction the architecture supports, not a certification already in hand.

There is one limit worth naming for teams headed toward federal work, where the bar is highest. Open-source security tooling does not bring a compliance certification of its own. The moment you self-host it, its security posture becomes your infrastructure’s responsibility, which is a deliberate undertaking rather than a checkbox.

07 Part VII: Where There Are No Good Answers Yet

Prompt injection has no complete fix, and nothing here changes that. The whole preruntime reduces risk and narrows what a successful injection can do. None of it gives the model the ability to tell instructions from data, because that ability does not exist yet. Indirect injection through retrieved content is built into retrieval, which is to say built into anything useful. The tension between capability and security never settles, because the combination the trifecta describes, private data plus outside input plus the ability to act, is exactly what makes an AI assistant useful in the first place. Every condition you take away to make it safer also takes away something it could do.

There is also the half this paper set aside on purpose. Controlling the input perfectly still leaves you with whatever the model does next. It generates output, calls tools, writes code that something runs. Bad data kept out of the model is progress and not safety. Containing the model after it acts, through sandboxing and isolation, and limits on where data can go, is the harder and less settled half, and it is the second paper.

Conclusion

You cannot make the model itself safe. It reads everything in one stream and it cannot be trained into reliable suspicion, so you work on the two places you do control: what reaches it, and what it can do with what it reaches.

The input side comes down to a handful of moves. Check every agent for the lethal trifecta, and require a person on any agent that has to hold all three legs. Anchor every request to a verified authorization context that comes from the session and never from the client. Mark untrusted content and give it a risk level so the model treats it as data. Run guardrails, and remember they are additive. Use intent routing to keep tools out of reach when the task does not need them. Put a contract on every path into the model, retrieval and memory included. Clean what you ingest, because poison in the knowledge base is poison the model will trust later. Scope tools and sub-agents tightly, and treat one agent’s output as untrusted input to the next.

Two hard rules sit underneath all of that.

  1. Treat guardrail detection as additive only, because its catch rate against real attacks is never 100 percent.
  2. If you cannot establish where a piece of ingested data came from, treat it as untrusted.

None of this makes the model trustworthy, and I would not want to leave you thinking it does. It keeps a model you cannot fully trust from doing much harm when it is wrong or gets turned. For an agent in production today, that is about as far as honesty lets you go. The input is half the problem. The next paper is the other half.

Appendix A

Who is Simon Willison?

Simon Willison is a British software developer and independent researcher. He is best known as the co-creator of the Django web framework and the creator of the open-source projects Datasette and LLM. Since 2022 he has written extensively on his blog about the security and practical behavior of large language models, and several ideas this paper leans on come from that work, including the term prompt injection itself, the dual-LLM pattern, and the lethal trifecta. Practitioners cite him often because he tends to name a problem clearly and early, frequently before the rest of the industry has a vocabulary for it.

Appendix B

The state of prompt-injection defenses

No published method fully solves prompt injection today, but a few directions are worth following, and they show up throughout this paper

Spotlighting (Microsoft).
Techniques for marking and transforming untrusted content so the model is less likely to read embedded instructions as commands. arXiv:2403.14720. https://arxiv.org/abs/2403.14720

The dual-LLM pattern (Simon Willison).
Separating the model that can take action from a quarantined model that reads untrusted text and cannot act.
https://simonwillison.net/2025/Apr/11/camel/

CaMeL (Google DeepMind).
Capability and information-flow control enforced outside the model, in code, so that data marked sensitive cannot
reach an outbound channel regardless of what an instruction says. The paper reports provable security on a majority
of the AgentDojo benchmark tasks. arXiv:2503.18813. https://arxiv.org/abs/2503.18813

AgentDojo (Debenedetti et al., NeurIPS 2024).
A benchmark for measuring prompt-injection attacks and the defenses against them. arXiv:2406.13352.
https://arxiv.org/abs/2406.13352

Constitutional Classifiers (Anthropic).
Layered classifiers aimed at catching jailbreaks and injected prompts, with the same ceiling every classifier has.
https://www.anthropic.com/news/constitutional-classifiers

The common thread is that the durable progress is architectural. The approaches that hold up move the trust decision out of the model and into code that surrounds it, rather than trying to make the model itself reliably suspicious.

References

  1. 01 OWASP. Top 10 for LLM Applications (2025). https://genai.owasp.org/llm-top-10/
  2. OWASP. Top 10 for Agentic Applications and the Agentic Security Initiative. https://genai.owasp.org/
  3. Willison, Simon. The lethal trifecta for AI agents (June 16, 2025). https://simonwillison.net/tags/lethal-trifecta/
  4. Willison, Simon. The dual-LLM pattern and commentary on CaMeL. https://simonwillison.net/2025/Apr/11/camel/
  5. Debenedetti, E., Shumailov, I., Fan, T., Hayes, J., Carlini, N., et al. Defeating Prompt Injections by Design (CaMeL). arXiv:2503.18813. v1: 67% of AgentDojo tasks with provable security; v2: 77% vs. 84% for an undefended system. https://arxiv.org/abs/2503.18813
  6. Debenedetti, E., et al. AgentDojo: A Dynamic Environment to Evaluate Prompt Injection Attacks and Defenses for LLM Agents. NeurIPS 2024. arXiv:2406.13352. https://arxiv.org/abs/2406.13352
  7. Aim Labs. EchoLeak (CVE-2025-32711, CVSS 9.3, zero-click indirect prompt injection in Microsoft 365 Copilot; server-side patch; no confirmed in-the-wild exploitation). https://www.aim.security/lp/aim-labs-echoleakm365 — MSRC: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-32711 — Analysis: https://arxiv.org/abs/2509.10540
  8. Hines, K., Lopez, G., Hall, M., Zarfati, F., Zunger, Y., Kıçıman, E. Defending Against Indirect Prompt Injection Attacks With Spotlighting. Microsoft, 2024. arXiv:2403.14720. https://arxiv.org/abs/2403.14720
  9. NVIDIA. NeMo Guardrails. https://docs.nvidia.com/nemo/guardrails/
  10. Meta. Agents Rule of Two: A Practical Approach to AI Agent Security (Oct 31, 2025). https://ai.meta.com/blog/practical-ai-agent-security/ — Willison endorsement: https://simonwillison.net/2025/Nov/2/new-prompt-injection-papers/
  11. Zou, W., Geng, R., Wang, B., Jia, J. PoisonedRAG. USENIX Security 2025. arXiv:2402.07867. https://arxiv.org/abs/2402.07867
  12. Kosman, Simcha (CyberArk Labs). Poison Everywhere: No Output from Your MCP Server Is Safe. 2025.
    https://www.cyberark.com/resources/threat-research-blog/poison-everywhere-no-output-from-your-mcpserver-is-safe
  13. Rehberger, Johann. ChatGPT: Hacking Memories with Prompt Injection (Sept 2024).
    https://embracethered.com/blog/posts/2024/chatgpt-hacking-memories/ — Hacking Gemini’s Memory (Feb 2025) https://embracethered.com/blog/posts/2025/gemini-memory-persistence-prompt-injection/
  14. Anthropic. Constitutional Classifiers. https://www.anthropic.com/news/constitutional-classifiers
  15. NIST. AI Risk Management Framework. https://www.nist.gov/itl/ai-risk-management-framework
  16. MITRE. ATLAS. https://atlas.mitre.org/

About the Author

Josh Greenwell

Software engineer at apiphani and co-founder of Culture Booster

Book Your AI Workshop Today

A complimentary half-day session with our team to align your goals. Walk away with a prioritized opportunity map highlighting where AI agents deliver maximum impact and a precise roadmap for your first build.
Get in Touch

Contact Us

  • Tell us more about your business and what you need from automation and business software.
  • One Financial Center
    Suite 1640
    Boston, MA 02111
  • Request a Quote: +1 (833) 695-0811

Get in Touch