Safeguard
Application Security

Building Secure VS Code Extensions: A Developer's Guide

VS Code extensions run as trusted Node.js code with full disk and network access and no permission model to fall back on. Here is how to build one that does not become the next supply chain incident.

Safeguard Research Team
Research
6 min read

VS Code extensions do not run in a sandbox. When a user installs your extension, its code loads into the Extension Host — a Node.js process with the same filesystem and network access as the person running the editor — and there is no manifest-based permission system gating what it can touch, unlike a Chrome extension declaring tabs or storage scopes. That design choice has consequences: researchers tracking the VS Code Marketplace reported in 2024 that more than 1,283 published extensions contained confirmed malicious code, cumulatively installed roughly 229 million times, and a 2026 follow-up found "AI" extensions with 1.5 million installs quietly exfiltrating source code back to remote servers. Microsoft's main structural mitigation, Workspace Trust, shipped in VS Code 1.57 in May 2021 and was refined the following month in 1.58 — but it is opt-in and self-reported by extension authors, not enforced by the runtime. If you're building an extension, none of this is someone else's problem to solve; the extension you ship inherits every one of these gaps unless you actively design around them. This guide covers what the Extension Host actually grants you, how Workspace Trust works and where it stops protecting users, and the concrete patterns for handling workspace files, secrets, and network calls without becoming the next entry in that 1,283-extension count.

What does the Extension Host actually grant your code?

The Extension Host grants your extension the full privileges of a standalone Node.js process — unrestricted fs access to any path the OS user can read or write, unrestricted outbound network access via http/https or fetch, and the ability to spawn child processes with child_process.exec. There is no per-extension capability list VS Code enforces at runtime; the package.json manifest declares activation events (when your code loads) and contribution points (what UI you add), but neither restricts what your code does once it runs. The one partial exception is Web Extensions: extensions that declare "browser" as their entry point and run inside vscode.dev execute in a browser Web Worker with no Node.js APIs and no filesystem access at all. If your extension doesn't need fs or child_process, shipping a Web Extension variant is the closest thing to real sandboxing VS Code currently offers — everything running in the desktop Extension Host gets ambient trust by default.

How does Workspace Trust actually limit a malicious or compromised extension?

Workspace Trust limits what runs, not what an already-running extension can do. When a user opens a folder VS Code hasn't seen before, it can launch in Restricted Mode, which disables tasks, debug configurations, and workspace-level settings.json overrides that could otherwise auto-execute code. Extensions declare their own behavior via capabilities.untrustedWorkspaces.supported in package.json, set to true, false, or "limited" — but this is a self-reported declaration, not a sandbox boundary VS Code enforces against your code. If you set false, your extension simply doesn't activate in untrusted workspaces; nothing stops it from reading arbitrary files if it does activate. As an author, the practical takeaway is to gate any feature that reads workspace-relative paths, spawns a shell, or evaluates workspace-provided config behind an explicit vscode.workspace.isTrusted check, and to fail closed — skip the feature, don't warn and proceed — when a workspace is untrusted.

What's the real-world attack pattern extensions need to defend against?

The dominant pattern is trust-then-defect: an extension builds a legitimate install base, then a compromised publisher account or a malicious update ships a payload months later, when scanners and reviewers have stopped looking closely. Documented incidents include typosquats of extremely popular extensions — a cloned "Dracula Official" theme was used to compromise over 100 organizations by mimicking a trusted, widely-installed theme rather than a functional tool, since even a color theme runs arbitrary activation-time JavaScript. Other campaigns hardcode C2 IPs, spoof a legitimate publisher's GitHub link in the marketplace listing, or bundle an unsigned executable that runs on first activation. From a defensive-authoring standpoint, the lesson is symmetrical: publish from a hardware-key-protected account, pin and audit every dependency in your extension's own node_modules, and never ship an update that silently adds new capabilities like a new network call or child_process usage without calling it out in your changelog — the same behavioral discipline you'd want from the extensions you install yourself.

How should an extension handle workspace files and secrets safely?

Treat every path, filename, and file contents that comes from the open workspace as untrusted input, the same way a web app treats a query parameter. Never pass a workspace-relative path into child_process.exec or a shell string without escaping — use child_process.execFile with an argument array instead, which avoids shell interpretation entirely. Use the SecretStorage API (context.secrets) for any credential your extension needs to persist, rather than writing tokens into globalState, workspace/.vscode/settings.json, or a plaintext file — SecretStorage delegates to the OS credential vault (Keychain, Credential Manager, libsecret) instead of storing plaintext on disk. If your extension needs to read a config file the workspace provides — a .eslintrc, a tsconfig.json — parse it with a strict JSON/YAML parser rather than eval or require(), since require()-ing a workspace-controlled .js config file executes it with your extension's full privileges the instant the workspace loads.

What should an extension avoid doing over the network?

Avoid making network calls with data derived from workspace content unless the user has explicitly and specifically consented to that feature — this is exactly the pattern the 2026 malicious "AI" extensions abused, framing wholesale source-code upload as a feature ("AI code review," "cloud autocomplete") to get consent for exfiltration by design. Pin the exact hosts your extension talks to and document them in your README and marketplace listing; a support-chat widget or telemetry pixel calling out to a domain nobody can explain in a support ticket is a red flag reviewers and users can actually catch. Avoid dynamically constructing request URLs or headers from workspace file contents — a crafted .env value should never end up interpolated into a URL your extension fetches, since a phrase like `https://api.example.com/${workspaceValue}` built from untrusted input is a straightforward path to SSRF or credential leakage if that value happens to contain a full replacement URL or unexpected host.

How Safeguard Helps

Safeguard's Eagle classifier currently scores artifacts across npm, PyPI, Maven Central, RubyGems, NuGet, crates.io, Go modules, Composer, major container registries, and Hugging Face for install-script behavior, obfuscation, egress patterns, and credential-harvesting signatures — the same indicator classes that would flag a trojanized VS Code extension, but the VS Code Marketplace and Open VSX are not yet ecosystems Eagle covers directly. Where Safeguard helps today is upstream of the editor: any extension you build almost certainly ships its own node_modules, and Safeguard's SCA and reachability analysis will catch a vulnerable or malicious transitive dependency in your extension's own dependency tree before you publish, the same way it does for any other Node.js project. If your team maintains internal VS Code extensions, running your build through Safeguard's existing npm coverage — SBOM generation, malware classification on every dependency, and secrets scanning across your extension's source — closes the gap that the Marketplace itself does not.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.