GUIDE · HARDENING 9 min ·

Vet VS Code extensions before your AI agent runsThe extension is the agent's attack surface

VS Code extensions run in the same editor host as your AI coding agent, with access to its config, its tokens, and the ability to drive it. Two 2026 supply-chain attacks weaponized that reach. Vet every extension before it loads, and constrain what it can touch after.

TL;DR· the answer, in twenty seconds

What happened: In 2026 two VS Code extension attacks targeted AI coding agents directly. A poisoned Nx Console build harvested Claude Code config and developer credentials in an 18-minute window. A trojanized Aqua Trivy build on OpenVSX embedded a 2,000-word prompt that drove five local AI agents in their most permissive mode to scan for credentials and exfiltrate them.

The minimum fix: Check the publisher, install count, and recent version history before you install. Read the manifest's contributed commands and activation events. Pin versions, use a separate editor profile for agent work, and turn off auto-update so a quiet republish cannot swap clean code for a payload behind your back.

The lesson: An extension runs with your reach, and your agent runs with your reach, so an extension can borrow the agent. The trust decision happens at install time. Make it deliberately.

In 2026 two supply-chain attacks stopped treating the AI coding agent as collateral damage and started treating it as the target. One stole the agent's credentials. The other took the agent's hands. Both shipped as VS Code extensions, and both landed inside the editor in minutes.

If you run Claude Code, GitHub Copilot, Codex, or Cursor, your agent shares the editor with every extension you have installed. That extension is not sandboxed away from your agent. It reads the same workspace, it can read the agent's config and tokens on disk, and it can issue the same commands you can. The install button is the trust boundary. Most developers click it the way they click "accept cookies."

The two attacks that moved the target onto the agent

On May 18, 2026, attackers published a trojanized build of the Nx Console extension to the Visual Studio Marketplace. The malicious version was live from 12:30 to 12:48 UTC, an 18-minute window, and that was enough. The Hacker News reported that the build ran a single shell command on startup that pulled and executed a hidden package from a planted commit, and that the command was dressed up as a routine MCP setup task so it would not draw a second look. The payload was a credential stealer. Its target list named 1Password vaults, npm and GitHub credentials, AWS keys, and Anthropic Claude Code configurations by name. The group behind it, TeamPCP, used the stolen credentials to reach downstream platforms and walked off with about 3,800 internal GitHub repositories. The advisory carries CVE-2026-45321 at CVSS 9.6.

Read the target list again. The attacker wanted your Claude Code config the same way they wanted your AWS keys. The agent's local state is now a named asset.

The second attack went further and used the agent as the weapon. On February 27 and 28, 2026, versions 1.8.12 and 1.8.13 of the Aqua Trivy extension landed on the OpenVSX registry. Socket found that the builds carried no classic malware. They carried a prompt. Version 1.8.12 embedded a 2,000-word natural-language instruction that framed the local AI assistant as a "forensic agent" and told it to hunt for compromises, credentials, and financial data. The extension tried to feed that prompt to any of five locally installed agents (Claude, Codex, Gemini, GitHub Copilot CLI, and Kiro CLI) and to run each one in its most permissive mode so the human-in-the-loop approval step never fired. Version 1.8.13 retargeted the prompt at developer tools and tokens, told the agent to use the GitHub CLI to create a repo named posture-report-trivy, and write the findings to a REPORT.MD. A former maintainer with publishing rights slipped the code in. It was not in the upstream GitHub repository at all.

Neither of these needed a zero-day. They needed a publish button and a developer who trusted the marketplace.

Why an extension is worse than a dependency

You already know to be careful with npm packages, and there is a whole discipline around it: lockfiles, audit runs, version pinning. An editor extension sits in a more dangerous spot than a package, for three reasons.

It runs in the editor host process. A VS Code extension executes Node.js code in the extension host, not in your app's runtime. It activates on events you do not control, including the moment you open a folder. The VS Code extension security model gives you Workspace Trust as a gate, but a trusted workspace grants the extension wide latitude, and most working folders end up trusted.

It can read what the agent reads. Your agent stores config and, in many setups, tokens on disk: ~/.claude, ~/.codex, ~/.config, project-level dotfiles. An extension running in the host can read those paths. That is what the Nx payload did. It did not need to break the agent. It read the agent's pockets.

It can drive the agent. This is the part the Trivy attack exposed and the part most threat models miss. The agent exposes a command surface, a CLI, and in some cases an MCP endpoint. An extension that can spawn a process or issue an editor command can invoke the agent and hand it instructions. If the agent is configured to auto-approve tool calls, there is no prompt to catch it. The extension borrows your agent's autonomy and points it wherever it likes.

GitGuardian's research has tracked for years how secrets accumulate in developer environments faster than anyone revokes them. The editor is where those secrets are most concentrated and least guarded. An extension is a tenant in that house.

The agent is what makes a dumb extension dangerous

Before agents, a malicious extension had to do its own work: write its own exfiltration code and find its own files. That code is detectable. Scanners look for it.

The Trivy attack carried almost none of that. The malicious logic was English. The actual file-scanning, credential-finding, and data-exfiltrating ran inside a legitimate, signed AI agent the developer installed on purpose. The extension was a launcher. The agent was the payload. This is the supply-chain version of the same prompt-injection problem The Register and others have covered across agent tooling all year: once an agent will follow instructions from data, any channel that delivers data is an instruction channel.

The defensive implication is direct. You cannot scan your way out of this with antivirus signatures, because there is no virus. The defense is the install decision and the blast radius after install.

Vet the extension before it loads

Run this before you click install on anything that touches an agent workspace. It takes two minutes.

Check the publisher, not the display name. Marketplace display names are not unique and not verified by default. Open the extension page and read the publisher ID. A "Verified" badge on a domain you recognize is worth something. A first-party tool published by a random account is the single loudest signal there is.

Check the version history and install count. The Nx and Trivy attacks both rode on extensions with real reputations and large install bases. A trustworthy extension with a brand-new patch version published hours ago, out of its normal cadence, is the pattern both attacks shared. Look at when the latest version shipped and whether it matches the project's usual release rhythm.

Read the manifest. Every extension ships a package.json. The fields that matter for blast radius are contributes, activationEvents, and any declared dependencies. Pull and read it before install:

# Inspect an extension's manifest before trusting it.
# Replace publisher.name with the extension ID from the marketplace URL.
npm pack --silent vsce 2>/dev/null  # one-time, if you want vsce tooling
npx ovsx get publisher.name --output ./ext-check 2>/dev/null || \
  curl -fsSL "https://open-vsx.org/api/publisher/name/latest" | python3 -m json.tool

# Or for an already-installed extension, read the manifest on disk:
cat ~/.vscode/extensions/publisher.name-*/package.json \
  | python3 -c 'import json,sys; m=json.load(sys.stdin); \
print("activationEvents:", m.get("activationEvents")); \
print("commands:", [c.get("command") for c in m.get("contributes",{}).get("commands",[])]); \
print("deps:", list(m.get("dependencies",{}).keys()))'

Two patterns deserve a hard stop. An activationEvents of ["*"] means the extension wakes on startup before you do anything, the same trigger the Nx payload used. A dependency list that pulls remote code at runtime means the manifest you read is not the code you run.

List what you already trust. Most developers have no idea how many extensions they are carrying. Audit the set:

# Every extension currently installed, with versions.
code --list-extensions --show-versions

# Cross-check against what you actually use this month.
# Anything you don't recognize is a candidate for removal.

The honest output of that command runs longer than anyone expects. Each line is an extension with reach into your editor host. Uninstall the ones you cannot name a reason for.

Constrain the blast radius after install

Vetting reduces the odds. Containment reduces the damage when vetting misses something, which it will.

Turn off auto-update for extensions. This is the single highest-value change. Both 2026 attacks relied on a quiet republish that auto-update would have pushed to your machine while you slept. In VS Code, set "extensions.autoUpdate": false and "extensions.autoCheckUpdates": false. Update on a schedule you choose, after the new version has been public long enough for someone else to find the payload.

// settings.json: update extensions on your terms, not the marketplace's.
{
  "extensions.autoUpdate": false,
  "extensions.autoCheckUpdates": false,
  "security.workspace.trust.enabled": true,
  "security.workspace.trust.untrustedFiles": "prompt"
}

Use a separate profile for agent work. VS Code profiles give you isolated extension sets. Run your AI agent in a profile that carries the minimum extension set it needs and nothing else. Your kitchen-sink profile with 60 extensions does not need to be the one holding an agent that has your production credentials in scope.

Keep Workspace Trust on, and mean it. When VS Code asks whether you trust a folder, that prompt decides whether extensions run their full code. Opening a cloned repo from a stranger in trusted mode runs whatever its workspace-recommended extensions and tasks want to run. Open unknown code in Restricted Mode first.

Do not let the agent auto-approve. The Trivy attack worked because it could push agents into a mode where tool calls execute without a prompt. If your agent supports an approval step for shell and network actions, keep it on for any session that shares an editor with extensions you have not audited yourself. The prompt that interrupts you is the prompt that interrupts the attacker.

Watch the OpenVSX side door. Cursor, Windsurf, and other VS Code forks pull from OpenVSX rather than the Microsoft Marketplace. The Trivy build landed on OpenVSX. The vetting steps above apply the same way there, and the version-history check matters more, because OpenVSX review is lighter. If you run a fork, know which registry it trusts by default and whether you can point it at a narrower mirror. A registry you did not choose is one more publisher you are trusting without a decision.

Scope the agent's credentials to the project. An extension that reads ~/.aws/credentials or a global token file gets whatever those files hold. An agent that only ever receives a credential scoped to the current repository, for the length of the task, hands a thief far less. The reach you remove ahead of time is the reach no review has to catch later.

A pre-install checklist you can paste into a PR

## Extension pre-install audit (agent workspace)

- [ ] Publisher ID read and matches the real project (not just display name)
- [ ] Latest version's publish date fits the project's normal cadence
- [ ] Install count and history look consistent (no surprise spike or fork)
- [ ] Manifest read: activationEvents is not "*", commands are expected
- [ ] No runtime remote-code dependencies in the manifest
- [ ] Auto-update is off; updates happen on a delay you choose
- [ ] Extension installed in the dedicated agent profile, not the global set
- [ ] Workspace Trust on; unknown repos opened in Restricted Mode first
- [ ] Agent approval step left on for shell and network tool calls

One reviewer signs off before a new extension lands in a shared agent profile. The same way a dependency bump gets a second pair of eyes, an extension that shares a process with your credentials earns one too.

What this means for your stack

Start with the two changes that cost nothing and stop both 2026 attacks cold: turn off extension auto-update, and run your agent in a dedicated profile with a short, audited extension list. Auto-update is what turns a republished package into code on your disk without a decision. A minimal profile is what keeps an untrusted extension out of the room where the agent and its credentials live.

The deeper pattern is that the editor host has no internal boundary. An extension, the agent, and your shell all run with the same identity and reach the same secrets. Constraining which extensions load is one half. The other half is making sure that when something does run with your reach, it cannot read a long-lived credential straight out of the environment. A token an extension can read from ~/.config is a token already lost. The fix for that category is to stop storing standing secrets where any process in your session can grab them, and to broker them at runtime with a scope and an expiry instead.

hasp is one working implementation of that runtime brokering. Install with curl -fsSL https://gethasp.com/install.sh | sh, run hasp setup, and the agent gets a short-lived reference instead of a key sitting in a dotfile. Source-available (FCL-1.0), local-first, macOS and Linux, no account.

The vetting routine above stands on its own whatever you use for secrets. The extension you do not install cannot hijack the agent you trust, and the credential that is never written to disk cannot be read off it.

Sources· cited above, in one place

NEXT STEP~90 seconds

Stop handing the agent your real keys.

hasp keeps secrets in one local encrypted vault, brokers them into the child process at exec, and never lets the agent read the value.

  • Local, encrypted vault — no account, no cloud, no telemetry by default.
  • Brokered run — agent gets a reference, the child process gets the value.
  • Pre-commit + pre-push hooks catch managed values before they ship.
  • Append-only HMAC audit log answers "did the agent touch the prod token?" in seconds.
→ okvault unlocked · binding ./api
→ okgrant once · pid 88421
→ okagent never read

macOS & Linux. Source-available (FCL-1.0, converts to Apache 2.0). No account.

Browse all clusters· eight threads, one index