[{"data":1,"prerenderedAt":656},["ShallowReactive",2],{"blog-stories/ghostapproval-ai-coding-assistants":3},{"id":4,"title":5,"body":6,"category":628,"date":629,"dateModified":629,"description":630,"draft":631,"extension":632,"faq":633,"featured":631,"headerVariant":628,"image":641,"keywords":642,"meta":643,"navigation":342,"ogDescription":644,"ogTitle":641,"path":645,"readTime":646,"schemaOrg":647,"schemaType":648,"seo":649,"sitemap":650,"stem":651,"tags":652,"twitterCard":654,"__hash__":655},"blog/blog/stories/ghostapproval-ai-coding-assistants.md","Six AI Coding Tools Wrote Files You Never Approved (2026)",{"type":7,"value":8,"toc":619},"minimark",[9,22,28,39,44,55,58,64,72,83,87,90,183,186,189,193,196,206,209,212,218,222,225,228,231,235,289,503,520,538,542,545,548,551,585,604,615],[10,11,12,13,17,18,21],"p",{},"On February 10, 2026, researchers at Wiz created a symlink named ",[14,15,16],"code",{},"project_settings.json"," that pointed at ",[14,19,20],{},"~/.ssh/authorized_keys",", put it in a repository, and asked six AI coding assistants to edit the file.",[10,23,24,25,27],{},"Every one of them offered to. The approval box said ",[14,26,16],{},". The write landed on the SSH key file that decides who can log into the machine.",[29,30,31],"tldr",{},[10,32,33,34,38],{},"Wiz published ",[35,36,37],"strong",{},"GhostApproval"," on 2026-07-08: a symlink planted in a repository you cloned makes an AI coding assistant display a harmless filename in its approval prompt while writing to a sensitive file outside the project. Six tools were affected. Three had shipped fixes before disclosure, two had not, and one vendor disputed that it was a bug. Nothing about it is novel except the target: symlink following is a Unix trick older than most of the people using these tools.",[40,41,43],"h2",{"id":42},"what-actually-happened","What actually happened",[10,45,46,47,50,51,54],{},"The technique combines two weaknesses that are individually ancient. Symlink following is ",[35,48,49],{},"CWE-61",". Showing the user one thing while doing another is ",[35,52,53],{},"CWE-451",". Neither is clever. What's new is that there's now a piece of software whose entire job is to read files you didn't write and then edit files on your behalf.",[10,56,57],{},"Wiz's own description of the outcome: \"The user approves what they believe is a harmless local edit; the agent writes to a sensitive file outside of the project workspace.\"",[10,59,60,61,63],{},"The detail that makes it worse is what some of the agents knew. In Claude Code's case, the model's visible reasoning identified the symlink and its real target. The permission dialog still showed only ",[14,62,16],{},". The information existed inside the system and did not make it to the person being asked to approve.",[65,66,67],"danger-box",{},[10,68,69,71],{},[14,70,20],{}," is not a config file. Anything appended to it is a public key that can log into your machine over SSH, from anywhere, without a password. It's the single highest-value write target on a developer laptop, which is why it's the one the researchers chose.",[10,73,74,75,78,79,82],{},"Shell startup files are the other obvious target. Append a line to ",[14,76,77],{},".zshrc"," or ",[14,80,81],{},".bashrc"," and your next terminal window runs the attacker's command, as you, with your credentials loaded.",[40,84,86],{"id":85},"the-six-tools-and-where-each-one-landed","The six tools, and where each one landed",[10,88,89],{},"Not all six failed the same way, and the differences matter more than the headline count.",[91,92,93,109],"table",{},[94,95,96],"thead",{},[97,98,99,103,106],"tr",{},[100,101,102],"th",{},"Tool",[100,104,105],{},"What Wiz found",[100,107,108],{},"Status at disclosure",[110,111,112,124,135,146,157,172],"tbody",{},[97,113,114,118,121],{},[115,116,117],"td",{},"Amazon Q Developer",[115,119,120],{},"Followed symlinks before showing the approval dialog",[115,122,123],{},"Fixed, language server 1.69.0 (2026-05-27), CVE-2026-12958",[97,125,126,129,132],{},[115,127,128],{},"Google Antigravity",[115,130,131],{},"Showed the symlink path instead of the resolved path",[115,133,134],{},"Fixed, 1.19.6 (2026-05-22)",[97,136,137,140,143],{},[115,138,139],{},"Cursor",[115,141,142],{},"Diff UI showed the symlink path, backend wrote through it",[115,144,145],{},"Fixed, 3.0 (2026-06-05), CVE-2026-50549",[97,147,148,151,154],{},[115,149,150],{},"Claude Code",[115,152,153],{},"Reasoning identified the target, the prompt did not show it",[115,155,156],{},"Disputed by Anthropic, see below",[97,158,159,162,169],{},[115,160,161],{},"Windsurf",[115,163,164,165,168],{},"Writes completed ",[35,166,167],{},"before"," the Accept and Reject buttons appeared",[115,170,171],{},"Acknowledged 2026-06-23, no fix at disclosure",[97,173,174,177,180],{},[115,175,176],{},"Augment",[115,178,179],{},"Symlink reads and writes with no confirmation at all",[115,181,182],{},"Acknowledged, no fix at disclosure",[10,184,185],{},"The bottom two rows are a different category of problem. If the file is already written by the time you're asked, the dialog isn't a control, it's a notification. Wiz reported that Augment exfiltrated credentials and wrote SSH keys without asking at all.",[10,187,188],{},"Disclosure ran a long time: discovered 2026-02-10, first reports out on 2026-02-12, and public on 2026-07-08. That's nearly five months, which is a reasonable window by responsible-disclosure norms and also five months in which two vendors did not ship a fix.",[40,190,192],{"id":191},"the-disputed-one","The disputed one",[10,194,195],{},"Anthropic was the only vendor of the six to say this was not a bug. Their position was that it \"falls outside our current threat model,\" because Claude Code asks you to confirm that you trust a directory before it starts working in it. The ticket was closed as Informative.",[10,197,198,199,201,202,205],{},"Wiz's counter was that informed consent requires accurate information, and a prompt naming ",[14,200,16],{}," when the write goes to ",[14,203,204],{},"authorized_keys"," is not accurate.",[10,207,208],{},"Two facts cut the other way and belong in the same paragraph, because leaving them out would make this tidier than it is. Anthropic had already shipped symlink warnings in Claude Code v2.1.32 on 2026-02-05, which is before Wiz submitted the report, as proactive hardening from an internal review. And current versions, from 2.1.173 onward, do resolve symlinks and warn before writing to sensitive files. So the behaviour was improved regardless of how the ticket was classified.",[10,210,211],{},"You can read that as a vendor correctly scoping its threat model around an explicit trust decision, or as a vendor arguing about labels while shipping the fix anyway. Both readings are available from the same facts. What matters for you is the version number, and the version number is fine now.",[213,214,215],"info-box",{},[10,216,217],{},"Trusting a directory and trusting every path a file in that directory can point at are different decisions. That gap is the whole finding, and it's worth holding onto even after every vendor has patched, because the next agent you install will make the same assumption.",[40,219,221],{"id":220},"why-this-one-is-aimed-at-you-specifically","Why this one is aimed at you specifically",[10,223,224],{},"Most security advice for founders is about your app. This one is about your laptop, and the exposure comes from a habit that vibe coding actively encourages.",[10,226,227],{},"You clone starter templates you didn't write. You fork a repo to see how someone did a thing. You point Cursor at a project a contractor handed over and ask it to explain the codebase. You accept a pull request from someone you've never met. And then you click Approve, repeatedly, because the alternative is reading every diff, which is the thing you got an agent to avoid.",[10,229,230],{},"We see the downstream half of this constantly in scans: credentials that were valid on a developer's machine turning up somewhere they shouldn't be. The usual assumption is that a key leaked through a commit or a bundle. A compromised laptop produces the same result and leaves less evidence.",[40,232,234],{"id":233},"three-checks","Three checks",[236,237,239,245,286],"step",{"number":238},"1",[10,240,241,244],{},[35,242,243],{},"Update the tool."," This is most of the fix.",[246,247,249],"code-block",{"label":248},"Terminal",[250,251,256],"pre",{"className":252,"code":253,"language":254,"meta":255,"style":255},"language-bash shiki shiki-themes github-dark","cursor --version                    # want 3.0 or later\nclaude --version                    # want 2.1.173 or later\n","bash","",[14,257,258,275],{"__ignoreMap":255},[259,260,263,267,271],"span",{"class":261,"line":262},"line",1,[259,264,266],{"class":265},"svObZ","cursor",[259,268,270],{"class":269},"sDLfK"," --version",[259,272,274],{"class":273},"sAwPA","                    # want 3.0 or later\n",[259,276,278,281,283],{"class":261,"line":277},2,[259,279,280],{"class":265},"claude",[259,282,270],{"class":269},[259,284,285],{"class":273},"                    # want 2.1.173 or later\n",[10,287,288],{},"For Amazon Q Developer you want language server 1.69.0 or later, and for Google Antigravity 1.19.6 or later. If you're on Windsurf or Augment, check the current release notes yourself, because neither had shipped a fix when this went public and that may have changed since.",[236,290,292,298,500],{"number":291},"2",[10,293,294,297],{},[35,295,296],{},"Scan a repo for escaping symlinks before you open it."," Clone first, open second. This lists every symlink whose real target sits outside the project folder.",[246,299,300],{"label":248},[250,301,303],{"className":252,"code":302,"language":254,"meta":255,"style":255},"git clone --no-checkout https://github.com/someone/starter-template\ncd starter-template && git checkout\n\nfind . -type l -print0 | while IFS= read -r -d '' link; do\n  target=$(readlink -f \"$link\")\n  case \"$target\" in\n    \"$PWD\"/*) ;;\n    *) echo \"ESCAPES: $link -> $target\" ;;\n  esac\ndone\n",[14,304,305,320,337,344,396,425,441,465,488,494],{"__ignoreMap":255},[259,306,307,310,314,317],{"class":261,"line":262},[259,308,309],{"class":265},"git",[259,311,313],{"class":312},"sU2Wk"," clone",[259,315,316],{"class":269}," --no-checkout",[259,318,319],{"class":312}," https://github.com/someone/starter-template\n",[259,321,322,325,328,332,334],{"class":261,"line":277},[259,323,324],{"class":269},"cd",[259,326,327],{"class":312}," starter-template",[259,329,331],{"class":330},"s95oV"," && ",[259,333,309],{"class":265},[259,335,336],{"class":312}," checkout\n",[259,338,340],{"class":261,"line":339},3,[259,341,343],{"emptyLinePlaceholder":342},true,"\n",[259,345,347,350,353,356,359,362,366,369,372,375,378,381,384,387,390,393],{"class":261,"line":346},4,[259,348,349],{"class":265},"find",[259,351,352],{"class":312}," .",[259,354,355],{"class":269}," -type",[259,357,358],{"class":312}," l",[259,360,361],{"class":269}," -print0",[259,363,365],{"class":364},"snl16"," |",[259,367,368],{"class":364}," while",[259,370,371],{"class":330}," IFS",[259,373,374],{"class":364},"=",[259,376,377],{"class":269}," read",[259,379,380],{"class":269}," -r",[259,382,383],{"class":269}," -d",[259,385,386],{"class":312}," ''",[259,388,389],{"class":312}," link",[259,391,392],{"class":330},"; ",[259,394,395],{"class":364},"do\n",[259,397,399,402,404,407,410,413,416,419,422],{"class":261,"line":398},5,[259,400,401],{"class":330},"  target",[259,403,374],{"class":364},[259,405,406],{"class":330},"$(",[259,408,409],{"class":265},"readlink",[259,411,412],{"class":269}," -f",[259,414,415],{"class":312}," \"",[259,417,418],{"class":330},"$link",[259,420,421],{"class":312},"\"",[259,423,424],{"class":330},")\n",[259,426,428,431,433,436,438],{"class":261,"line":427},6,[259,429,430],{"class":364},"  case",[259,432,415],{"class":312},[259,434,435],{"class":330},"$target",[259,437,421],{"class":312},[259,439,440],{"class":364}," in\n",[259,442,444,447,450,452,456,459,462],{"class":261,"line":443},7,[259,445,446],{"class":312},"    \"",[259,448,449],{"class":330},"$PWD",[259,451,421],{"class":312},[259,453,455],{"class":454},"sns5M","/",[259,457,458],{"class":364},"*",[259,460,461],{"class":364},")",[259,463,464],{"class":330}," ;;\n",[259,466,468,471,474,477,479,482,484,486],{"class":261,"line":467},8,[259,469,470],{"class":364},"    *)",[259,472,473],{"class":269}," echo",[259,475,476],{"class":312}," \"ESCAPES: ",[259,478,418],{"class":330},[259,480,481],{"class":312}," -> ",[259,483,435],{"class":330},[259,485,421],{"class":312},[259,487,464],{"class":330},[259,489,491],{"class":261,"line":490},9,[259,492,493],{"class":364},"  esac\n",[259,495,497],{"class":261,"line":496},10,[259,498,499],{"class":364},"done\n",[10,501,502],{},"Silence means nothing escapes the folder. Any line of output is worth understanding before you let an agent loose in there. A legitimate repo occasionally has a symlink; one pointing into your home directory does not have an innocent explanation.",[236,504,506],{"number":505},"3",[10,507,508,511,512,515,516,519],{},[35,509,510],{},"Read the resolved path, not the filename."," Now that the patched tools show you the real target, the approval box is only useful if you look at it. An edit to a project file that resolves somewhere under ",[14,513,514],{},"~/.ssh",", ",[14,517,518],{},"~/.aws",", or your shell config is the exact shape of this attack.",[521,522,523],"warning-box",{},[10,524,525,526,528,529,531,532,534,535,537],{},"If you think a machine was compromised, changing the tool version doesn't undo it. Read ",[14,527,20],{}," and remove any key you can't account for, check ",[14,530,77],{}," and ",[14,533,81],{}," for lines you didn't add, and rotate the cloud credentials that were sitting in ",[14,536,518],{}," or your environment.",[40,539,541],{"id":540},"what-to-take-from-it","What to take from it",[10,543,544],{},"The interesting thing here isn't the symlink. It's that six independent teams, building broadly the same product, all made the same mistake in the same place: the gap between what the agent decided to do and what the interface said it was going to do.",[10,546,547],{},"Agents are getting a lot of new powers this year, and each one arrives with an approval prompt attached. That prompt is doing an enormous amount of work. It's the only thing standing between \"the model considered doing something\" and \"the thing happened on your machine,\" and GhostApproval is a clean demonstration that the prompt can be accurate about the words while being wrong about the effect.",[10,549,550],{},"So the durable habit isn't scanning for symlinks. It's noticing that approving an action and understanding it are two different things, and that the interface is the only bridge between them.",[552,553,554,561,567,573,579],"faq-section",{},[555,556,558],"faq-item",{"question":557},"Which AI coding tools were affected by GhostApproval?",[10,559,560],{},"Six: Amazon Q Developer, Anthropic Claude Code, Augment, Cursor, Google Antigravity, and Windsurf. Amazon Q, Google Antigravity and Cursor shipped fixes before the July 8 2026 disclosure. Windsurf and Augment had acknowledged the report but not shipped a fix at that point. Anthropic disputed that it was a bug, which is covered in detail above.",[555,562,564],{"question":563},"Am I at risk if I only open my own repositories?",[10,565,566],{},"Largely no. The attack needs a symlink planted in a repository you did not write, so the risk arrives with cloned starter templates, forked projects, a contributor's pull request, or any repo you point an agent at because you wanted it to explain the code. If everything you open you also authored, this is not your problem.",[555,568,570],{"question":569},"What version do I need?",[10,571,572],{},"Amazon Q Developer language server 1.69.0 (May 27 2026), Google Antigravity 1.19.6 (May 22 2026), or Cursor 3.0 (June 5 2026). Claude Code shipped symlink warnings in v2.1.32 on February 5 2026 and versions from 2.1.173 resolve symlinks and warn before writing to sensitive files. For Windsurf and Augment, check your vendor's current release notes, since neither had shipped a fix when the research went public.",[555,574,576],{"question":575},"Does approving a file edit mean the agent already wrote it?",[10,577,578],{},"In two of the six tools, yes. Wiz found that Windsurf wrote modifications to disk before the Accept and Reject buttons even appeared, and that Augment performed symlink reads and writes with no user confirmation at all. In those cases the approval dialog was reporting a decision rather than asking for one.",[555,580,582],{"question":581},"How do I check a repo for this before opening it?",[10,583,584],{},"Clone it without opening your editor, then list every symlink and see whether any of them resolve outside the project folder. The command is in the checks section above. A repo with a symlink pointing at your home directory has no innocent explanation, and finding one takes about ten seconds.",[586,587,588,594,599],"related-articles",{},[589,590],"related-card",{"description":591,"href":592,"title":593},"The full security picture for Cursor, including what it sends where and which settings are worth changing","/blog/is-safe/cursor","Is Cursor Safe?",[589,595],{"description":596,"href":597,"title":598},"Permissions, the trust-the-directory prompt, and what an agent can reach on your machine","/blog/is-safe/claude-code","Is Claude Code Safe?",[589,600],{"description":601,"href":602,"title":603},"The other direction: not your laptop, but the app you shipped, and 18,697 records that walked out of it","/blog/stories/lovable-app-exposed-18000-users","How a Lovable App Exposed 18,000 Users",[605,606,608,612],"cta-box",{"href":455,"label":607},"Scan Your App Free",[40,609,611],{"id":610},"laptop-patched-what-about-the-app","Laptop patched. What about the app?",[10,613,614],{},"A CheckYourVibe scan checks the thing you shipped from the outside, no signup, and reports what a stranger can reach.",[616,617,618],"style",{},"html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sns5M, html code.shiki .sns5M{--shiki-default:#DBEDFF}",{"title":255,"searchDepth":277,"depth":277,"links":620},[621,622,623,624,625,626,627],{"id":42,"depth":277,"text":43},{"id":85,"depth":277,"text":86},{"id":191,"depth":277,"text":192},{"id":220,"depth":277,"text":221},{"id":233,"depth":277,"text":234},{"id":540,"depth":277,"text":541},{"id":610,"depth":277,"text":611},"stories","2026-08-03","Wiz found that a symlink in a cloned repo makes AI coding assistants show one filename in the approval box and write to another, like your SSH keys.",false,"md",[634,636,637,638,639],{"question":557,"answer":635},"Six: Amazon Q Developer, Anthropic Claude Code, Augment, Cursor, Google Antigravity, and Windsurf. Amazon Q, Google Antigravity and Cursor shipped fixes before the July 8 2026 disclosure. Windsurf and Augment had acknowledged the report but not shipped a fix at that point. Anthropic disputed that it was a bug, which is covered in detail below.",{"question":563,"answer":566},{"question":569,"answer":572},{"question":575,"answer":578},{"question":581,"answer":640},"Clone it without opening your editor, then list every symlink and see whether any of them resolve outside the project folder. The command is in the checks section below. A repo with a symlink pointing at your home directory has no innocent explanation, and finding one takes about ten seconds.",null,"ghostapproval, ai coding assistant vulnerability, is it safe to clone a repo cursor, cursor symlink vulnerability, claude code symlink, windsurf security, CVE-2026-50549, CVE-2026-12958, ai agent wrote outside workspace",{},"You approved an edit to project_settings.json. The agent wrote to ~/.ssh/authorized_keys. Six AI coding tools, one decades-old trick, and the version numbers that fix it.","/blog/stories/ghostapproval-ai-coding-assistants","9 min read","[object Object]","BlogPosting",{"title":5,"description":630},{"loc":645},"blog/stories/ghostapproval-ai-coding-assistants",[139,150,161,653],"Access Control","summary_large_image","rUbI6cKbPGOeTBdAZjNe7FJm1DLeC_K_uk7ztvQ1Ecw",1785794502945]