# IDE Extension Security (VS Code, Open VSX)

> IDE extension security covers the risk of VS Code and Open VSX extensions, which run unsandboxed with the user's privileges and are targeted by GlassWorm.

Also known as: VS Code extension security, Open VSX security, editor extension security, VSIX security, code extension security

Source: https://socket.dev/glossary/ide-extension-security

Last updated: 2026-10-27

IDE extension security (also called VS Code extension security or Open VSX security) is the practice of controlling which editor extensions run on developer machines. A VS Code or Open VSX extension is unsandboxed Node.js code with the user's full privileges that activates when its manifest says and updates silently. Campaigns like [GlassWorm](https://socket.dev/supply-chain-attacks/glassworm-v2) exploit that to steal developer credentials.

The most important fact in this entry is that VS Code has no permission model for extensions. Microsoft's own [extension runtime security documentation](https://code.visualstudio.com/docs/configure/extensions/extension-runtime-security) says it in one line: "The extension host has the same permissions as VS Code itself." A color theme can read `~/.ssh`, spawn a shell, and send data anywhere, and nothing in the install flow tells you whether it does. Our position: treat every extension as an unsandboxed, auto-updating dependency, and judge it by the code in the artifact it ships, not by its listing, its download count, its verified badge, or its GitHub repository.

## Are VS Code extensions safe?

VS Code extensions are as safe as their publisher's next update, because VS Code enforces nothing on an installed extension's behavior. Desktop VS Code loads extensions into a local [extension host](https://code.visualstudio.com/api/advanced-topics/extension-host), a Node.js runtime on the same machine as the editor. The extension host exists to protect startup time and UI responsiveness. It is not a security boundary. Per Microsoft's documentation, an extension can read and write files, make network requests, run external processes, and modify workspace settings.

Three defaults turn that into a [supply chain attack](https://socket.dev/glossary/supply-chain-attack) surface:

- Extensions live in a per-user folder, `~/.vscode/extensions` on macOS and Linux.
- VS Code checks for extension updates and installs them automatically.
- Each extension decides for itself when it activates. `"activationEvents": ["*"]` means every startup.

Install-time code execution in package managers is the closest analogy. We think default-on [install scripts](https://socket.dev/glossary/postinstall-scripts) were a design mistake, and [npm v12](https://socket.dev/blog/npm-12) now ships with them off by default. VS Code has no equivalent switch for activation. With `*` activation, an extension's `activate()` function behaves like an install script that runs on every launch.

### A color theme that runs code looks almost identical to one that doesn't

We packaged two color themes with the official `vsce` tool (version 4.0.0) to see what signal a registry or reviewer actually gets. The first is a pure theme. The second adds two manifest fields:

```json
{
  "name": "midnight-demo-theme",
  "publisher": "example",
  "version": "0.0.1",
  "categories": ["Themes"],
  "contributes": {
    "themes": [
      { "label": "Midnight Demo", "uiTheme": "vs-dark", "path": "./themes/midnight.json" }
    ]
  },
  "main": "./extension.js",
  "activationEvents": ["*"]
}
```

`vsce package` builds the second one without a warning about either field. The VSIX is a plain zip archive. The only place the difference surfaces is one property in the generated `extension.vsixmanifest`:

```shell
$ npx vsce package
 DONE  Packaged: midnight-demo-theme-0.0.1.vsix (6 files, 2.17 KB)
$ unzip -p midnight-demo-theme-0.0.1.vsix extension.vsixmanifest | grep ExecutesCode
<Property Id="Microsoft.VisualStudio.Code.ExecutesCode" Value="true" />
```

The pure theme's manifest omits the property entirely. In vsce 4.0.0 the flag is computed as `executesCode: !!(manifest.main ?? manifest.browser)`, so it records only that an entry point exists. That one boolean is the whole capability declaration for a VS Code extension. Compare [browser extension security](https://socket.dev/glossary/browser-extension-security), where Chrome and Firefox make extensions declare permissions and host patterns that the browser enforces.

The shape above is not hypothetical. In [the GlassWorm-linked theme cluster](https://socket.dev/blog/glassworm-vscode-themes), we found that Cosmic Nebula Themes, sold as a color theme, declared `app.js` as its entry point, activated on `*`, decrypted an embedded stage with AES-256-CBC, and ran it through `eval()`. We think registries should hold any extension in the Themes category that executes code for review by default. A theme has no legitimate need for an entry point, and the registry already has the bit that says it has one.

> Figure 1. Every control VS Code offers sits before the extension runs. None of them constrains what the code does after activate() .
>
> The six checkpoints between a publisher and a running VS Code extension, and what each one does not check

Figure 1 traces an extension through six checkpoints. At publish, the Marketplace malware scan and Open VSX pre-publish checks look for bad code, but not whether a real account meant to ship it; Nx Console 18.95.0 passed both. At download, the Marketplace signature proves the bytes are unchanged since upload, not that they are benign. At first install, VS Code 1.97 and later ask once per publisher, and that trust extends to publishers a pack pulls in. At auto-update, `extensions.autoUpdateDelay` waits before installing a new version but never inspects it. At activation, `activationEvents` in the extension's own manifest choose when code runs. At runtime, the extension host runs the code as Node.js with VS Code's permissions, and nothing checks file, network, process, or credential access.

## Which manifest fields control what an extension can do?

An extension's capabilities are decided by a handful of `package.json` fields, and none of them restricts what the code can reach once it runs. They decide when code runs, what else gets installed, and which editor safety modes the extension opts out of. Socket's extension analysis raises a dedicated alert for nine of the fields below, each defined on [socket.dev/alerts](https://socket.dev/alerts).

| Manifest field | What it does | Why it matters | Socket alert |
| --- | --- | --- | --- |
| main or browser | Entry point; its presence is the only "executes code" signal | Color themes carrying GlassWorm loaders | None; the code itself is analyzed |
| activationEvents: \["\*"\] or onStartupFinished | Runs the extension on every startup | The trojanized Auto Import clone activates on \*, then waits 10 seconds | VS Code: Broad activation events |
| activationEvents: \["workspaceContains:\<glob\>"\] | Runs when an opened folder contains a matching file | Activation can be scoped to repositories that contain a chosen file | VS Code: Workspace file pattern activation |
| extensionPack | Installs every listed extension alongside this one | 17 copycat extensions updated on April 29, 2026 to pull in one GlassWorm extension | VS Code: Extension pack |
| extensionDependencies | Installs required extensions automatically | Same transitive pull as a pack; GlassWorm has abused both fields | VS Code: Extension dependency |
| enabledApiProposals | Turns on unstable proposed APIs | Microsoft says not to publish these to the Marketplace | VS Code: Proposed APIs Enabled |
| capabilities.untrustedWorkspaces | supported: true keeps the extension fully on in Restricted Mode | Opts the extension out of Workspace Trust gating | VS Code: Untrusted workspaces support |
| capabilities.virtualWorkspaces | Runs against remote or virtual file systems | Changes file access assumptions | VS Code: Virtual workspaces support |
| contributes.debuggers | Registers a debug adapter that launches and controls processes | Deep access to runtime targets | VS Code: Debugger contribution |
| Webviews | Renders HTML UI, optionally with remote content | GlassWorm-linked themes shipped scripted welcome-page webviews | VS Code: Webview contribution |

Two of these fields deserve more weight than the rest. `extensionPack` and `extensionDependencies` turn one install into several, which makes every extension a potential [transitive dependency](https://socket.dev/glossary/transitive-dependency) installer. Neither field requires the referenced extension to share a publisher. VS Code's [publisher trust prompt](https://code.visualstudio.com/docs/configure/extensions/extension-runtime-security), added in VS Code 1.97, makes this worse in one respect: trusting an extension pack also extends trust to the publishers of the extensions it pulls in.

## How does GlassWorm infect VS Code and Open VSX extensions?

GlassWorm infects developers through four delivery paths that end in the same place: attacker JavaScript running in the extension host. The campaign was first reported in October 2025 and has run in waves on Open VSX since, with spillover into the Visual Studio Marketplace, GitHub, and npm.

**Impostors.** Most GlassWorm extensions are [typosquatting](https://socket.dev/glossary/typosquatting) or brandjacking clones that copy a popular extension's README, source, and metadata. In [the 72-extension wave that turned to transitive dependencies](https://socket.dev/blog/open-vsx-transitive-glassworm-campaign), the publisher `daeumer-web` imitated `dbaeumer`, the publisher of ESLint, and the clones targeted Claude Code, Codex, and Antigravity users alongside Prettier and Python tooling.

**Stolen publishing credentials.** On January 30, 2026, in [the oorzc account compromise](https://socket.dev/blog/glassworm-loader-hits-open-vsx-via-suspected-developer-account-compromise), four established Open VSX extensions with more than 22,000 combined downloads received malicious versions. Open VSX assessed the activity as consistent with a leaked token and deactivated the publisher's two tokens. This is [maintainer account takeover](https://socket.dev/glossary/maintainer-account-takeover) in an extension registry: age and download count stop meaning anything. The Eclipse Foundation's [October 2025 security update](https://blogs.eclipse.org/post/mika%C3%ABl-barbero/open-vsx-security-update-october-2025) traced the first wave's leaked tokens to "developer mistakes, not a compromise of the Open VSX infrastructure."

**Sleepers.** GlassWorm now publishes clean extensions first and arms them later through the normal update path. In [the March 2026 sleeper activation](https://socket.dev/blog/glassworm-sleeper-extensions-activated-on-open-vsx), `96-studio.json-formatter@0.0.1` went up clean on March 12. On March 17, eight other extensions shipped 11 new versions that listed it in `extensionPack`. On March 18 at about 12:58 UTC, version 0.0.2 began downloading a VSIX from a GitHub release and force-installing it into every editor it could find with `--install-extension --force`. The target list covered VS Code, VSCodium, Cursor, and Windsurf; version 0.0.4, about two and a half hours later, added Positron. Moving the payload to GitHub put it outside the reach of Open VSX takedowns.

**Source that does not match the artifact.** The [trojanized Auto Import clone](https://socket.dev/blog/glassworm-sleeper-extensions-activated-on-open-vsx) delivered in that wave copied the original TypeScript verbatim. The clean `src/extension.ts` was 36 lines; the shipped `out/src/extension.js` was 659 lines, with more than 620 lines of obfuscated loader injected after compilation. Later waves moved the loader into bundled native `.node` binaries, as in [the 73 Open VSX sleeper extensions](https://socket.dev/blog/73-open-vsx-sleeper-extensions-glassworm), and into an 824,552-byte TinyGo WebAssembly module in [GlassWASM](https://socket.dev/blog/glasswasm-malware-open-vsx-extensions), a variant we link to GlassWorm with medium confidence. Reading the GitHub repository tells you nothing about any of these.

Once running, the loader keeps its [command and control (C2)](https://socket.dev/glossary/command-and-control-c2) off the registry entirely. It checks for Russian locale and time zones and exits if it finds them, then reads its next-stage URL from a Solana transaction memo:

```http
POST https://<one of 9 public Solana RPC endpoints>
Content-Type: application/json

{"jsonrpc":"2.0","id":1,"method":"getSignaturesForAddress",
 "params":["6YGcuyFRJKZtcaYCCFba9fScNUvPkGXodXE1mJiSzqDJ"]}
```

The memo on the newest transaction holds JSON like `{"link":"aHR0cHM6Ly8..."}`, a base64 URL. The payload server returns the encrypted stage in the body and the key and IV in `secretkey` and `ivbase64` response headers. Rotating infrastructure costs the operator one Solana transaction and no republish. On May 26, 2026, CrowdStrike, Google, and the Shadowserver Foundation [disrupted all four GlassWorm C2 channels at once](https://www.crowdstrike.com/en-us/blog/inside-crowdstrike-takedown-of-a-developer-targeting-botnet/). The disruption did not retroactively remove any previously published extensions.

The name oversells one thing. Mikaël Barbero of the Eclipse Foundation, responding to the first wave, was blunt: "this was not a self-replicating worm in the traditional sense." GlassWorm spreads by stealing GitHub, npm, and Open VSX credentials and publishing with them. Each stolen publisher account becomes a trusted release channel that the registry has no reason to question.

## Open VSX and the VS Code Marketplace are separate trust domains

The Visual Studio Marketplace serves Microsoft's VS Code. Open VSX, run by the Eclipse Foundation, serves Cursor, Windsurf, VSCodium, Antigravity, and other VS Code-based editors. The two registries share an ID format, `publisher.name`, and nothing else. Cursor's [extension documentation](https://cursor.com/docs/configuration/extensions) states the consequence: "The same extension ID can map to a different publisher or code on Open VSX than on the Microsoft Marketplace."

Attackers exploit that gap at scale. John Tuckner, who founded Secure Annex before [Socket acquired it](https://socket.dev/blog/socket-acquires-secure-annex) in April 2026, counted 491 Open VSX IDs, in the year to July 31, 2026, that attackers or researchers claimed under names belonging to Marketplace projects. 338 of them appeared in 2026, and 415 imitated extensions in the Marketplace's top 10,000, as reported in our post on how [Open VSX unblocked extension IDs used in the evil-twin campaign](https://socket.dev/blog/open-vsx-unblocks-malicious-extension-ids). That campaign used 77 impostors, all removed by August 3, 2026. The cleanup exposed a second problem: Open VSX's deny list stores bare ID strings with no version or hash, so once the real owner reclaims an ID, the list no longer records that the same ID once shipped malware.

The gap also travels inside repositories. A `.vscode/extensions.json` file recommends extensions by ID, and VS Code prompts users to install them on first open. The same file opened in an Open VSX-based editor resolves those IDs against a different registry, possibly to a different owner.

| Control | Visual Studio Marketplace | Open VSX |
| --- | --- | --- |
| Publish-time scanning | Malware scan on every package, plus dynamic analysis in a clean-room VM | Pre-publish checks: monitoring from February 2026, enforcement planned for March 2026 |
| Publisher identity | Verified publishers prove domain ownership; official publisher names are protected | Namespace ownership claims; no cross-registry name protection |
| Signatures | Marketplace signs every extension; VS Code verifies on install | Signatures are published; whether they are verified depends on the editor |
| Takedown | Block list, and VS Code uninstalls blocked extensions that are already installed | Deny list of ID strings; removal from the registry |
| Client update delay | extensions.autoUpdateDelay in VS Code 1.123 and later | Depends on the editor; Cursor offers install cooldowns as a team control |
| Stopped Nx Console 18.95.0? | No: it passed Microsoft's automated checks | No: its scan completed and the version went live in 3 minutes |

Open VSX's [move to pre-publish checks](https://socket.dev/blog/open-vsx-begins-implementing-pre-publish-security-checks) narrows the window for obvious impostors, but, as we noted when it was announced, pre-publish scanning cannot reliably detect compromised publisher accounts or malicious updates through trusted release paths.

Verdict: the Marketplace has the stronger registry-side controls, and its block list is the only takedown that reaches machines where the extension is already installed. Do not assume any of that carries over when a team moves to an Open VSX-based editor; record publisher, version, and file hash, not just the ID. Neither registry stops a legitimate publisher account shipping a malicious version, which is the case that has done the most damage.

## Nx Console: a signed update from a real publisher

The Nx Console compromise is the clearest proof that registry controls do not stop a malicious update from a legitimate account. In May 2026, GitHub disclosed that attackers had exfiltrated about 3,800 internal repositories after an employee's machine ran a poisoned Nx Console release, as we reported in [the Nx Console compromise](https://socket.dev/blog/npm-invalidates-tokens-mini-shai-hulud). Nx Console has 2.2 million installs.

The [Nx postmortem](https://nx.dev/blog/nx-console-v18-95-0-postmortem) reconstructs the chain. On May 11, a contributor ran `pnpm install` during [the TanStack compromise](https://socket.dev/blog/tanstack-npm-packages-compromised-mini-shai-hulud-supply-chain-attack), part of [Mini Shai-Hulud](https://socket.dev/supply-chain-attacks/mini-shai-hulud), and a `prepare` script harvested their GitHub credentials. On May 18, all times UTC:

- 12:30: the Visual Studio Marketplace registers the v18.95.0 upload, and it passes Microsoft's automated checks.
- 12:33: the Open VSX scan completes and v18.95.0 goes live there.
- 12:36: a maintainer spots the release from a routine publisher notification email.
- 12:47: the maintainer unpublishes it from the Marketplace, about 17 minutes after upload.
- 13:09: Open VSX unpublishes it, about 36 minutes after it went live.

Microsoft's telemetry counted 28 Marketplace installs, and Open VSX recorded 41 downloads. Nx's own telemetry showed about 6,000 extension activations, and Nx says the number of affected users may be significantly higher than Microsoft's count. The payload harvested Vault, npm, AWS, GitHub, 1Password, SSH, `.env`, GCP, and Docker credentials.

Every registry check passed. The publisher was real, the package was signed by the Marketplace, and both registries' scans let it through. Provenance and signatures are necessary, but a compromised maintainer publishes through the legitimate pipeline with valid ones.

> Figure 2. An update delay works against a fast smash-and-grab release and does little against a patient sleeper.
>
> Two malicious extension releases on their own time scales, against VS Code's auto-update delay

Figure 2 sets two releases against the update delay. Nx Console 18.95.0 was uploaded at 12:30 UTC on May 18, 2026, and unpublished from the Marketplace at 12:47 and from Open VSX at 13:09, well inside both the original two-hour delay, which would have ended at 14:30 UTC, and the current 12-hour default. The GlassWorm sleeper `96-studio.json-formatter` published a clean 0.0.1 on March 12 at 18:51 UTC and was added to other extensions' `extensionPack` lists from March 17 at 13:31 UTC. It shipped malicious 0.0.2 on March 18 at 12:58 UTC and 0.0.4 at 15:31 UTC. Socket flagged both malicious versions within minutes of publication; the removal time was not published. The delay starts counting only at the malicious release, so it protects users only if that version is removed inside the window.

## How do you secure VS Code extensions?

Securing VS Code extensions means controlling which versions reach developer machines, because nothing controls them once they run. Each control below closes one gap and leaves others open.

**Allowlist by publisher, extension, and version.** VS Code 1.96 added the [`extensions.allowed` setting](https://code.visualstudio.com/docs/enterprise/extensions), and the `AllowedExtensions` policy enforces it on managed devices. Once any rule exists, everything unmatched is blocked, and blocked extensions already installed are disabled.

```json
{
  "extensions.allowed": {
    "microsoft": true,
    "github": true,
    "esbenp.prettier-vscode": true,
    "dbaeumer.vscode-eslint": ["3.0.0"],
    "redhat": "stable"
  },
  "extensions.autoUpdateDelay": 24
}
```

The cost is maintenance. Version rules accept exact versions only, with no ranges, so pinning every extension means approving every update by hand. An allowlist also does nothing about a malicious version of an extension you already allowed by publisher, which is the Nx Console case.

**Delay automatic updates.** VS Code 1.123, released June 3, 2026, began holding automatic extension updates for two hours after publication. As of October 2026, the [Marketplace documentation](https://code.visualstudio.com/docs/configure/extensions/extension-marketplace) lists a 12-hour default for `extensions.autoUpdateDelay`. This is a [dependency cooldown](https://socket.dev/glossary/dependency-cooldown) for the editor, and had it existed in May 2026, it would have kept auto-updating clients off Nx Console 18.95.0. It has three gaps. It governs automatic updates only, so the Update button and fresh installs get the newest version immediately. The 1.123 release notes exempt trusted publishers such as Microsoft, GitHub, and OpenAI. And it buys time only if someone detects and removes the version inside the window; a sleeper that stays undetected simply outlasts it.

**Do not count on Workspace Trust.** [Workspace Trust](https://code.visualstudio.com/api/extension-guides/workspace-trust) protects you from the contents of an untrusted folder triggering code through extensions. It does nothing about an extension that is itself malicious. An extension that declares `"untrustedWorkspaces": { "supported": true }` runs fully in Restricted Mode.

**Audit what is already installed.** This loop flags the patterns above in your own extensions folder. It needs only `jq`:

```bash
for p in ~/.vscode/extensions/*/package.json; do
  jq -r '
    "\(.publisher).\(.name)@\(.version)" as $id
    | [ (if ((.categories // []) | index("Themes")) and (.main // .browser) then "theme that executes code" else empty end),
        (if ((.activationEvents // []) | index("*")) then "activates on every startup (*)" else empty end),
        (if ((.extensionPack // []) | length) > 0 then "pack installs: \(.extensionPack | join(", "))" else empty end),
        (if ((.extensionDependencies // []) | length) > 0 then "depends on: \(.extensionDependencies | join(", "))" else empty end),
        (if .capabilities.untrustedWorkspaces.supported == true then "runs fully in Restricted Mode" else empty end) ]
    | select(length > 0) | "\($id): \(join("; "))"' "$p"
done
```

Run against our demo theme and a pack that pulls in a GlassWorm extension, it prints:

```text
acme.pack@1.0.0: pack installs: 96-studio.json-formatter
example.midnight-demo-theme@0.0.1: theme that executes code; activates on every startup (*)
```

A clean audit means only that today's manifests look normal. It says nothing about obfuscated code, and nothing about the next update.

**Analyze each version before it installs.** The useful signal is the diff between versions: a new `extensionPack` entry, a theme that gains an entry point, compiled JavaScript that no longer matches its source. Socket scans Open VSX extensions and, in an experimental release since October 6, 2026, the Visual Studio Marketplace's more than 100,000 extensions. Socket Firewall, a [package firewall](https://socket.dev/glossary/package-firewall), [filters marketplace results and blocks VSIX downloads](https://socket.dev/blog/socket-firewall-blocks-malicious-code-extensions) for both registries; as of June 2026 it is in closed beta for Enterprise customers. A proxy sees only traffic routed through it. A VSIX that malware downloads from a GitHub release and installs with the editor's CLI never goes through the marketplace request path at all.

**Plan for the payload, not the extension.** Removing a malicious extension does not undo what its downloaded stage already did. GlassWorm's macOS stage installed a LaunchAgent and took `~/.aws`, `~/.ssh`, npm `_authToken` values, and GitHub tokens. Treat any hit as a credential exposure and rotate in the order our analysis recommends: GitHub tokens, then npm tokens, then AWS keys, then SSH keys.

## What should change

We do not expect the extension host to get a permission model soon, because too many legitimate extensions depend on unrestricted Node.js. That makes the registries and the update path the only place left to enforce anything, and three changes would cost legitimate publishers very little. Registries should hold for review any Themes-category extension whose manifest sets `ExecutesCode`. A version that newly adds `extensionPack` or `extensionDependencies` should require the user to approve it again, the way browsers re-prompt when an extension asks for a new permission. And deny lists should key on artifact hashes, not reusable IDs. Until then, the defense lives outside the process: decide which bytes reach the extension host, because once they are there, they are you.
