超過 256KB 的 HTML 檔可令 Claude Code Desktop 預覽功能讀取 SSH 私鑰
Pad an HTML File Past 256KB and Claude Code Desktop's Preview Could Read Your SSH Keys
Noma Security 的 SneakPeek 研究指出,Claude Code Desktop 的 HTML 預覽處理超過 256KB 的檔案時會改用 file://,令內嵌腳本可能讀取本機憑證。修正已透過自動更新推送至 Claude Desktop 1.30096.1 或之後版本;文章指出,目前沒有來源報告野外攻擊。作者並提醒,按檔案大小分流時,各程式路徑都應套用相同的安全檢查。
- Book: System Design Pocket Guide: Interviews
- The series: Pocket Guides for Developers — 8 short books on agents, RAG, prompts, observability, events, databases and system design — the whole series
- My project: Hermes IDE | GitHub — an IDE for developers who ship with Claude Code and other AI coding tools
- Me: xgabriel.com | GitHub
A coverage report is the most boring file in a repo. You clone a project, ask Claude to look over the test coverage, and the HTML report opens in the app's preview pane. Until a fix shipped on August 17, that was enough, according to Noma Labs, for a script hidden in the report to read your SSH private key, your AWS credentials and your GitHub token, and send them to someone else's server.
Noma Security published the research, which it calls SneakPeek, on October 14, 2026. The bug was in the HTML preview of the Claude Code Desktop app, not in a model. Anthropic scored it CVSS v4.0 8.7 (High) and, per Noma, "treated the impact as equivalent to code execution on the host." The fix reached users through auto-update in Claude Desktop 1.30096.1, per Noma's timeline.
This one is already patched. It is still worth five minutes: confirm you're on the fixed build, and learn the shape of the bug, because you can ship the same one in your own code.
Three gaps that only lined up above 256KB
Claude Code Desktop has a Browser pane. Anthropic's docs say it can "open static HTML files, PDFs, images, and videos from your project", next to the dev servers Claude starts to check its own changes. Noma's write-up describes how that pane loaded an HTML file, and the size of the file decided which path it took:
- Files under 256KB loaded as
data:URLs with a Content Security Policy ofscript-src 'none'and an opaque origin, so embedded scripts were blocked (Noma). - Files over 256KB were written to a temp file and loaded as
file://URLs. That path never got the CSP, and in Chromium a page on afile://origin could usefetch()to read other local files (Noma). - A request filter did block
file://reads, but only when the pane was proxying a dev server. It was not active when the pane rendered a static HTML file from disk (Noma; CSO Online).
The CSP, the opaque origin and the request filter all existed, and the big-file path got none of them. The researcher who wrote it up put it this way to CSO Online: "The protections were real, they just didn't cover every path."

Small files went through the guarded door. Files over 256KB took the side entrance.
How the attack would have run
Noma's proof of concept needs no exploit kit, only a repo that looks normal:
- The attacker publishes a repository with a coverage report padded past 256KB, with a script inside.
- You clone it and ask Claude to review a file, such as that report.
- The preview pane renders it, the script runs, and Noma says no permission prompt appears.
- The script reads files such as SSH keys, AWS keys, npm and GitHub tokens, Kubernetes credentials, Docker registry auth and Claude's own OAuth tokens, and ships them out with
navigator.sendBeacon()(Noma).
Noma's TL;DR adds that the payload could also get your real IP address through WebRTC. Neither Noma's post nor the press release reports exploitation in the wild.
The patch had a bypass
The timeline is the part that should make you careful about "we patched it" in your own work. From Noma's post and the press release:
- July 4: Noma reports the chain with a proof of concept.
- July 9: Anthropic triages it and scores it 8.7.
-
July 24: Anthropic's patch, which blocked
fetch()andXMLHttpRequesttofile://from the page, goes back for a retest. The same day, Noma moves thefetch()into a Web Worker created from ablob:URL, a context the filter didn't cover. - Early August: Anthropic confirms the bypass and starts a deeper fix.
- August 17: The fix ships in Claude Desktop 1.30096.1 and later, through auto-update.
Noma's own words on the process are kind to Anthropic: the company "took Noma's disclosure seriously from the start," per the post. Anthropic did not immediately respond to CSO Online's request for comment. CSO reports that Anthropic is preparing a GitHub Security Advisory and has requested a CVE. Noma's post lists both as pending, though its press release says they were posted in October. As of October 14, none of these sources gives a CVE ID, so check GitHub's advisory database before you go looking for one.
What to do now
Check the Desktop app version. The bug and the fix are in the Desktop app's preview renderer, so the version that matters is Claude Desktop's. Anthropic's docs say to open Claude → Check for Updates on macOS or Help → Check for Updates on Windows, and on Linux to update through your package manager. You want 1.30096.1 or later. If a machine on your team has had auto-update turned off since summer, that is the one to look at.
Think about what that machine had on disk. If someone on your team previewed HTML from repositories they didn't trust in the Desktop app before August 17, rotating the credentials that lived on that machine is a cheap precaution. Nobody has reported an attack, so treat it as hygiene.
Shrink what a stolen file is worth. CSO's sources recommend scoped, short-lived tokens instead of long-lived credentials on developer machines, and watching file reads and outbound connections at the runtime layer, because agent logs may not see this kind of activity (CSO Online). An expired token is useless wherever it leaks from.
Look before you hand a stranger's repo to any agent. This lists HTML files over 256 KiB in a fresh clone, skipping node_modules:
find . -name '*.html' -size +256k \
-not -path '*/node_modules/*'
A big HTML file isn't proof of anything. Coverage reports and bundled docs get large on their own. It just tells you which files deserve a look in a plain text editor first.

The fix is in. The habit of checking what an untrusted repo carries is still yours.
The bug to look for in your own code
Strip away Electron and the bug is a branch on size where only one side carries the security checks. Here is a simplified sketch of the pattern Noma describes:
// Sketch of the pattern, not Anthropic's code
const LIMIT = 256 * 1024;
function previewUrl(html: string, tmpPath: string) {
if (Buffer.byteLength(html) < LIMIT) {
// CSP blocks scripts, origin is opaque
const b64 = Buffer.from(html).toString("base64");
return `data:text/html;base64,${b64}`;
}
// Big file: written to disk, loaded from file://
// No CSP here, and the origin can read local paths
return `file://${tmpPath}`;
}
Size branches usually exist for performance, and the branch for big inputs tends to inherit none of the guards. Upload handlers that stream large files past a validator have the same shape. Black Duck's head of cybersecurity governance told CSO Online that "relying on size-based thresholds or client-side filters for security boundaries is inherently fragile."
When you review a branch like this, ask one question: does every path out of this function pass through the same checks? If the answer depends on input size, file type or which mode the app is in, the checks belong after the branch, not inside one arm of it.
If this was useful
SneakPeek started as a design problem: two paths into the same pane, and only one of them inside the trust boundary. The System Design Pocket Guide: Interviews walks through 15 real systems step by step, and deciding where data crosses a boundary, and what guards that crossing, comes up again and again. It's one of 8 short books in Pocket Guides for Developers, covering agents, RAG, prompts, observability, events, databases and system design.
I also build Hermes IDE, a source-available desktop app for macOS, Windows and Linux that runs Claude Code, Codex and other agents side by side, and each task can get its own branch and worktree. Weigh that with my bias in mind. It's on GitHub.
來源:DEV Community · dev.to
