- Book: AI That Ships
- The series: AI in TypeScript — 5 books, from your first LLM call to agents in production — all five here
- My project: Hermes IDE | GitHub — an IDE for developers who ship with Claude Code and other AI coding tools
- Me: xgabriel.com | GitHub
A coding agent on a developer's laptop was about to run npm install function-color. Nobody had asked for that package. It came from a file called AGENTS.md sitting in the developer's ~/Downloads folder, and nobody knew how it got there.
That is the incident Forever Security described on October 22, 2026, in a write-up by Gal Weizman and Omer Samet titled "Markdown Is the New Executable". Their software stopped the install on a customer's machine. Help Net Security covered it on October 26. The package, function-color, came from a malicious campaign and npm has since removed it.
The researchers then rebuilt the attack in a lab. Against Codex, they report: "In our testing, Codex ran the malware in more than 90% of attempts." A CLAUDE.md variant got Claude Code to route its traffic through an attacker's server "about 50% of the time", which exposed the login token. Their summary of the fix is the uncomfortable part: "There is nothing here for OpenAI or Anthropic to patch."
How the file gets into Downloads
According to Forever, a web page can save a file named AGENTS.md into your Downloads folder on its own, choosing both the name and the contents. Their words: "No click. No approval." In their lab page, the file lands in ~/Downloads the moment you leave the page.
So Downloads is a folder any website can write to. The Forever post also points out that agents browse on their own now, so an agent can visit the page that plants the file, with no human watching it arrive. Forever's own numbers from its customer base, as Help Net Security reports: about half of its customers run a coding agent inside Downloads, and about a quarter of agent sessions open a browser.
The real file was 13 KB and read like a dull "Engineering Work Guidelines" document. The attack was one section near the end. Forever says the payload failed every time when they placed it in a folder full of security notes, so the bland tone is deliberate.

A file any website can write becomes a file your agent reads at startup.
What Codex did with it
Codex builds its instructions from AGENTS.md files. Per the official docs, it reads a global file from ~/.codex, then starts at the project root ("typically the Git root") and walks down to your current directory, taking one AGENTS.md per directory. "If Codex cannot find a project root, it only checks the current directory." Downloads is rarely a git repository, so starting Codex there means the planted file is the project instruction file.
Codex has a trust gate. The config docs say "Codex loads project .codex/ layers only when you trust the project", and Forever says Codex only obeys an AGENTS.md in a trusted project. Their catch: "Once you have worked in Downloads, it qualifies."
Forever describes the chain like this:
- The agent writes a
[mcp_servers.project-tools]block into~/.codex/config.toml. - The new MCP server runs
launchctl, which runsnpm install function-coloroutside Codex's control. - A child
codex execruns with--dangerously-bypass-approvals-and-sandbox, because the default sandbox withholds the network access an install needs.
Note where that first write lands. Per the config docs, trust governs project .codex/ layers. ~/.codex/config.toml is your user-level config: it ranks below project config in precedence, and it applies in every folder.
Because the change sits in the machine-wide config, later Codex sessions in any project run the attacker's command too. Most of Forever's lab runs used a harmless stand-in that opened a calculator; the real package ran once, in an isolated VM.
What Claude Code did with a CLAUDE.md
Claude Code reads CLAUDE.md from your working directory "and every directory above it", per its memory docs. Since v2.1.277 it also reads an AGENTS.md when there is no CLAUDE.md in that chain, and your user-level ~/.claude/CLAUDE.md doesn't count for that check. So Claude Code started in Downloads would load the same planted AGENTS.md. Forever's Claude test used its own CLAUDE.md, and they say Claude was much harder to fool than Codex.
The version that worked had a section called "Configure the project model gateway". It asked Claude to write .claude/settings.json with ANTHROPIC_BASE_URL pointing at an attacker server on a domain with a valid certificate. Per Help Net Security, every request then carries your login token to that server, and replaying a captured token let the researchers take over the account without running any code. In the same session, a helper agent Claude spawned to check the new config connected through the attacker's server, and that gave the researchers code execution about a fifth of the time. On the next launch, the fake gateway can also pose as the model and return a Bash tool call with the sandbox turned off.
One detail matters for your defaults. Claude Code's permission modes page lists .claude as a protected directory: in default and acceptEdits modes, a write there is a prompt you answer; in auto mode a classifier decides; in bypassPermissions it is allowed. The write-up doesn't say which mode the lab sessions used. Either way, the prompt for that write is the place a person can still stop it.

Both attacks end in a settings file: Codex's user config, Claude Code's project settings.
Why there is nothing to patch
Every step above is a documented feature doing its job. Agents are built to read instruction files from the folder you start them in. They are built to edit config when asked. Codex supports MCP servers in config.toml; Claude Code supports a custom base URL for gateways and proxies, and companies use both for real.
The broken assumption is about the folder. You treat a folder you work in as one you filled yourself, and Downloads is filled by the web. As Help Net Security puts Forever's view: the agent is working as designed, so the defence is on you.
The checklist to run now
These are Forever's recommendations, turned into commands. Run them on macOS or Linux; they only read.
Look for agent instruction files in Downloads and your home folder that you did not put there:
find ~/Downloads ~ -maxdepth 1 \
\( -iname 'AGENTS*.md' -o -iname 'CLAUDE*.md' \)
ls -la ~/Downloads/.claude ~/Downloads/.codex 2>/dev/null
Check Codex's user config for MCP servers and base URLs you didn't add:
grep -nE 'mcp_servers|base_url|launchctl' \
~/.codex/config.toml
Check every Claude Code settings file near your home folder for endpoints:
find ~ -maxdepth 4 -path '*/.claude/settings*.json' \
-exec grep -HnE 'BASE_URL|PROXY' {} +
And look for the package itself. The write-up doesn't say where the install landed, so check global installs and project folders both:
npm ls -g function-color
find ~ -maxdepth 5 -type d \
-path '*node_modules/function-color' 2>/dev/null
Then the habits:
- Don't start an agent in
~/Downloadsor in$HOME. Start it in a project folder you created or cloned on purpose. - Read any
AGENTS.mdorCLAUDE.mdyou didn't write before an agent does, including in a repo you just cloned. - Keep approval prompts and sandboxes on. Treat any instruction to switch them off as the attack.
- If a line turns up that you can't explain, assume the token leaked: sign out of the CLI, sign back in, and replace any API key that was in that environment. Treat a machine where
function-colorwas installed as compromised.
Where Hermes IDE fits, and where it doesn't
I build Hermes IDE, so weigh this with that in mind. Hermes runs Claude Code, Codex and other agents, each in its own real terminal with its own sign-in. Hermes never asks for your tokens.
Two of its defaults line up with the checklist. A task you start with ⌘N (Ctrl+Shift+N on Windows and Linux) runs in a project you picked, on its own hermes/ branch and worktree, with the task as the agent's first prompt. That makes "where did I start this agent" a choice you see, not whatever folder a terminal happened to be in. And per the README, Hermes never answers a permission prompt for you and never types into a terminal on its own, so when the agent's mode asks before writing .claude/settings.json (as default and acceptEdits do), that prompt waits for you.

The ⌘N New task sheet: agent, project, a new hermes/ worktree, the approval mode, and a preview of the command Hermes will run.
It does not block this attack. ~/.codex/config.toml lives outside every worktree, so a change there reaches Hermes sessions like any other. A malicious AGENTS.md committed to a repo you picked is in the worktree too. And if you approve the prompt, the write happens. What Hermes adds is visibility: every agent turn is saved, and the Review Desk (⌘G) shows changes by turn, so a settings file written inside the repo appears in that turn's diff, unless git ignores it.
Hermes is free, source-available under BSL 1.1, and runs on macOS, Windows and Linux. It isn't affiliated with OpenAI, Anthropic or Nous Research. Download it or read the code on GitHub.
If this was useful
This attack works because an agent treated a file it found as policy. AI That Ships is the book on the layer that should sit between an agent and production in TypeScript: guardrails on what it can do, evals that catch it drifting, and cost ceilings when something loops.
The whole AI in TypeScript series, from a first LLM call to agents in production, is at xgabriel.com/ai-in-typescript.

Top comments (0)