.png)
Eighteen Minutes, 2.2 Million Targets
It's been a hard week for GitHub. Yesterday GitHub confirmed it had been breached. The attackers reportedly pulled data from roughly 3,700 internal repositories, and the entry point was a poisoned VS Code extension running on a GitHub employee's machine.
The day before, Nx Console (a popular VS Code extension for the Nx monorepo build system) showed the mechanism that made the breach possible. When it comes to security, VS Code extensions are the Wild West.
What happened with Nx Console
On May 18, version 18.95.0 of the Nx Console extension (2.2 million installs, verified publisher badge) was uploaded to the Visual Studio Marketplace at 12:30 UTC. Microsoft did not flag the upload. The Nx maintainer did not receive the marketplace's upload notification email until 12:36 UTC, six minutes later. The compromised build was unpublished at 12:47 UTC and Microsoft fully registered the takedown at 12:48 UTC. Total exposure on the Visual Studio Marketplace: about eighteen minutes.
The same build also went out on OpenVSX. Earlier reporting said OpenVSX was unaffected, but the maintainers' updated advisory has it live there from 12:33 UTC to 13:09 UTC. That is roughly thirty-six minutes, twice the Visual Studio Marketplace window.
According to the advisory, anyone who had Nx Console installed with auto-update enabled during those windows should assume they were compromised, and anything on disk (tokens, secrets, SSH keys, anything credentials-adjacent) needed to be rotated.
The root cause was a contributor's GitHub token that had been scraped in an earlier supply chain attack and then used to publish the malicious release. The publishing pipeline accepted the stolen token at face value, which was all the attackers needed.
Auto-update is the actual problem
Every popular extension marketplace ships with auto-update on by default. VS Code, Cursor, the whole lineup. The reasoning makes sense in isolation, because most developers never update anything manually, so leaving it off means a long tail of editors running stale, vulnerable code.
The trade-off stops making sense once you account for hostile/compromised publishers. Auto-update gives an attacker who controls a release a direct push channel into every machine running that extension. Marketplaces don't impose any review gate or waiting period between when an update is published and when installed clients pull it in.
The check itself doesn't have a single fixed interval. There's a 12-hour fallback timer in extensionsWorkbenchService.ts (1 hour on Insiders with a pending product update), an immediate check on startup once the workbench is idle, and several event-based triggers (product update check, allowlist policy change, connection going from metered to unmetered, the auto-check toggle being turned on). On top of all that, there's a separate path that catches updates as a side effect of any other marketplace interaction. When VS Code queries the gallery for any reason (Extensions sidebar visible, marketplace search, recommendation prompts, background subsystems that fetch gallery metadata), it syncs the result against installed extensions and, if any are now outdated, fires eventuallyAutoUpdateExtensions, which installs through a delayer throttled to 1 second. With extensions.autoUpdate on (the default), the install happens in the background with no prompt.
The eighteen-minute window in the Visual Studio Marketplace caught any running editor that did a gallery interaction touching the Nx Console listing during those minutes: sidebar visible, marketplace search, recommendation lookup, or any of the background subsystems that fetch gallery metadata. Across timezones and working hours, on an extension with 2.2 million installs and an Extensions sidebar that many developers leave open, that population is much larger than a periodic timer alone would suggest. During our own testing of these features we’ve seen auto-update firing within minutes of publishing a new version, which matches the gallery-sync path rather than the 12-hour timer. The OpenVSX window did the same thing for roughly thirty-six minutes.
The maintainers' timeline also shows the detection gap that auto-update opens up. Six of those eighteen minutes passed before the maintainer even saw the marketplace's upload notification. Auto-update was pulling the malicious version onto user machines during those six minutes, before any human on the publisher side could have known to act.
Why this keeps working
The Nx case is not the first time this has happened to a VS Code extension. In November 2025, AsyncAPI's compromise was the first-blood event of the Shai-Hulud 2.0 worm wave. The attackers had stolen both AsyncAPI's npm and OpenVSX publishing tokens, which had been sitting in GitHub repository secrets for years, and used them to push malicious AsyncAPI npm packages and a malicious version of the AsyncAPI VS Code extension on OpenVSX. At least one developer reported being infected through the extension on the project's own issue tracker before maintainers could deprecate the affected releases. The same mechanism was at work: a stolen credential, used to push malicious code to an auto-updating user base over a short window.
A few things are stacked against defenders here:
- High install counts and verified publisher badges signal trust, and trust is what attackers want to hijack. Popular extensions are already on millions of machines, so a compromised release pushes malicious code into all those existing installs automatically, without needing any social engineering against new users.
- Extensions ship as interpreted JavaScript rather than compiled binaries. The malicious Nx Console payload was 2,777 bytes injected into a minified file, which then fetched a 498 KB obfuscated dropper from an orphan commit in the official
nrwl/nxrepository. EDR has no signature for a minified JavaScript diff against an extension users already trust. - Maintainer credentials are easier to phish or scrape than they should be. The Nx case started with a token from a separate compromise, used downstream against a trusted target. The AsyncAPI case started with an OpenVSX token that had been sitting in a GitHub repository secret for years.
- The community is getting fast at catching this, but eighteen minutes of automatic distribution is enough to do damage, and that's just on one of the two marketplaces hosting the extension.
Once it lands on a machine, the marketplace can't help you
Marketplaces also have no way to recall an extension once it has been installed. Pulling the listing stops new installs, but anyone who already grabbed the malicious version keeps running it until they update to a clean release or uninstall manually. Auto-update will eventually pull affected users onto the patched version once the maintainer ships one, but the marketplace has no kill-switch and no way to roll an install back any faster.
Wiz Research's analysis of the Shai-Hulud 2.0 long tail shows this dynamic in practice. The malicious AsyncAPI VS Code extension (v1.0.1) was pulled from OpenVSX on November 26th, 2025, but the registry rolled back to the previous clean version (v1.0.0). Installed copies on v1.0.1 saw no newer version available, so auto-update never triggered. The extension kept running on developer machines and exfiltrating credentials for nearly a month, accounting for over 90% of long-tail infections from the campaign and roughly 100-200 new compromised repositories per day from November 25th through December 24th. The flow only stopped when AsyncAPI published a clean v1.1.0 on December 24th (after Wiz Research reported the issue to them the day before), at which point IDEs auto-updated to it and daily new compromises dropped to a handful within days.
Microsoft also pulls listings silently. There is no notification to users who installed during the exposure window. Their editor doesn't surface a "you may have installed a compromised version, please check your machine" message. Unless you were following security news on the day of the incident, your only signal might be that the extension stopped showing up in marketplace search results. The Nx Console repo already has an issue from a developer asking, in good faith, why the extension had vanished from the marketplace. Most users wouldn't think to ask.
The combined effect is that the marketplace's only effective response is preventing new installs of a known-bad version. For developers who already pulled the malicious build during the window, the cleanup falls entirely on them, and depends on them learning about the incident through some channel other than the marketplace itself.
What to actually do
If you had Nx Console installed with auto-update during those windows on May 18, the advisory's guidance is the right starting point: rotate tokens, secrets, SSH keys, and anything else reachable from the affected machine. Anyone who ran the OpenVSX build is in scope for the longer thirty-six-minute window rather than the Visual Studio Marketplace one.
For the broader pattern, the cheap intervention is to put a delay between when a release is published and when it is allowed to install. Aikido's Device Protection ships with a default 48-hour hold on new package and extension versions, which would have caught Nx Console (and the recent durabletask PyPI compromise) without anyone having to identify it as malicious first. As an individual without a vendor solution, the closest you can do is disable automatic extension updates in VS Code and update on a deliberate schedule instead. It is slower, and you will be behind on legitimate releases by a day or two. That is the price of not running other people's freshly published code on your laptop the moment they publish it.
VS Code already supports the underlying policy surface to make this an org-level setting. The extensions.allowed enterprise policy lets administrators constrain which publishers, extensions, and even specific versions can be installed across managed devices. A cooldown option on top of that (something like "only auto-install versions older than N hours") would slot into the same setting. There is no good technical reason it doesn't exist already, and given how often these incidents are happening on the marketplace it ships with, it should. The same goes for Cursor and any other VS Code-based editor: if you already let organizations enforce an allowlist, also let them enforce a cooldown.
The GitHub story will run for weeks because the victim is famous, and the same machinery underneath is going to keep producing breaches. As long as marketplaces auto-push updates from accounts that can be compromised in any of a dozen ordinary ways, every popular extension is one stolen token away from turning into a worm.
Aikido Device Protection
If you want a cooldown today without waiting for VS Code or Cursor to ship one natively, Aikido Device Protection is what we recommend. It enforces a configurable minimum-age policy (48-hour default) on package and extension installs at the device level, so the same policy covers any VS Code-based editor your developers use.

