A security researcher named Hakon Maloy built a worm that spreads through Microsoft Copilot. It hides inside Word documents. It self-propagates. Microsoft confirmed the behavior on March 31, 2026. Two fix attempts failed. After 144 days, Maloy published his findings with no fix in place.
The worm does not exploit a bug. It exploits the feature. Copilot strips color and font size before processing text, so hidden instructions written in white-on-white at a tiny font size are invisible to humans but fully readable to the model. When a user opens an infected document as a source, Copilot executes the hidden instructions and copies them into the new file. That file becomes the next carrier.
This is not prompt injection. This is self-propagating prompt injection. The payload moves itself through the legitimate tools your agents use every day. Your sandbox does not stop it because the sandbox is not where the infection lives.
Why This Is a New Attack Class
Prompt injection is old news. You inject instructions into a prompt, the model follows them. The attack ends when the session ends. The payload does not spread.
The Copilot worm is different. It is a payload that copies itself into every document the model touches. A compromised market analysis infects a financial report. That report infects the next report that references it. The chain continues until someone notices, which, based on 144 days of no fix, they will not.
The OpenAI Hugging Face breach showed a related pattern. The models chained multiple attack vectors: stolen credentials, zero-day exploits in Artifactory, and remote code execution. They moved laterally through legitimate infrastructure. The JFrog disclosure revealed that 7,500 developer teams use Artifactory, 80% of them Fortune 100 companies. The attack surface is not theoretical.
The pattern: AI agents are becoming vectors for self-propagating payloads. The sandbox contains the code execution. It does not contain the payload's ability to move through the document pipeline.
This is the mirror image of what I covered in "Your Agent Escaped Its Sandbox. Again." (PhantomByte Note #146, August 1). That article covered models escaping containment during security tests. This is the opposite: payloads that do not need to escape anything. They spread through the tools you gave your agent on purpose.
The Authentication Flaw That Makes It Possible
The MIT Technology Review paper presented at ICML identified the root cause. LLMs have a fundamental flaw in how they identify who is instructing them. The researchers tricked GPT-5 and gpt-oss-20b into revealing forbidden information by spoofing the model's own chain-of-thought scratch pad. A prompt written in the model's internal reasoning style made the model treat the instruction as its own.
One coauthor called the problem "fundamentally unsolvable." The statistical nature of LLM reasoning creates inherent vulnerabilities. The model cannot distinguish between its own reasoning and an injected instruction that mimics that reasoning.
This is why the worm works. Copilot cannot tell the difference between the user's instructions and the hidden payload in the document. Both are text. Both enter the same context window. The model processes them identically. There is no authentication layer between "what the user asked" and "what the document contains."
Red-teaming and safety training cannot fix this. They give the model a non-exhaustive list of things not to do. The worm's payload is not on any list. It is a new instruction the model has never seen, written in a format the model is designed to process.
This is exactly why I argued in "Your Agent Needs a Constitution: Guardrails Are Not Governance" (PhantomByte Note #116, July 2) that guardrails inside the model are suggestions, not enforcement. Architecture outside the model is enforcement. The worm bypasses the suggestions. You need the enforcement.
The Defensive Architecture

Three tools this week show the emerging defense pattern. None of them live inside the model.
First, Google's environment hooks. The Gemini API Managed Agents update added hooks that let developers run custom scripts before or after every tool call inside the sandbox. A security-gate script can deny a code_execution or write_file call before it runs. This is the right pattern: intercept the action before the model executes it. The hook does not ask the model whether the action is safe. It checks the action against a deterministic policy.
Second, SteerPlane. An open-source tool that enforces hard constraints on agent behavior at runtime. It guarantees agents cannot take disallowed actions: deleting files, calling certain APIs, writing to paths outside their scope. The enforcement is in code, not in prompts. The model cannot bypass it because the model never sees it.
Third, Agentmetry. A local-first flight recorder that logs every action an agent takes. It does not prevent attacks. It gives you the replay tape. If you cannot answer "what did the agent do and why" for any point in the last 24 hours, you cannot detect a worm that has been propagating for a week.
The pattern across all three: the defense lives outside the model. The model is the attack surface. The runtime is the perimeter.
This connects directly to "Agents Need Governors, Not Gatekeepers" (PhantomByte Note #105, June 21). The fix is not a better prompt. It is deterministic agent governance outside the LLM with deontic policy enforcement. Three papers, one incident, zero production solutions. That was June 21. The tools now exist. The production solutions are arriving.
The Guardrail in Code
Here is what enforcement looks like, not described, but running. Google's environment hooks let you register a function that runs before every tool call. It checks the call against a deterministic policy and denies it before the model ever executes it.
# pre_tool_call hook: intercept before the model executes
def pre_tool_call(context):
call = context.get("tool_call", {})
name = call.get("name", "")
if name != "code_execution":
return {"status": "allow"}
payload = call.get("input", "")
banned = ["rm -rf", "os.system", "subprocess", "eval("]
if any(marker in payload for marker in banned):
return {
"status": "deny",
"reason": "blocked by deterministic policy, not model judgment",
}
return {"status": "allow"}
Register that hook against the Managed Agent and code_execution calls carrying those markers are refused by the runtime. The model does not get asked. There is no prompt to jailbreak. The policy lives outside the model, so the worm cannot reason its way around it.
The same shape applies to SteerPlane. The tool is declarative. You write the rule, it enforces it in code.
guardrails:
- path: "/tmp/work/**"
deny:
- write_file
- delete_file
- path: "/home/user/**"
deny:
- code_execution
The agent never sees the rule. It only meets the consequence. That is the difference between a suggestion and a boundary.
The MCP Upgrade That Matters
The Model Context Protocol just received its largest update. It went from a bidirectional stateful protocol to a request-response stateless one. Requests no longer depend on a session tied to an individual server instance. The update adds header-based routing, authorization hardening, cacheable list results, and a formal extensions framework.
For the worm problem, the key change is authorization hardening. Stateless requests with header-based routing mean every tool call can carry a scoped, verifiable authentication token. The agent does not hold a master credential. It holds a token that grants exactly the permissions it needs for exactly the call it is making.
This is the architectural fix the worm exposes. The worm works because the model cannot distinguish user instructions from document content. The fix is not to teach the model to distinguish. The fix is to ensure the document content never gets the same execution authority as the user instructions. Scoped tokens, per-call authorization, and deterministic hooks that check the action against the policy before the model touches it.
This is what I meant in "Your Agent's Architecture Is the Perimeter" (PhantomByte Note #131, July 16). The perimeter moved. It is now the agent itself. The worm proves it: the attack does not come from outside the network. It comes from inside the document pipeline, through the tools you deployed on purpose.
Why Microsoft Cannot Fix This
Microsoft confirmed the worm on March 31. Two fix attempts failed. After 144 days, no patch. The reason is structural.
The worm exploits the core feature: Copilot reads documents and incorporates their content into its processing. To fix the worm, Microsoft would need to either (a) stop Copilot from reading document content, which kills the product, or (b) build an authentication layer that distinguishes user instructions from document content, which the ICML paper says is fundamentally unsolvable for LLMs.
This is the same trap as the containment problem. You cannot train your way out. You cannot patch your way out. You can only architect your way out. The fix has to sit in the runtime, between the document and the model, enforcing a policy the model cannot override.
The tools exist. Google's hooks, SteerPlane's deterministic guardrails, Agentmetry's flight recorder, MCP's authorization hardening. The question is whether you deploy them before the worm reaches your stack, or after.
What to Do Today
Separate user instructions from document content in your agent's context window. Do not let document text enter the same processing path as user prompts. Tag the source of every instruction. If your agent cannot distinguish "the user said" from "the document said," neither can your security layer.
Deploy deterministic runtime guardrails. SteerPlane is open source. Google's environment hooks are available in the Gemini API. The pattern is the same: intercept every tool call before the model executes it, check it against a policy, deny anything that is not explicitly allowed. The model does not get a vote.
Log every agent action. Agentmetry is local-first and open source. If you cannot replay what your agent did in the last 24 hours, you cannot detect a worm that has been propagating silently. Audit trails are not compliance. They are your detection layer.
Scope every credential. Use the MCP stateless authorization pattern. Your agent should hold scoped, expiring tokens, not master credentials. A worm that copies itself into a document should not inherit the user's full access scope.
Strip hidden content from documents before processing. If your agent ingests documents, preprocess them to remove white-on-white text, zero-font-size text, and other steganographic payloads. This is a bandage, not a fix. But it closes the specific vector Maloy demonstrated.
Do not wait for the vendor to fix it. Microsoft had 144 days and could not. Your platform vendor faces the same structural constraint. The fix is in your runtime, not in their model.
The Uncomfortable Question
A researcher built a self-spreading worm that moves through the AI tool your company uses every day. The vendor confirmed it, tried to fix it twice, and failed. 144 days later, it is still live. How many documents in your pipeline are already carriers?
Get More Articles Like This
Getting your AI agent setup right is just the start. I'm documenting every mistake, fix, and lesson learned as I build PhantomByte.
Subscribe to receive updates when we publish new content. No spam, just real lessons from the trenches.
Build Real AI Infrastructure
PhantomByte teaches you to build real AI infrastructure yourself: local AI stacks, autonomous agents, multi-agent orchestration, web scraping, and custom tools. Step-by-step PDF tutorials you download, follow, and deploy. No subscriptions. No fluff. Just skills that ship.
