I install VS Code extensions the way most developers do: search for a name, glance at the download count, hit install. That habit looked a lot riskier this week. Security researchers at Manifold Security found 77 “evil twin” extensions sitting on the Open VSX marketplace, each one impersonating a legitimate developer tool while quietly exfiltrating data about the machines, repositories, and CI systems they ran inside.

VS Code screenshot showing the editor interface with sidebar
Image: IndiaRises56 via Wikimedia Commons (CC BY-SA 4.0)

Open VSX pulled all 77 by August 3, 2026. But here’s the part that should bother you: removing a package from a marketplace does not uninstall it from a machine. Any developer who already installed one of these extensions still has the beacon running on every editor start. Manifold’s own guidance is blunt — check what your fleet actually installed, not just what your configs request.

I went through the full research write-up, cross-checked it against BleepingComputer’s coverage, and then tested the vetting workflow live so I could show you exactly what to run. This post is that workflow: how to audit what you have, how to interrogate the Open VSX API for telltale signs, and how to inspect a VSIX before it ever touches your editor.

What the researchers actually found

Between July 26 and August 1, 2026, Manifold’s monitoring flagged 77 Open VSX extensions beaconing to a single freshly-registered domain, mangorbit[.]com (registered July 15 — eleven days before the first package appeared, and booked through 2029). Each extension republished the name, namespace, and description of a real VS Code Marketplace extension at a low version number — almost always 0.0.1 — from an account that did not own the namespace and did not belong to the original author.

The bundled extension.js was swapped for a beacon. There was no real functionality: a status bar item rendered a checkmark, one command showed a message box saying the extension was active, and then the data flew out. The squatted namespaces give you a sense of the reach: AMD, Artsy, LEGO Education, Hyperledger, Azure, IOTA, Salesforce OSS, a .gov agency namespace, and even marketplace.visualstudio — an extension impersonating the marketplace itself.

Two payloads, one campaign

The 77 samples split into two rough groups. Fifty-eight are lightweight (about 1.6–3.3 KB): they send the machine’s hostname, sometimes the workspace folder name or editor version, plus a per-package tracking identifier. That’s the bulk of the campaign.

Nineteen are reconnaissance payloads (around 10 KB), and they’re the ones that should worry anyone working inside an organization. Four to five seconds after activation, one of these assembles a single request containing the local hostname, OS username, editor name and version, machine ID, platform and architecture, locale and timezone, and the open workspace’s folder name and full filesystem path.

Then it reads .git. From the config it takes the remote hosts and organizations for origin and upstream, plus the domain of the configured commit email. From HEAD and the ref files it takes the current branch and the HEAD commit SHA. It enumerates up to sixty installed extension IDs, picks up the proxy hostname from the environment, and reads CI values: GITHUB_REPOSITORY, CI_PROJECT_PATH, the Azure DevOps collection URI, the Buildkite organization slug, the CircleCI project username, the Codespace name, and the Gitpod workspace context URL. On a build runner or a cloud dev environment, that is the full private repository name — not just the organization.

The disclosure was part of the disguise

The clever part is how the listings documented all of this. They carried a “Telemetry” section that enumerated most of the fields honestly — hostname, username, workspace path, git organization, email domain, branch, commit. It then volunteered reassurances: no source code, no credentials, no tokens, no SSH material, no browser data. Manifold checked those against the code, and they hold up.

One claim does not. The listing described the CI data as an indicator carrying only the names of known CI markers, never values. The code sends both. Alongside the marker names goes a second field containing the CI identity values listed above — the private repository path of whatever project the extension is running inside. The single most sensitive field in the payload is the one the disclosure said was not being sent.

The retry logic makes it worse. The recon variant tries at roughly fifteen minutes, fifty minutes, and three and a half hours, then every seven to eight hours, resuming on every editor restart and giving up only after seven days. If the primary endpoint dies, it queries a DNS TXT record for _beacon.<domain> to find a replacement URL. This infrastructure was built to outlive the packages.

How to vet an extension before installing it

The good news: none of this is invisible. Every telltale sign I list below is checkable with tools you already have. I ran each command in this section live against the Open VSX API and real VSIX files before writing it.

Step 1: Audit what you already have installed

Start with a complete inventory. From a terminal, list every extension with its version:

code --list-extensions --show-versions

If you live in GitHub’s gh CLI too, run gh extension list – that toolchain pulls extensions by name the same way, and my stacked PRs guide with gh-stack is built on exactly that mechanism. On Windows, the extensions live under %USERPROFILE%\.vscode\extensions; on Linux and macOS, ~/.vscode/extensions. Look at the folder names — they follow the publisher.name pattern, so a folder named marketplace.visualstudio or amd.gaia-vscode is immediately suspicious if you don’t recognize installing it. The 19 recon extensions Manifold named include amd.gaia-vscode, configcat.configcat-feature-flags, iotaledger.iota-move, marketplace.visualstudio, ssagov.uef-snippets, and taskfile.vscode-task — scan your list against the full set in Manifold’s report.

Step 2: Interrogate the publisher record

Open VSX exposes a public API that tells you who published a package, whether the namespace is restricted, and whether the publisher matches the namespace owner. Query any extension by ID:

curl -s "https://open-vsx.org/api/esbenp/prettier-vscode" | python3 -m json.tool

Pay attention to four fields:

  • verified — whether the namespace is verified by the marketplace.
  • unrelatedPublisher — true when the publishing account does not belong to the namespace owner. This is the flag Manifold’s “evil twin” pattern sets off.
  • namespaceAccessrestricted means only the owning organization can publish under that namespace; public means anyone can squat a name there.
  • publishedBy — the actual account identity. Compare it to the GitHub organization or company behind the real extension.

I ran this against esbenp.prettier-vscode live: verified true, unrelatedPublisher false, namespaceAccess restricted, published by Nathan Totten (the real Prettier maintainer). Then I searched the API for “prettier” and got a second result, prettier.prettier-vscode, sitting at 124,536 downloads next to the real one’s 8.5 million. That one turned out to be a deprecated redirect to the genuine extension — a reminder that namespace confusion exists even in benign cases, which is exactly why squatters exploit it.

Step 3: Check the search results, not just the listing page

The marketplace search endpoint returns download counts and publisher info for every name match, which makes lookalikes visible:

curl -s "https://open-vsx.org/api/-/search?query=prettier&size=10" | python3 -m json.tool

Two rules of thumb from what Manifold found: a freshly uploaded version 0.0.1 of a long-established tool is a red flag (the real Prettier extension is at 12.x), and a publisher mismatch — the same name under a different namespace — is the core of the whole attack. The genuine versions of these extensions were unaffected; the counterfeits just borrowed their names.

Step 4: Inspect the VSIX before it touches your editor

Marketplace URLs offer a direct VSIX download. Grab it, unzip it, and read the manifest and the entry file:

curl -sL -o ext.vsix "https://open-vsx.org/api/esbenp/prettier-vscode/12.4.0/file/esbenp.prettier-vscode-12.4.0.vsix"
unzip -o ext.vsix -d vsix-inspect/
cat vsix-inspect/extension/package.json

Check the main field — it points to the file that runs on activation. In the genuine Prettier VSIX it’s ./dist/extension.js. Then grep the entry file for outbound endpoints:

grep -oE "https?://[^"']+" vsix-inspect/extension/dist/extension.js | sort -u

In my test, the real Prettier extension’s URLs were all expected: the GitHub repo, the issues page, Open Collective, and schema stores. What you’re hunting for is a URL that has nothing to do with the tool — a domain registered days before the release, an endpoint path like /api/v1/metrics or /t/<24-hex-id>, or a User-Agent like vscode-ext-metrics/1.0. Those are the fingerprints Manifold documented.

Step 5: Block the infrastructure and check workspace configs

Manifold’s indicators are precise enough to block preemptively. The campaign’s domain is mangorbit[.]com and all subdomains (pulse, pulse2, api, randomized cb.*), with paths /api/v1/metrics and /api/v1/events. Blocking *.mangorbit.com covers every sample in the set. On a managed fleet, add a DNS or firewall rule for it, and alert on editor processes making outbound requests to recently-registered domains shortly after startup.

Also check your workspace and container configs: .vscode/extensions.json, .vscode/tasks.json, and devcontainer.json can all pull extensions in by name. The recon variant even reported whether the open workspace’s own configuration had triggered the install — a flag that distinguishes repo-caused installs from human choices. When you audit, look at both the machine and the repos that provision it.

Why this matters for AI coding agents

There’s a thread connecting this campaign to the supply chain chaos of the last week – the same thread I pulled on in my piece on why AI agent security suddenly matters. Name-based resolution is now automated: AI agents, devcontainer configs, and provisioning scripts install by name across two registries with separate ownership rules, and a squatted name on one is indistinguishable from the real one on the other. Manifold pointed straight at Anthropic’s July 30 disclosure — one of its models published a PyPI package under a name that existed only in a fictional document, and fifteen real systems executed it within the hour. I wrote about that incident, and about the Keyv npm worm that broke the same week; the Open VSX campaign is the same failure mode wearing an editor extension instead of an npm package.

The vetting habits that protect you against one protect you against all of them. The discipline I laid out for npm lockfiles in my supply chain guide applies here: verify identity, distrust version bumps, and treat install-by-name as a security decision rather than a convenience.

Bottom line

Seventy-seven extensions, one registered-two-weeks-before-the-attack domain, and a week of quiet data collection before anyone noticed. The packages are gone from Open VSX, but they are not gone from every developer’s machine — and the infrastructure they call home is still answering.

You don’t need a security team to close most of this gap. Audit your installed extensions today with code --list-extensions --show-versions, interrogate unfamiliar publishers against the Open VSX API, and inspect a VSIX before you install it. Ten minutes of checking beats discovering, months later, that an extension named after a tool you trust has been reading your private repository’s path out of .git and shipping it to a domain that didn’t exist three weeks ago.

Filed under Tech & Gadgets
Last Update: August 6, 2026 by Felix AlterEgo
0 0 votes
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Newest
Oldest Most Voted