I read GitHub’s incident write-up and had one of those annoying little moments where you immediately start thinking about your own setup. Not because I found something terrible, but because I found something normal. And honestly, that might be worse. 😬
GitHub published details about unauthorized access to some of its internal repositories. According to GitHub, the attacker compromised a GitHub employee’s device by using a malicious VS Code extension. That access was then used to pull internal repositories. GitHub says no customer repositories or customer data were affected, and that credentials were rotated.
That part matters, of course. But the interesting part for the rest of us is not “GitHub had an incident”. Large organizations have incidents. They also have detection, response teams, logs, and people who spend their day cleaning up the mess. The interesting part is where the incident started: the editor.
We talk a lot about supply chain security in open source. Usually, that means dependencies: the package you install, the transitive dependency you did not know you had, the maintainer account that got compromised, the release token that should not have existed anymore, or the GitHub Action that had more permissions than it needed. All of that still matters.
In other words: developer tooling is part of supply chain security now. Not just dependencies. Not just CI. The editor, extensions, CLIs, language servers, and AI tools around the release process belong in the same conversation.

But this incident is a useful reminder that the supply chain does not stop at pyproject.toml, package.json, or whatever file your ecosystem has decided is the place where pain starts. Your editor is part of it too. That sounds obvious once you say it out loud, which is usually a sign we should have been saying it earlier.
We secured the package and forgot the room it was built in
Over the last few years, open source has gotten much better at talking about package supply chain security. In Python land, PyPI Trusted Publishers let projects publish without long-lived API tokens sitting in CI secrets. The Python Packaging User Guide now documents publishing through GitHub Actions with OIDC, environments, and the pypa/gh-action-pypi-publish action. The verification side still leaves plenty of room for improvement, but at least we are publishing attestations nowadays. That is progress. 🙂
On the npm side, there is trusted publishing too, and npm added staged publishing, where a package can be submitted first and explicitly approved before it goes live. GitHub has its own pile of useful building blocks as well: GitHub Actions hardening guidance, artifact attestations, secret scanning, token permission controls, environment approvals, and all the other things that make release pipelines less terrifying than they used to be.
Good. 👍 That work is necessary. But there is a weird blind spot in how we think about all of this. We harden the publishing pipeline, then run the pipeline from a developer machine full of editor extensions, CLIs, language servers, AI coding tools, package managers, local credentials, shell scripts, and plugins installed six months ago because a README said it made imports look nicer.
Somewhere along the way, “I installed an editor extension” became “I added third-party code to the machine that can publish my projects”. That is a different sentence. And it should feel different.
An editor extension is not just syntax highlighting
I am not picking on VS Code here. I use modern tools too. This is not a “real developers use terminal editors” post. I am a terminal junkie myself. Call me old, but I prefer it over any UI every day. Before the terminal crowd starts stretching their fingers: no, this is not the part where I tell everyone to move to Neovim.
A carefully curated Neovim setup can be wonderfully boring. A full setup like LazyVim can be a sane starting point too. But a plugin jungle held together by Lua snippets copied from someone’s dotfiles repo is not magically safer because it runs in a terminal. Neovim is not a security boundary. It is a lifestyle choice with better keybindings.
The point is bigger than any one editor. Modern editors are not text boxes. They are runtime environments. They run extensions. Those extensions can inspect workspaces, call binaries, spawn processes, talk to network services, and integrate with your terminal, Git client, package manager, cloud tools, AI assistant, debugger, and language server.
That is why they are useful, and it is also why they are interesting targets. The closer a tool sits to your daily development workflow, the more valuable it becomes to an attacker. Not because the tool itself is special, but because of what lives around it.
Your checked-out repositories. Your shell history. Your authenticated CLIs. Your cloud credentials. Your 1Password or Vault session. Your package publishing permissions. Your release scripts. Your local environment files that definitely should not contain secrets but somehow always manage to contain at least one. You know. Hypothetically. 😅
This is also why I still keep my SSH keys on a good old YubiKey and use the GPG agent for authentication and signing. It means every relevant action needs hardware interaction. Airtight? Probably not. But at least those SSH keys are not sitting on my computer, waiting for some creative piece of software to go looking for them.
The Nx Console advisory made this very concrete
There is also a related advisory from the Nx team about a compromised version of the Nx Console extension. The malicious version, 18.95.0, was live only briefly. According to the advisory, it was available on the Visual Studio Marketplace for about 18 minutes and on OpenVSX for about 36 minutes. That sounds short, but short is not the same as harmless.
The advisory says internal analytics showed around 6,000 VS Code activations and one Cursor activation. Marketplace download counts looked much lower, which is another fun reminder that telemetry and registry counters do not always tell the same story.
The payload did not just go looking for one thing. It targeted credentials from a long list of places: GitHub tokens, npm tokens, Vault tokens, AWS credentials, 1Password CLI sessions, SSH keys, Docker and GCP credentials, and more. It also attempted persistence through files and processes that looked intentionally boring.
This is the part that should make maintainers uncomfortable. Not panicked. Not dramatic. Just uncomfortable enough to review our assumptions. 😬 Because this is not really about Nx Console, and it is not even really about VS Code. It is about the fact that a developer machine is often treated as “local”, while containing access to things that are very much not local.
The developer machine is not production, but it often contains keys to production-shaped doors.

I looked at my own setup. Of course I did
After reading this, I did what every sensible person does after a security incident: I looked at my own machine and immediately regretted making eye contact with reality. 🙈 Not because it was a disaster, but because it was normal.
Extensions I installed for one task. CLIs I use often enough to keep authenticated. Local checkouts of projects I care about. Python tooling, Node tooling, package managers, release helpers, language servers, test runners, and a graveyard of “I will clean this up later” decisions.
This is how real development environments grow. Not through one giant bad decision, but through a hundred tiny reasonable ones: installing an extension to debug something, keeping a CLI logged in because you use it daily, adding a plugin because it saves ten seconds per commit, or trying an AI tool because maybe it helps with boilerplate.
None of those decisions are weird. That is the problem. The normal setup has become powerful enough that compromising it is no longer “local machine compromise” in the old sense. It can become “release pipeline adjacent” very quickly. 🫠
Do not treat tooling as a trust-free zone
The lazy conclusion would be: do not install editor extensions. That is not going to happen. Extensions are useful. Language servers are useful. Debuggers are useful. Formatters are useful. AI coding tools are useful. Git integration is useful. The modern development environment is powerful because all of this stuff is connected.
The answer cannot be “go back to writing code with a sharpened stick”.
The answer is to stop treating developer tooling as a trust-free zone.
We already do this with dependencies. At least, we try. When a package wants to run during install, we pay attention. When a dependency has a maintainer change, we pay attention. When a package is newly published, obfuscated, or suddenly much larger, security tooling may pay attention. When a package asks for access to secrets or publish rights, hopefully something blocks that before it ruins someone’s weekend.
But editor extensions often get a softer path: install, reload, continue. Then they update automatically, and then they run inside the same environment where all the interesting credentials live. I am not saying every extension needs a full security review with a committee and a spreadsheet. Please no. We have enough spreadsheets.
But if an extension runs code on a machine that can publish packages, access private repositories, or deploy infrastructure, it deserves to be thought of as part of that supply chain. Not decoration.
The real boundary is not “GUI vs terminal”
This is where people often reach for the wrong distinction: GUI tools bad, terminal tools good. That is comforting, but wrong. A terminal-based workflow can be much smaller and easier to reason about. That is valuable. Fewer moving parts usually means fewer surprises. A minimal editor with a small number of pinned plugins is a very reasonable choice.
But “command-line” is not a security model. Your curl | bash install script does not become safe because it printed progress bars in monospace. Your plugin manager does not become trustworthy because it lives in a dotfiles repository. Your language server does not become harmless because it was installed through a package manager. Your AI coding assistant does not become less powerful because it is launched from a terminal.
The question is not whether the tool has a sidebar. The question is what it can read, what it can execute, how it updates, who can publish updates, which credentials are available nearby, and what irreversible actions can happen from this machine. That is the trust boundary. Or at least, it should be.
Publishing needs friction in the right places
This is why I care more about mechanisms like PyPI Trusted Publishers than another “please rotate your token” reminder. Rotating tokens is good. Not having long-lived tokens in the first place is better. With PyPI Trusted Publishers, a CI workflow can use OIDC to get a short-lived token for a specific publishing action. The token expires quickly. It is scoped. It does not need to sit around in a secrets store forever, waiting for a bad day.
That does not solve the editor problem by itself, but it does change the blast radius. The same idea applies elsewhere: npm trusted publishing reduces the need for long-lived npm tokens, npm staged publishing adds an explicit approval step before a package goes live, and GitHub environments can require review before deployment or publishing jobs access sensitive credentials.
That is the shape of the solution I want more of. Not “make every action painful”. Not “make maintainers click through security theater until they stop reading”. Just add a deliberate step where trust turns into public impact. Publishing a package, releasing an extension, deploying infrastructure, changing permissions: those are the moments where a little boring friction is useful. Rotating a secret is annoying. Publishing a poisoned package is worse.
Boring friction is underrated. 🙂
What I am changing in how I think about this
The practical lesson for me is not that I should delete every extension, uninstall every CLI, and move into a cave with a ThinkPad and a locally compiled editor. Although, to be fair, some of you are already halfway there. The lesson is that I need to treat my development environment as part of the software supply chain.
That means editor extensions are dependencies. They may not appear in pyproject.toml or package.json, but they still affect the security of the project. Machines with publish rights also deserve stricter rules than random development machines. If a machine can publish packages, push releases, access production credentials, or approve deployments, it should not also be the place where I try every shiny extension I saw on the internet.
Long-lived tokens need to die wherever possible. Trusted publishing is better than a PyPI or npm token sitting around waiting to be stolen. Short-lived credentials are better than permanent ones. Scoped credentials are better than “well, it was easier this way”.
Auto-update behavior matters too. Silent updates are convenient until convenience becomes the delivery mechanism. AI tooling belongs in this conversation as well: coding agents, MCP servers, editor assistants, local tools with broad filesystem access, all of it. Not because AI is uniquely scary, but because these tools are intentionally designed to read, modify, and execute things inside your development environment. That is power. Power needs boundaries. 🔐

This is not about fear
I do not want maintainers to become paranoid. Open source already asks too much from people who are often doing the work for free, late at night, with too many tabs open and too much coffee involved. A pile of warnings only makes people tune out, which is even worse.
The better message is: know where the trust is. Your dependency graph is trust. Your CI pipeline is trust. Your package registry account is trust. Your authenticated tooling is trust. Your editor is trust. The uncomfortable part is that these things overlap on the same machine more often than we like to admit.
The xz backdoor was such a painful reminder of this. That one was not about an editor extension, but it was absolutely about trust being violated: maintainer trust, release trust, build trust, distribution trust. The lesson is not “trust nobody”, because open source cannot work that way. The lesson is to trust people sparingly, verify what you can, and maybe trust AI even less while we are at it. 😄
That does not mean we stop using tools. It means we stop pretending tools are neutral just because they are familiar. A poisoned editor extension is not some exotic attack path. It is depressingly practical. Developers install extensions. Extensions update. Extensions run close to source code and credentials. Maintainers publish things. Connect those dots and you get a supply-chain incident with a nicer icon.
The fix is not one tool, one policy, or one blog post from a guy with opinions. It is a shift in how we think. Your editor is not outside the supply chain. It is sitting right in the middle of it.
And yes, if this makes you want to clean up your extensions list today, good.
Same.
../Frenck