Skip to main content
← All articles
ai

GhostApproval: the approval dialog in my coding agent lied to me

GhostApproval: the approval dialog in my coding agent lied to me

I approve a lot of file edits from Claude Code without reading them as carefully as I should. The little dialog says "write to project_settings.json?", I glance at the filename, I hit yes, I keep moving. On 8 July, Wiz Research published a flaw called GhostApproval that turns exactly that habit into a way to overwrite my SSH keys. The dialog wasn't lying about the filename. It was lying by omission about where that file actually pointed.

The trick: the approval dialog shows you the wrong target

The mechanism is almost annoyingly simple, and it's a Unix-era classic dressed in AI clothing. A malicious repo ships a file with an innocent name — say project_settings.json — that's actually a symlink pointing outside your workspace to something sensitive like ~/.ssh/authorized_keys or ~/.zshrc. The agent does an ordinary "edit this file" operation, follows the symlink, and writes to the real target. But the human approval prompt only shows the harmless in-workspace name. Wiz files it under two old CWEs: symlink following, and UI misrepresentation of critical information. The sandbox check validated the symlink's path, not where it resolved to.

Wiz's line for it is the one I keep repeating to myself: the consent is "formally present but substantively empty." You clicked yes. You just didn't consent to what actually happened.

It hit six of the big ones

Affected: Amazon Q Developer, Anthropic Claude Code, Augment, Cursor, Google Antigravity, and Windsurf. A couple got CVEs — Amazon Q's symlink issue is CVE-2026-12958 (fixed in 1.69.0; note it's a different bug from CVE-2026-12957 in the same bulletin, so don't conflate them), and the Cursor flaw was assigned CVE-2026-50549, fixed in Cursor 3.0. Worth being precise on that Cursor one: a second research team (Cato, who called it "DuneSlide") disclosed the same CVE independently, and the public record credits neither firm by name — so it's "assigned CVE-2026-50549," not "Wiz's CVE."

The Claude Code part, told fairly

This is the bit I care about, because Claude Code is my daily driver, and it's the most nuanced of the six. Wiz tested it (v2.1.42) and found the exact failure — the model's own reasoning even noted the file was a symlink to a config file, while the user prompt still just read "Make this edit to project_settings.json?"

But Anthropic disputes that it's a vulnerability, and their argument isn't nothing: it "falls outside our current threat model," because when you start Claude Code in a directory you first have to confirm you trust that directory. Trust the folder, then approve the edit — in their framing, you owned both decisions. They also say a symlink warning shipped in the Edit/Write dialog back in v2.1.32, on 5 February, from their own internal review, before Wiz's report. No CVE was assigned to Claude Code. So: real behaviour, genuinely disputed classification. I'll let you decide whether "you trusted the directory" fully covers "a repo you cloned to inspect just rewrote your authorized_keys."

A human-in-the-loop approval is only worth something if the human is shown the truth. Approve fast enough, on a target that's been quietly relabelled, and you're not a safeguard — you're a rubber stamp with extra steps.

What I did about it

Two things, immediately. First, I updated every one of these tools I actually run — the fixes are the real remediation, and Wiz's guidance to vendors (resolve symlinks before showing the dialog, and flag any write that lands outside the project) is the correct fix regardless of whose threat model it sits in. Second, I went and looked at my own ~/.ssh/authorized_keys and shell rc files to confirm nothing had been appended, because that's the payload this targets.

The durable lesson is a habit change, not a patch. I let coding agents loose on repos I'm merely evaluating — cloned to read, not trusted — and I approve their edits at speed. GhostApproval is a reminder that "review this untrusted repo with my agent" and "trust this directory completely" are not the same sentence, even though the tooling increasingly treats them as one. When I clone something sketchy now, it goes in a throwaway container, the same way I run Pi. The approval dialog is not my security boundary. The container is.

Links

BM
Blue Moose
The moose behind Blue Moose. Full-stack PHP developer — Drupal by day, Symfony by night, tests always.