devdot
← All postsEngineering ·

A Text File in Your Downloads Folder Can Hijack Your Coding Agent. Start Agents in the Project Root.

Researchers got OpenAI Codex and Claude Code to run attacker instructions from a planted AGENTS.md. There is nothing to patch, which makes working directory hygiene your job.

Researchers at Forever Security published a neat and slightly uncomfortable finding this weekend. They found it on a customer's machine first: a malicious AGENTS.md sitting in ~/Downloads, which was also the folder the developer had launched their coding agent from. The agent read the file, trusted it, and installed a rogue npm package called function-color.

No exploit. No memory corruption. The agent did exactly what it was built to do.

How the attack works

Coding agents load instruction files from their working directory automatically. AGENTS.md, CLAUDE.md and similar files are how you tell the agent about your codebase, your conventions, your test commands. That's the feature.

The problem is that the agent can't tell your instructions from someone else's. If the file is in the folder, it's in the prompt.

Chrome, Edge and Firefox all save downloads to ~/Downloads by default. So a malicious page can plausibly drop an instruction file there without a single click from the user. The attacker then needs one thing: you launching an agent in that folder.

The numbers from the testing are not comforting:

  • Against OpenAI Codex, a harmless payload (popping the calculator) worked about 90% of the time.
  • Against Claude Code, a malicious CLAUDE.md redirected API traffic to a researcher-controlled server, exposed the agent's OAuth token and ran code in roughly half the tests.
  • Cursor CLI wasn't tested, but it's commonly run from Downloads too.

Nothing to patch

This is the part worth sitting with. The researchers say there is nothing for OpenAI or Anthropic to fix, because the agent is following instructions in its folder as designed. Both vendors recommend running agents from the specific project directory, not your home or Downloads folder.

So the control moved to you. It's not a CVE you wait on. It's a habit you either have or don't.

What we'd do on a team today

Start with the boring stuff, because the boring stuff is the whole defence.

  1. Never launch an agent from a folder you didn't create. Downloads, Desktop, a temp directory, an unzipped archive. If you want to poke at someone else's code, use a disposable container or VM.
  2. Open agents at the project root, not above it. Starting in ~ means every stray instruction file under it is in play.
  3. Treat instruction files like code in review. A new or changed AGENTS.md in a pull request deserves the same eyes as a change to your CI config. It can run commands. Arguably it already does.
  4. Keep approval prompts on. The function-color install only works if the agent is allowed to run npm install unattended. Make that a deliberate decision per project, not a global default someone flipped last month.
  5. Pin and allowlist packages. If your agent environment can only install from an internal registry or a lockfile, a typosquatted package has nowhere to land.

None of this is exotic. It's the same least privilege thinking we already apply to CI runners and deploy keys, pointed at a tool we adopted so quickly that we skipped the threat model.

The wider lesson

Every convenience we gave agents, auto-loading context, running commands, installing dependencies, is also an input channel. The more helpful the agent, the more an attacker gets from controlling a single file it reads.

If your team is rolling out coding agents, write down where they're allowed to run and what they're allowed to read. Put it in onboarding. It takes ten minutes and it closes the cheapest attack on the board.

We're here to help founders and teams design and build digital products that are built to scale with you, not slow you down. If you're looking to build something, get in contact with us today!

NEXT POST →PHP2Shell Touches Most of the Web. Patch Fast, Then Find Every Place PHP Is Hiding.