The preview could read your home folder
On October 14, Noma Labs disclosed SneakPeek, a flaw in Claude Code Desktop's preview pane. An HTML file padded past 256 KB, disguised as a test coverage report, was loaded from disk with no script policy, and its JavaScript read SSH keys, AWS secrets, kubeconfigs and Claude's own OAuth tokens from the developer's home folder, then sent them out with a background beacon. Anthropic scored it CVSS 8.7 and fixed it in August. In Bromure Agentic Coding, pages render in a separate, disposable VM that holds none of your files, and the agent's own home holds stand-ins instead of keys.
The repository looked normal. The file was a coverage report. The developer asked the agent to look at it, and the preview pane did the rest.
A developer clones a repository and asks Claude Code to look at the test coverage report in it. It's a routine request. The report is an HTML file. When Claude reads or edits an HTML file in Claude Code Desktop, a preview pane renders it next to the conversation, without asking. This report carries a script. Within seconds, the script has read the developer's SSH private key, AWS keys, npm and GitHub tokens, Kubernetes credentials and Claude's own OAuth tokens, and sent all of it to a server the attacker controls.
That is the proof of concept Noma Labs published on Wednesday, October 14, under the name SneakPeek. CSO Online covered it the same day. Anthropic scored the flaw CVSS 8.7 (High) and treated its impact as equivalent to code execution on the host. Noma reported it on July 4. The fix shipped through auto-update on August 17 in Claude Desktop 1.30096.1, after a first patch was bypassed. A GitHub advisory and a CVE are pending.
The bug is fixed. The setup that made it worth finding is still on most developer laptops: something that renders untrusted content, running as you, next to everything you own.
Three defenses and a size limit
The preview pane is a Chromium panel inside the Electron app. Noma counted three defenses around it, and found a gap in each.
A script policy for small files. Files under 256 KB were
loaded as base64 data: URLs with a Content Security Policy, a list of rules
that tells the browser which scripts may run. That policy blocked scripts. Files
over 256 KB were written to a temporary file and loaded from disk as a
file:// URL, and the policy didn't apply to that path. Padding a report past
256 KB is trivial. Real coverage reports are often that big.
An origin with the run of the disk. A page's origin decides what it may
read. A data: URL gets an opaque origin that can read nothing. A page loaded
from file:// got, in Noma's words, "same-origin access to the victim's entire
local filesystem." The script worked out the home folder from the temporary
file's path and read files with fetch() and XMLHttpRequest.
A filter that wasn't switched on. The pane had a request filter meant to
block file:// access. It engaged for proxied dev servers and stayed off for
static HTML rendered from disk.
To get the data out, the script called navigator.sendBeacon(), a web API that
fires a one-way HTTP POST in the background. Nothing in the app limited where
the pane could send requests. Noma's write-up adds the developer's real IP
address, collected through WebRTC.
Anthropic's first patch, on July 24, blocked fetch() and XMLHttpRequest to
file:// paths from the page. The same day, Noma moved the reads into a Web
Worker started from a blob: URL, a context the patch didn't cover, and all
three defects worked again. The final fix in August closed the policy gap, the
file:// loading and the worker path together. Noma's researcher Eli Ainhorn
summed it up for CSO: "The protections were real, they just didn't cover every
path."
The agent didn't do it
SneakPeek uses no prompt injection. The agent read a file you asked it to read, and the app around it rendered that file. The script ran in the preview's rendering process, with the app's access to your disk.
Crystal Morin of Sysdig told CSO that agent-level logs might not capture this execution, so defenders would have to watch file reads and outbound connections at the runtime layer. Your session transcript would show a harmless request and a harmless answer.
A careful model, strict permission rules and a human approving each command would have changed nothing. The renderer sat on the Mac, running as you, next to your home folder and every credential you've collected there.
Ainhorn's advice to CSO goes at that second part: keep long-lived credentials off developer machines, and use scoped, short-lived tokens.
The same report in a Bromure machine
In Bromure Agentic Coding, each agent runs inside its own Linux VM on your Mac, called a machine, and the embedded browser that renders web pages runs in a second, disposable VM beside it. Clone Noma's repository into a machine and ask Claude Code to look at the coverage report.
Reading the report is reading text
The agent reads coverage/index.html the way it reads any file: as text in
its context. Nothing in the chat renders it. If you click the file in the
Files pane, the detail view shows HTML as highlighted source, the same way
it shows a .py or a .json. No script on the page runs. Files over 512 KB
aren't previewed at all. A report padded past 256 KB to dodge a script policy
shows up as 300 KB of markup.
Rendering happens in another machine
When you do want to see the report rendered, the agent serves the folder from its machine and opens it in the browser pane, at the machine's IP address. The browser is a full Chromium, and it runs in its own sidecar VM. As the manual puts it, "a file the agent writes in its home directory is not on the browser's disk."
The script runs and finds nothing to read. The page's origin is
http://192.168.64.x:8000, an ordinary web origin. Chromium doesn't let it read
file:// paths. A script that got past that, through a worker or a browser
bug, would land on an ephemeral copy-on-write clone of the browser
image: no ~/.ssh, no ~/.aws, no kubeconfig, no OAuth token, none of your
projects. The home folder it derives from a temporary path belongs to a
Chromium VM with nothing in it. The beacon goes out empty.
By default the browser keeps no profile. Stay signed in to websites is off, so cookies and logins vanish when the browser closes, and a hidden browser is torn down after five minutes. WebRTC stays off unless you allow the camera or microphone in the machine's Browser settings, so the IP-address trick has nothing to call.
The agent's home holds stand-ins
Now point the same script at the agent's own VM. Say the repository's test command writes and opens the report itself, so the code runs where the agent works. Walk through Noma's list in that home folder:
- SSH private key. There isn't one.
SSH_AUTH_SOCKis backed by an agent bridge to the Mac. The VM can list identities and ask for signatures, and the private key bytes never leave the host.ls ~/.ssh/id_*finds nothing. - AWS.
~/.aws/configpoints at a helper that hands out the real access key ID with a fake 40-character secret. The Mac re-signs each request with the real secret on its way to AWS. - Kubernetes.
~/.kube/configis synthetic.kubectltalks to the proxy on the Mac, which holds the real cluster credentials. - Docker, git and npm tokens.
~/.docker/config.jsonand~/.git-credentialshold stand-ins shaped like the real thing. Tokens you import, from a.envor by hand, become stand-ins scoped to their hosts. - Claude's OAuth tokens. They stay on the Mac. The agent runs with a placeholder key, and the proxy puts the live access token on the wire for requests to Anthropic.
The stand-ins are also tripwires. The proxy on the Mac scans every request
leaving the machine, headers and body, for a stand-in headed to a host it
wasn't minted for. A beacon carrying the brm-docker-… password or the fake
GitHub token to the attacker's server gets HTTP 451. Nothing is forwarded, the
VM pauses, and an alert titled This environment may have been compromised
opens on the Mac with the token preview, the credential's name, the host it
belongs to and the host it was sent to. The detection lands as a red row in the
Security Timeline. Set Unmatched traffic to Deny in the machine's
Outbound connections firewall, and a connection to a host the project
doesn't use is refused before any of that.
That covers Morin's point about the runtime layer. The checks sit on the Mac, on the wire, outside the agent and outside anything rendering inside the VM.
The fix and the next preview
Anthropic fixed SneakPeek in August and Noma retested it. Six weeks passed between the report and the fix, and the first patch fell to a Web Worker the same day it shipped. Renderer fixes tend to go that way. A browser engine has many ways to start a script and many ways to make a request, and each defense has to cover all of them.
Coding tools keep adding panes that render things: HTML previews, Markdown with embedded content, notebook outputs, dev servers, screenshots of the app the agent just built. Each one is a renderer pointed at content the developer didn't write, from repositories they cloned a minute ago. On a laptop, each one runs next to the home folder.
In Bromure, the pane that renders pages runs in a VM that holds none of your files, and the machine where the agent works holds stand-ins. A renderer bug there exposes a disposable Chromium and a set of strings no server will accept.
Source, not a page
The agent reads HTML as text, and the Files pane shows it as highlighted source. Opening a report to review it runs none of its scripts.
A browser with nothing on its disk
Pages render in a separate, disposable Chromium VM. The agent's files and your Mac's files aren't on its disk, and it forgets everything when it closes.
A home folder full of stand-ins
No SSH private key, a fake AWS secret, a synthetic kubeconfig, stand-in registry and git tokens. The real credentials stay encrypted on the Mac.
An alarm on the wire
A stand-in sent to a host it wasn't minted for gets a 451. The VM pauses, an alert names both hosts, and the Security Timeline keeps the record.
Look at the report
"Look at the coverage report" is about as ordinary as requests to a coding agent get. Developers will keep asking it about files in repositories they didn't write, and tools will keep rendering those files to be helpful.
Ask in a Bromure machine, where the page renders in a VM with nothing to read and the agent's keys are stand-ins. For more on stand-ins and the alarm they raise, read The agent leaked its own key and The key was your .env file.