DEV Community

Cover image for Pad an HTML File Past 256KB and Claude Code Desktop's Preview Could Read Your SSH Keys
Gabriel Anhaia
Gabriel Anhaia

Posted on AI-assisted

Pad an HTML File Past 256KB and Claude Code Desktop's Preview Could Read Your SSH Keys


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:

  1. Files under 256KB loaded as data: URLs with a Content Security Policy of script-src 'none' and an opaque origin, so embedded scripts were blocked (Noma).
  2. 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 a file:// origin could use fetch() to read other local files (Noma).
  3. 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."

A bulky orange-tinted document squeezing through an unguarded side door while a small document is stopped at a locked main gate
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() and XMLHttpRequest to file:// from the page, goes back for a retest. The same day, Noma moves the fetch() into a Web Worker created from a blob: 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/*'
Enter fullscreen mode Exit fullscreen mode

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.

A black silhouette at a desk holding a magnifier over an oversized document, with a ring of keys tucked into its pages and an orange thread trailing out of a window
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}`;
}
Enter fullscreen mode Exit fullscreen mode

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.

System Design Pocket Guide: Interviews — 15 Real System Designs, Step by Step

Top comments (0)