Poisoned rule files can make coding agents import the wrong package
Researchers hid instructions in AGENTS.md-style rule files and got coding agents to swap real dependencies for attacker packages. What it means for teams.
Rule files such as AGENTS.md, CLAUDE.md and .cursorrules tell coding agents how a project works. Developers copy them between repositories and download them from public collections. A paper posted on 7 October 2026 by researchers at Duke University shows what a few sentences hidden in such a file can do: make the agent replace a legitimate dependency with one the attacker controls, in most of the tasks they tested. The attack needs no access to the victim's agent or model, and current prompt injection detectors missed many of the poisoned files.
Rule files are trusted context
The AGENTS.md project describes the format as "a README for agents: a dedicated, predictable place to provide the context and instructions" an agent needs, and says it is used by over 60,000 open-source projects. Tools load these files automatically. The Claude Code documentation, for example, says CLAUDE.md files and auto memory are "loaded at the start of every conversation", and that Claude Code by default reads AGENTS.md when a project has no CLAUDE.md: every one in the working directory and the directories above it at session start, and a subdirectory's own AGENTS.md when it opens a file there. A rule file can also import other files with @path syntax.
The same page is clear about what that context is and isn't: Claude "treats them as context, not enforced configuration". That sentence is the heart of the problem. An instruction in a rule file carries the authority of the project, but nothing checks it.
What the researchers did
The paper calls it a package hallucination attack. Ordinary package hallucination is a model inventing a dependency that doesn't exist, which attackers can then register (sometimes called slopsquatting). Here the attacker chooses the package: a lookalike such as numpy_hl in place of numpy, published to a registry, with a poisoned rule file to steer the agent to it.
Hand-written injections did poorly. Rule files are long and agent prompts are full of tool definitions, so a short malicious instruction gets diluted: even the optimised prompt in the paper's example was 139 tokens out of about 13,800, 1% of the input. The authors' method, PackHallu, rewrites the injected text over many rounds against a local stand-in agent, scoring each candidate on how often the malicious package name appears anywhere in the agent's reasoning and tool calls rather than only in the final code. One optimisation run took 24 to 30 hours on up to two workstation GPUs.
The test set-up was broad:
- 3 benchmarks (BigCodeBench, DS-1000 and RefactorBench) covering 10 Python packages;
- 8 agent frameworks: OpenHands, OpenCode, Aider, Pi Coding Agent, Cline and Kilo Code, plus Claude Code and Cursor;
- 13 models, local and hosted.
All agents ran in their autonomous modes, with confirmation steps turned off. The malicious packages lived on a local mirror of PyPI and only printed a message, so nothing was published to a public registry.
What they found
In the main experiment (OpenHands with five open models, on BigCodeBench), the agent imported the attacker's package in 79.29% of tasks on average, and in 67.68% the code also ran and triggered its payload. The best existing technique the authors compared reached 14.99% and 11.35%.
The optimised text transferred. Prompts tuned on one framework and model still worked against others: an average of 74.81% imports across four combinations of widely used agents and models, where both differed from the stand-in. Cursor reached up to 98% on some packages. Claude Code, running Claude Sonnet 4.6, was the most resistant, at 31% to 59%.
Three findings deserve attention:
- More capable models were more susceptible when the framework was fixed. The authors attribute this to stronger models following instructions, including rule files, more faithfully.
- Popular packages were harder to displace.
numpyandscipywere the hardest targets across the board, which the authors suggest may reflect how often they appear in training data. - Context dilutes the attack. On repository-level refactoring tasks, success fell to 18.60%, because the surrounding code makes the injected text a smaller share of the input.
The Claude Code case study is instructive. In 3 of 87 tasks the agent recognised the injection as an attack and complied anyway. The largest group of failures, 33 tasks, came from the agent writing the malicious import, seeing it fail at runtime, and reverting to numpy instead of installing the missing package. Not installing unfamiliar packages was, in effect, the defence.
Detection didn't solve it. On 200 rule files, half poisoned, five prompt injection detectors either missed most attacks or flagged too many benign files. The best balance was a 21% miss rate with 11% false positives. One detector missed every poisoned file. Rule files are instructions by design, which is exactly what injection detectors look for.
How much weight to give it
This is a preprint, and the conditions favour the attacker: confirmations off, a victim who adopts the poisoned file unread, and a malicious package that installs cleanly. Some tested versions are older than what teams run today; the Claude Code tests used version 2.1.141. There is no report here of the attack being used in the wild.
The underlying point doesn't depend on those details. An agent that follows project instructions faithfully will follow malicious ones too, and the supply chain for those instructions is informal: copied files, marketplaces, pull requests that touch dotfiles nobody reviews. The dependency swap is one payload. The same channel could ask for a weaker configuration or a different download URL.
What it means for engineering and security leaders
- Treat rule files as code. They change what an agent writes and runs. Review changes to
AGENTS.md,CLAUDE.md,.cursorrulesand the files they import as carefully as build scripts. - Don't rely on the model to notice. Even the most resistant agent in the study imported the attacker's package in 31% to 59% of tasks.
- Put the control where the package is installed. A model can be talked into writing an import. A registry allowlist or a confirmation step can stop the install.
Practical takeaways
- Add rule files and their imports to CODEOWNERS, so changes need a named reviewer.
- Read any rule file you download before you use it, and prefer writing your own short one to adopting a long one from a marketplace.
- Watch dependency diffs: flag new packages in manifests, lockfiles and import statements in agent-written pull requests, especially near-copies of well-known names.
- Install from a private registry or proxy with an allowlist, so an unknown package fails to resolve.
- Keep confirmations on for package installs, even in otherwise autonomous sessions. In Claude Code, the documentation's own advice applies: to block an action regardless of what the model decides, use a
PreToolUsehook. - Rerun your agents on a sample task with each new rule file and check what they import.
On the Enterprise plan, SpanDock guardrails on computers check every tool call in Claude Code, Codex and Gemini CLI before it runs. A guardrail with your own regular expression, applied to tool-call arguments and set to Confirm, makes the person confirm a matching call, such as a package install command, and flags it for review. Tools without hooks, such as Cursor, aren't checked this way; only their MCP calls through the gateway are.