GhostApproval: six AI coding agents shipped a permission dialog that was reading the wrong path
Tomás Vega
You approve a diff. The agent writes the file. The file, it turns out, was not the file. Somewhere between the approval dialog and the disk, a symbolic link redirected the write to a path you never named, and an attacker's public key ended up sitting in your authorized_keys. This is GhostApproval, disclosed this week by researchers at Google-owned Wiz. Per DevOps.com on July 9, the same primitive hit six AI coding agents at once: Anthropic Claude Code, Amazon Q Developer, Google Antigravity, Augment, Cursor, and Windsurf.
The trick is a Unix feature that has been in every filesystem you have ever used. A symlink is a file whose contents resolve to a path somewhere else. Write to project_settings.json and, if that name is actually a symlink pointing at ~/.ssh/authorized_keys, you did not write to your project. You wrote to your SSH keyring. Every operating-systems textbook has flagged this for decades. Six of the most-funded coding agents on the market shipped without checking.
Where the dialog stopped telling the truth
Here is the ugly detail. Most of these agents do ask before they touch a file. The problem is what the dialog says. The prompt shows the intended path, the string the model typed, and not the resolved path, the file the OS is actually about to open. So you see the friendly project filename, click yes, and the write lands where the attacker aimed it.
That is a full trust failure at the boundary that was supposed to be the last line of defence. The confirmation dialog was the human check. It is the reason we tolerate agents running with the user's own credentials on the developer's own machine. If the check does not resolve the path before it names it, it is theatre.
Why this is a CI/CD story, not a desktop one
You might read the above and reach for the "well, don't clone hostile repos" reflex. In 2026 that reflex is not available. Coding agents ingest issues, PR descriptions, generated code, docs, and, yes, cloned repositories from anywhere the pipeline touches. The distance between "malicious npm package" and "malicious workspace" is now one git clone in a container. Any pipeline step that runs an agent over untrusted content is a plausible vector for the same primitive: dependency review bots, PR triage jobs, changelog generation, doc-diffing, security scanners that shell out to an agent.
Two shifts follow. First, the agent's write path is now part of the pipeline threat model. What identity does it run as? What parts of the filesystem does it reach? What happens when a hostile symlink is on disk before the agent starts? Second, "the user approved it" is no longer a defensible audit line if the approval never named the real target.
A short audit list
The immediate mitigations are unglamorous and, mostly, thirty years old:
- Resolve paths before showing them to a human. If the write target contains a symlink, the dialog names the resolved path or refuses the write.
- Sandbox the agent's write scope to the workspace, with a hard denylist on
~/.ssh, shell rc files, cron files, and the user's config directory. - Never let a coding agent inherit an SSH agent, a browser cookie jar, or long-lived cloud credentials. Give it short-lived, workspace-scoped tokens or nothing.
- In CI, run agents inside an ephemeral container with the checkout as the only writable mount. Bind everything else read-only, and treat the container as untrusted on exit.
The vendor scoreboard, as of Wiz's report
Per DevOps.com's account: AWS, Cursor, and Google shipped fixes. Augment and Windsurf have fixes in progress. Anthropic initially rejected the finding as outside its threat model, then added symlink warnings to Claude Code in a later release. Six vendors, six response curves. The through-line is that none of them caught the class of bug in-house before an external researcher filed the report.
What "decades-old feature" is really telling you
The most useful framing in the disclosure is the "decades-old" label. Symlink-following bugs are the oldest privilege-escalation class in Unix. They live in a body of literature every long-running service learned to respect years ago: resolve first, check the resolution, then open with O_NOFOLLOW where the platform gives it to you. Six agents built in the last two years shipped without any of that. Why? Because agent tooling is written to a "helpful assistant on my laptop" threat model, not a "hostile input, TOCTOU, adversarial filesystem" one. The category is catching up with the deployment. Painfully.
We keep re-learning that agents are just programs with the developer's keys. Every new capability, tool use, file write, MCP server, is a new place where the OS assumes an adult is watching. GhostApproval is a reminder that the adult was a dialog box, and the dialog box was reading from the wrong side of the symlink. Patch your agents. Then patch the pipeline that runs them. Your authorized_keys file will thank you.
Source: DevOps.com (devops.com)