Your Editor Extensions Can Run Anything: Lock Them Down

Every extension you install in your editor is code that runs on your machine as you, with access to everything you can access. Most of us have a dozen or more installed and haven't looked at the list in months.
A recent incident is a good reminder of why that matters. Let's take a look at what happened, then go through listing, cutting and pinning your extensions, and where to look if one ever goes bad.
What happened
On 29 October, a tampered version of a popular AI coding extension was available in an editor's extension marketplace for about 11 hours. The tampered versions weren't built by the extension's actual developer.
According to the marketplace's security notice, anyone on macOS or Windows who installed or updated to one of the two affected versions in that window got an attacker's SSH key added to their authorized_keys file. They also got persistence scripts that try to open a reverse SSH tunnel roughly every 60 seconds, and a beacon sending back details about the machine (hostname, username, OS, Git email, public IP and the names of environment variables).
Uninstalling the extension wasn't enough to clean any of this up, because the persistence scripts keep running on their own.
To be fair to everyone involved, the notice says there's no evidence that any other extensions or other versions were affected, and the attacker's server has since been taken offline. So this isn't a reason to panic about every marketplace out there. It's a very good reason to look at what you've got installed, though.
What an extension can actually do
VS Code extensions (and extensions for the editors built on VS Code) are Node.js code. Once they're loaded, they run with your user's permissions, so they can read and write your files, run shell commands, make network requests and read your environment variables.
There's no prompt asking whether an extension is allowed to do any of this. If it's installed and enabled, it can.
In this case, Phorion's technical write-up found the malicious code had been added to the top of the extension's bundle, so it ran as soon as the editor loaded the file. That's before the extension's own activate function runs, which means you didn't even need to open or use the extension for it to fire.
AI extensions are a particularly good target, since they tend to sit right next to your API keys, your repos and your terminal. The payload here only collected the names of environment variables, not their values (Phorion found it grabbed up to 40 names matching things like KEY, TOKEN, SECRET and AWS). Saying that, a list of names still tells an attacker which keys are worth coming back for.
List what you've got
Let's start by seeing what's installed. VS Code's CLI can list every extension along with its version:
code --list-extensions --show-versionsYou'll get one line per extension, in publisher.name@version format.
If you use an editor built on VS Code, it'll usually ship its own command line tool with the same flags, so swap code for that. It's worth a quick look at its --help first, just to be sure.
Once you've got the list, go through it and note two things. First, which extensions hold keys or tokens, or run an agent that can edit files and run commands, because those are the ones that matter most.
Second, note where they came from. VS Code installs from the Visual Studio Marketplace, but editors built on VS Code often use a different marketplace, so it's worth knowing which one you're trusting.
Cut the ones you don't use
This is the biggest single win, since every extension you remove is one fewer that can run code as you.
code --uninstall-extension publisher.nameBe honest with yourself here. The theme you tried once, the language support for something you haven't written in years, the second AI assistant you installed to compare with the first... bin them.
If you're not sure whether you still need something, right-click it in the Extensions view and choose Disable for a week. VS Code won't activate a disabled extension, and if you haven't missed it by the end of the week, uninstall it.
Pin the ones that matter
By default, VS Code updates your extensions automatically. Most of the time that's what you want, since it's how bug fixes and security fixes reach you. But it also means a bad update can land on your machine without you doing anything.
For the handful of extensions that hold keys or run agents, I'd turn off auto-update and update them on purpose.
Turn off auto-update for one extension
Right-click the extension in the Extensions view and untick Auto Update. Everything else carries on updating as normal.
Turn off auto-update for everything
If you'd rather update everything by hand, add this to your settings.json:
{
"extensions.autoUpdate": "off"
}
In current versions of VS Code this setting takes "on" or "off" (older true and false values get migrated for you). With it set to "off", you can still switch auto-update back on for individual extensions using the same Auto Update toggle.
Editors built on VS Code don't always pick up changes like this straight away, and some still expect false instead of "off". If your editor isn't VS Code, check which values its Settings UI offers.
It's also worth knowing that recent versions of VS Code wait before auto-installing an update. The extensions.autoUpdateDelay setting defaults to 12 hours after an update is published, which gives the marketplace time to pull a bad version before it reaches you. This doesn't apply to a short list of publishers VS Code itself treats as trusted (Microsoft and GitHub, for example).
Editors built on VS Code may not have this setting at all. Some have their own version of it, usually called a cooldown, but it can be switched off by default, so it's worth searching your editor's settings for it.
Install a specific version
If you want to stay on a version you know is good, install it by version:
code --install-extension [email protected]Installing by version like this pins the extension, so VS Code won't auto-update it until you update it yourself.
You can also pick a version from the UI by right-clicking the extension and choosing Install Specific Version. If you go that way, check the Auto Update toggle is unticked afterwards.
The downsides
Pinning holds back security fixes too. If there's a known bug in the version you've pinned, it stays there until you remember to update, so this only works if you actually check your pinned extensions now and then.
And pinning would only have protected people who already had a good version installed. If you installed or updated the extension during those 11 hours, pinning wouldn't have helped, which is why it's worth knowing what to look for.
Check for what a bad one leaves behind
The marketplace's notice includes quick checks for this incident's SSH key. They only read files, so they're safe to run.
macOS
grep -qs svc-telemetry ~/.ssh/authorized_keys && echo "svc-telemetry key found" || echo "No key found; machine not impacted"This searches your authorized_keys file for svc-telemetry, the comment on the attacker's key. Phorion recommends checking the key's fingerprint as well (it's in the notice), since a comment is easy to change.
The notice also lists the persistence files to look for. There's a LaunchAgent named to look like one of Apple's, ~/Library/LaunchAgents/com.apple.sysmond.plist, and two hidden files, ~/.config/.smt and ~/.config/.smtk.
Windows
Here's the PowerShell version from the notice:
$files = "$HOME\.ssh\authorized_keys", "C:\ProgramData\ssh\administrators_authorized_keys" | Where-Object { Test-Path $_ }
if ($files -and (Select-String svc-telemetry $files)) { "svc-telemetry key found" } else { "No key found; machine not impacted" }This checks your own authorized_keys file and the system-wide administrators_authorized_keys file. For persistence, look for a scheduled task called SysMon, a startup script at %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\sysmon.cmd, and smt.cmd and smtk in %USERPROFILE%\.config.
Linux
The notice only lists macOS and Windows as affected platforms. But Phorion found the payload had setup logic for Linux too, and the notice's indicators include an @reboot cron entry pointing at ~/.config/.smt, so it's worth running crontab -l and the same grep check if you're on Linux.
Make it a habit
Those checks are specific to this one incident, and the next one will use different names. So it's worth getting used to looking at the places this kind of thing usually hides, every now and then, rather than only after something's gone wrong.
On macOS:
cat ~/.ssh/authorized_keys
ls -la ~/Library/LaunchAgents
lsof -nP -iTCP:22 -sTCP:ESTABLISHED
The first command shows every key that's allowed to SSH into your machine as you. If you never SSH into your own laptop, this file should be empty or missing (mine doesn't exist at all).
The second lists your LaunchAgents, which should all be things you recognise, and the third shows any open SSH connections. If you're not using SSH right now, it should return nothing.
On Windows, the rough equivalents are:
Get-Content $HOME\.ssh\authorized_keys
Get-ScheduledTask | Where-Object TaskPath -eq '\'
Get-NetTCPConnection -RemotePort 22 -State EstablishedThe middle one lists scheduled tasks in the root folder. Windows keeps most of its own tasks in subfolders under \Microsoft\, so anything in the root was added by an app or by you, and it should all be stuff you recognise.
If Get-Content says it can't find the file, that's fine, because it means no keys are allowed in. And if you're in the Administrators group, check C:\ProgramData\ssh\administrators_authorized_keys too, since that's the file Windows uses for admin accounts.
If you were hit
If any of those checks found something, here's what to do.
Don't stop at uninstalling
Removing the extension leaves the SSH key and the persistence scripts in place. The notice has remediation scripts for macOS and Windows that remove the key, the persistence and any running tunnel. On macOS you run it as your normal user (not root), and on Windows you run it from PowerShell as Administrator.
The scripts exit with an error if anything is left behind, so check the output rather than assuming it worked.
Rotate your credentials
Rotate any credentials and sessions that machine could reach, and do it from a different machine you trust. Start with anything matching the names the payload collected (keys, tokens, secrets and cloud credentials), since those are the ones an attacker already knows about.
Decide whether to rebuild
The notice says to assess whether the machine should be rebuilt. If you can't be sure what ran on it, or it holds production access, I'd rebuild it.
Whether the SSH step worked
Most affected machines probably got off fairly lightly. Turning on the SSH server needs admin rights, which the extension didn't have on most machines, so on those machines the step failed and the attacker couldn't log in through the tunnel. Phorion also found a bug on macOS where the payload misread the "you need administrator access" error as SSH already being switched on, so it never tried to enable it.
But as Phorion points out, that isn't something to rely on. If Remote Login was already turned on, or you turn it on later, the attacker's key is sitting there waiting, so clean up either way.
On a team
Everything above works fine for one person, but reviewing extensions by hand doesn't scale to a whole team.
A good starting point is a five minute review once a month. Run the list command, uninstall anything nobody uses, and check whether your pinned extensions need updating. It's also worth agreeing who decides whether a new AI extension gets installed, so it's not left to whoever finds one first.
The proper answer for a bigger team is an allowlist. VS Code (1.96 and later) has an extensions.allowed setting that blocks any extension that isn't on the list:
{
"extensions.allowed": {
"microsoft": true,
"github": true,
"esbenp.prettier-vscode": true,
"dbaeumer.vscode-eslint": ["3.0.0"]
}
}
A key without a dot is a publisher, so "microsoft": true allows everything Microsoft publishes. A key with a dot is a single extension, and you can pass an array of versions to only allow specific ones. If you block something that's already installed, VS Code disables it.
To stop people simply editing the setting, you can enforce it with the AllowedExtensions policy through Intune, Group Policy or your Mac MDM, and that overrides whatever's in each person's settings.
It isn't free, though. Someone has to look after the list and deal with requests.
Allowing an extension also doesn't stop a tampered version of it getting through. Pinning versions in the allowlist does, but then someone has to approve every update. If your team uses editors built on VS Code, check whether they support the same setting too.
Wrapping up
Of everything here, cutting your extension list down is what I'd do first. It only takes a few minutes and it's the biggest win.
After that, I'd turn off auto-update for the couple of extensions that hold your keys or run an agent, and keep the check commands somewhere you can find them. That way, next time something like this happens, you'll know what you've got installed and where to look.