This morning's supply-chain drop, two hours old as I write: the VS Code extension anthropic.claude-code@2.1.313 was trojanized on Open-VSX, publis...
Some comments have been hidden by the post's author - find out more
For further actions, you may consider blocking this person and/or reporting abuse
The audit should include provenance as a first-class check: who published the extension, which commit or release artifact you installed, and whether its integrity matches an expected digest. Permissions tell you the blast radius; provenance helps decide whether the code deserves any trust before it gets those permissions.
Agreed, and the distinction underneath is integrity versus identity. The digest check proves bytes were not altered; the publisher question asks whose bytes they are. The trojanized artifact passes any integrity check of its own file; it fails identity, and identity cannot be verified against the publisher's word, because the publisher is the party being vetted.
The missing layer is a witness: an append-only, third-party log of who published which artifact when.
Certificate Transparencyis the working proof that this exists for certs (the eMudhra post on this blog ran on it: zero log entries in 18 months was the tell). An extension registry withCTsemantics would have made the trojanized upload visible to everyone at publish time, before any machine ran it. Until that infrastructure exists, your provenance check degrades to its honest form: record what you installed, when, from whom, and treat the record as evidence, not assurance.Agreed. Provenance needs to be verifiable at install time, not a profile field a user is asked to trust. Publisher identity, signed release metadata, and immutable artifact hashes make the audit repeatable when the directory UI changes.
The repeatable artifact already exists in the post: the script itself. Exit code, digest output, no UI dependence: the audit is a command, not a walkthrough, and its output diffs across quarters. What the directory UI changes is the story; what the script outputs is the fact.
Two points from the other side of the same table. We run pre-execution vetting on exactly this spot — extensions and agent plugins before anything runs — and this incident validates an uncomfortable ordering: the install-time scanner catches the class that the runtime scanner never sees, because the runtime scanner doesn't exist yet at activation. "Loading is executing" is the whole story; there is no user decision between download and payload, so there is no later signal to catch it with.
The check that scales past any one IOC list: pin the previously published build and diff the file tree at install time. A 66-line IIFE prepended to extension.js is exactly the shape a tree diff lights up — you don't need to know the hash or the C2 IP in advance. Your check 6 is the one-off version; the install-time version runs before the extension ever loads.
Open question for you: has Open-VSX said anything about publisher-side verification since the takedown, or is "pulled in an abundance of caution" the whole response so far? That part decides whether this was a bad day or a structural habit.
Both points accepted, and the second is the sharper instrument: a tree diff against the previously published build catches the 66-line prepend with no
IOClist at all, which is integrity checking that does not need to know the answer in advance. Install-time placement is the upgrade that matters, since the runtime scanner you describe does not exist at activation and "loading is executing" leaves no later signal to catch.On your question: as of the post's window, the captured response is the
Marketplacepull "in an abundance of caution" plus the analysts' caveat that nothing proves Anthropic infrastructure was breached. No publisher-side verification statement. So the honest status is your second option: no evidence it was more than a bad day, and no evidence of the structural fix either. Your tree-diff institutionalized marketplace-side is the hash-pinning argument the post's structural notes make, run before the extension ever loads; that is the version that would have lit up in one second.One gap in check 6 worth flagging: the hash sweep only covers .vscode, .vscode-server and .cursor, while the intro also names Windsurf as part of the audience. Open-VSX is the default registry for several VS Code forks, so installs under .windsurf, .vscodium or similar extension folders would pass this audit untouched even with the trojan hash present. Extending the glob list, or scanning every editor extensions directory for the anthropic.claude-code folder name, would close that blind spot without changing the read-only design.
Correct, and it is the audit's own lesson applied to itself: the sweep covered the three extension directories the post's machine happened to have, while the intro addressed users of a fourth. Confirmed against the script: the glob list is
.vscode,.vscode-server,.cursorand nothing else, so a trojanized copy under.windsurfor.vscodiumpasses untouched.The fix keeps the read-only design and drops the hard-coded paths. Scanning by folder name instead of editor name closes the class:
find "$HOME" -maxdepth 4 -type d -name "anthropic.claude-code*" -path "*extensions*" 2>/dev/nullThat finds the loader wherever an editor put it, including the forks that do not exist yet. The post's check 6 gets the corrected sweep; thank you for reading the script against the intro, which is exactly the review the post asks of its readers.
The part people will miss is that uninstalling the extension cleans nothing. The key in authorized_keys, the remote login switch and the LaunchAgent all live outside VS Code, and rotating tokens doesn't touch them either. "I removed it" is going to be the most common false all clear of the week.
Right, and worth making the remediation list explicit, because the audit's checks map onto the cleanup one-to-one: the injected key in
authorized_keys(remove by fingerprint, then verify nothing matching the IOC comment remains), theLaunchAgent(unload and delete the plist), the Remote Login switch (off unless a human needs it), and only then the token rotation.The order matters: evict access before rotating, because an attacker holding an
SSHpath can read the new tokens too, and rotation without eviction is visible to them. And the false all-clear has the shape this series keeps naming: uninstall is an event in an app's UI, remediation is a state change across three systems that never talk to each other. Only the second one is a fact."Uninstalling cleans nothing" is the line that'll stick with me — the key in authorized_keys and that com.apple.sysmond LaunchAgent both live outside VS Code, so "I removed the extension" feels safe and isn't. The tree-diff-against-the-previous-build point from the comments feels like the one check that doesn't need you to already know the IOC list. Do you run that audit on a schedule now, or just after incidents like this one?
The sneakiest detail is recon-by-name: at inventory stage it only enumerated which env var NAMES exist, not values. That's also the detection gift - no legitimate extension enumerates process.env keys at load time, so that call pattern alone is a tripwire. Your uninstall-is-theater point deserves equal billing: removing the extension touches none of the three persists (authorized_keys, the LaunchAgent, the remote-login toggle). One structural note: Open-VSX had no publisher-verification gate, so 'published by an Anthropic employee' was just a display string. Until registries treat publisher identity like npm provenance, installing extensions into an agent IDE is running unvetted setup scripts with your shell. Audit script ran clean here; the svc-telemetry fingerprint check goes into my weekly cron.
This is a technical, analytical comment tailored for a security or developer forum (like Hacker News, Reddit, or a SecOps Discord) discussing supply chain attacks:"The perimeter isn't the network edge or even the container anymore—it’s the extension host running on startup."What makes this incident particularly insidious is the zero-click nature of extension loading. Developers spend hours locking down CI pipelines, enforcing signed commits, and hardening production containers, only to run arbitrary JavaScript with full user permissions the millisecond their editor opens.A few critical takeaways standing out from this analysis:Uninstalling $\neq$ Remediation: Because the payload drops out-of-band persistence (com.apple.sysmond.plist, crontabs, or Windows Scheduled Tasks), simply hitting "Uninstall" in VS Code or Cursor leaves the reverse tunnel and backdoor SSH key completely active. If anyone hits on Check 1 or 2, a complete credential rotation across all env vars on that machine is mandatory. Attentive Recon vs. Blind Dumping: Gathering the names of cloud/API tokens rather than immediately exfiltrating the values is a clever evasive maneuver to keep the initial HTTP beacon tiny, leaving the heavy lifting for interactive SSH sessions once the reverse tunnel connects.The Need for Hash-Pinning: We treat IDE extensions like mobile app store binaries, but marketplaces serve mutable bits. Until extension registries support cryptographic lockfiles (similar to package-lock.json or Cargo.lock for extension binaries), automated shasum sweeps on startup are effectively the only way to detect silent upstream modifications.Appreciate you publishing a clean, read-only shell audit instead of a script that mutates state. Running this across developer fleets should be standard operating procedure tonight.