A malicious IDE extension runs with the developer's own rights and usually looks exactly like the tool it claims to be, so it is rarely caught by policy alone. This guide is about detection: building an extension and plugin inventory, triaging a package for the static signals that matter, watching the editor process at runtime, and correlating with identity logs when a developer token is used somewhere it should not be. It covers JetBrains IDEs and Visual Studio Code (with compatible editors that use Open VSX), listed alphabetically, and is written for detection engineers, endpoint teams and platform owners who look after developer fleets. Hardening the workstation itself is covered in the Developer Workstation and IDE Security Guide; this page assumes that baseline and goes deeper on finding what slipped through.
Key takeaways
- Inventory first, detect second. You cannot hunt across extensions you have not listed; collect publisher, ID, version and install source from every local and remote editor host on a schedule.
- Working features are not evidence of safety. The 2026 JetBrains Marketplace campaign shipped AI assistants that did what they promised while sending each user's API key to a hard-coded server.
- The editor process tree is the best runtime signal. Shells, downloaders, raw IP connections and plain HTTP from the IDE or its extension host deserve alerts, tuned against a known-good baseline.
- Identity logs catch what the endpoint misses. Stolen npm, GitHub, marketplace and AI provider keys show up as use from new locations, new tokens or unexpected publishes.
- Update time is attack time. A trusted extension can turn malicious in one release, so detection must re-run on every version change, not only on first install.
Why extension threats evade ordinary controls
Visual Studio Code documentation states that the extension host has the same permissions as VS Code itself, so an extension can read and write files, make network requests, run external processes and modify workspace settings. JetBrains plugins run inside the IDE's JVM with the same reach. MITRE ATT&CK records this as T1176.002 IDE Extensions (persistence), and the initial delivery as T1195.001 Compromise Software Dependencies and Development Tools. What follows is usually credential theft from files and stores (T1552.001 Credentials In Files, T1555 Credentials from Password Stores, T1555.001 Keychain) and exfiltration over web protocols (T1071.001, T1041).
Three properties make these threats hard to spot:
- Legitimate shape: the package is a normal extension with a real feature set. In the JetBrains Marketplace AI plugin campaign, 15 plugins under seven publisher accounts delivered chat, code review and test generation while posting the user's key as plaintext JSON over HTTP when the settings were applied.
- Trusted channel: the code arrives through the official marketplace and auto-update. In the GlassWorm campaign, attackers published with valid publisher tokens, some of which developers had leaked in public repositories, and hid the loader in invisible Unicode characters (T1027.018).
- Developer noise: editors legitimately spawn compilers, language servers, Git and package managers, so naive "IDE spawned a process" rules drown in alerts.
Building the inventory
Detection starts with an accurate, scheduled list of what is installed, where it came from and which version is running. Collect it with your endpoint management or EDR tooling and ship it to the same place as your other asset data.
| Item | JetBrains IDEs | Visual Studio Code |
|---|---|---|
| Default install location (Windows) | %APPDATA%\JetBrains\<product><version>\plugins | %USERPROFILE%\.vscode\extensions |
| Default install location (macOS) | ~/Library/Application Support/JetBrains/<product><version>/plugins | ~/.vscode/extensions |
| Default install location (Linux) | ~/.local/share/JetBrains/<product><version> | ~/.vscode/extensions |
| Relocation setting to check | idea.plugins.path in idea.properties or custom properties | --extensions-dir command line option; portable mode data folder |
| Listing method | Read plugin folders per product and version; each IDE version has its own directory | code --list-extensions --show-versions, with --profile for each profile |
| Logs for triage | idea.log under %LOCALAPPDATA%\JetBrains\<product><version>\log, ~/Library/Logs/JetBrains/<product><version> or ~/.cache/JetBrains/<product><version>/log | Window and Extension Host output logs; Developer: Policy Diagnostics report |
| Central control to compare against | IDE Services plugin rules in a profile | AllowedExtensions policy (extensions.allowed) |
- Cover every product and version: JetBrains keeps a separate plugins directory per product and version, so a developer with IntelliJ IDEA, PyCharm and an older build has three inventories. Sweep them all, not only the newest.
- Cover remote hosts: the VS Code documentation describes local, web and remote extension hosts; with Remote SSH, containers, WSL or Codespaces, extensions can be installed and run on the remote machine. Inventory dev containers, jump hosts and cloud workstations too.
- Cover forks: editors built on VS Code may keep extensions in their own folders and pull from Open VSX rather than the Visual Studio Marketplace. Confirm each fork's paths and registry before you claim coverage.
- Record the source: marketplace, private marketplace or corporate plugin repository, or a sideloaded VSIX or ZIP. VS Code documentation notes that allowlist rules for locally supplied VSIX packages use the identifiers declared inside the package and do not verify where it came from, so sideloads need separate review.
- Diff on change: alert on new IDs, publisher changes and version changes, and keep history so you can answer "who had version X between these dates" during an incident.
Static triage of a suspicious package
When an extension is new to the estate, changes publisher, or appears in threat reporting, unpack it and look for the signals below. None proves malice on its own; several together justify blocking pending review.
| Signal | Where to look | Why it matters |
|---|---|---|
| Eager activation | VS Code package.json activationEvents containing "*" or onStartupFinished | Runs on every editor start. VS Code documentation tells authors to use "*" only when no other event combination works, so a simple formatter or theme that asks for it is out of place. |
| Process and network capability out of scope | Calls to child process, HTTP clients or sockets in the main entry; Java HTTP clients in plugin JARs | A theme or snippet pack has no reason to start processes or contact servers. |
| Hard-coded endpoints | Raw IP addresses, plain http:// URLs, paste sites, blockchain RPC endpoints, calendar or document APIs used as configuration stores | Both campaigns above used a hard-coded IP or a dead drop resolver (T1102.001) rather than the vendor's own API. |
| Hidden or encoded code | Long runs of invisible Unicode (variation selectors), large encoded strings passed to an evaluator, packed bundles that do not match the published source | Invisible characters do not render in editors or diffs, which is why manual review missed GlassWorm. |
| Native binaries | Bundled .node, .dll, .so or .dylib files | Native code escapes JavaScript and JVM level inspection; acceptable for language tooling, odd for most other categories. |
| Credential path references | Strings such as .npmrc, .git-credentials, .ssh, keychain or browser profile paths, cloud CLI config folders | Maps directly to T1552.001 and T1555 collection. |
| TLS weakening | JetBrains plugins that install a JVM-wide trust manager or disable certificate validation | The JetBrains campaign did this; it also weakens TLS for every other plugin in the same IDE. |
| Secret handling in settings | Settings panels that take API keys and send them anywhere other than the provider's documented endpoint | AI assistant plugins collect high-value keys by design; check where those keys travel. |
On updates, diff the new package against the last approved version: new endpoints, new activation events, new native files.
Provenance and marketplace signals
Marketplace protections are a useful first filter and their signals are worth collecting, but neither vendor presents them as a guarantee.
JetBrains Marketplace
- Review: the JetBrains Marketplace approval guidelines say every new plugin and update gets automated checks and a manual review before public release, that plugins must not introduce security vulnerabilities or privacy issues, and that plugins collecting personal or telemetry data must ask for explicit permission. JetBrains may re-review plugins and withdraw approval later.
- Signing: JetBrains documents that plugins are signed twice, by the author and then by JetBrains Marketplace, and that the IDE shows a warning dialog when a plugin is unsigned or its certificate is revoked. Plugins from a custom plugin repository are not re-signed with the JetBrains key; authors use an internal CA or self-signed certificate that users trust through Settings | Plugins | Manage Plugin Certificates or the
intellij.plugins.truststoreproperty. - Detection use: treat any user-added certificate under Manage Plugin Certificates that is not your corporate CA as a finding, and alert on installs that triggered the unsigned plugin warning.
Visual Studio Code and Open VSX
- Marketplace checks: VS Code documentation lists malware scanning with several antivirus engines on each new extension and update, dynamic detection in a clean room VM, name squatting protection, secret scanning, unusual usage monitoring, and a block list that removes verified malicious extensions and uninstalls them from VS Code.
- Verified publisher: the blue check mark shows the publisher proved domain ownership and has been in good standing for at least six months. It says nothing about whether a publisher's account or token was later stolen.
- Publisher trust prompt: since VS Code 1.97 users confirm trust in a third-party publisher on first install, and the trust list is managed with Extensions: Manage Trusted Extensions Publishers. Installs from the command line do not automatically trust the publisher, so scripted installs need their own review path.
- Signature verification: VS Code verifies the Marketplace signature at install. Treat
extensions.verifySignatureset to false as a detection rule in its own right. - Open VSX: after GlassWorm the Eclipse Foundation said, in its 27 October 2025 update, that it had revoked leaked tokens, introduced a token prefix to make leaked tokens easier to scan for, and planned shorter default token lifetimes and scanning at publication. Coverage for forks depends on that registry, so collect its signals separately.
Runtime and network detection
Static triage catches packages before they run; runtime detection catches what triage missed and what changed after an update. Build these rules in your EDR or SIEM and baseline them per team, because a backend Java team and a front-end team produce very different process trees.
| Behaviour | Telemetry | ATT&CK | Tuning note |
|---|---|---|---|
| IDE or extension host spawns a shell, script host or downloader that then reaches the internet | EDR process creation with parent chain and command line | T1059.007, T1176.002 | Allow known build tools by path and hash; alert on new child names per team |
| IDE process reads credential stores or token files it does not normally touch | EDR file access events, macOS keychain access logging | T1552.001, T1555, T1555.001 | Git and package managers read their own config; alert when the reader is the editor process itself |
| Outbound connection from the IDE to a raw IP, over plain HTTP, or to a newly registered domain | EDR network events, proxy and DNS logs | T1071.001, T1041 | Plain HTTP from an IDE is rare and worth alerting on everywhere |
| IDE contacts blockchain RPC endpoints or uses calendar or paste services as configuration | DNS and proxy logs with process attribution | T1102.001 | Allow for teams that build blockchain software; alert elsewhere |
| New persistence created soon after an extension update | Run keys, launch agents, scheduled tasks, cron entries | T1176.002 then platform persistence | Correlate with the inventory diff time window |
| Change to extension security settings | extensions.verifySignature, extensions.allowed user overrides, new JetBrains plugin certificates or truststore property | T1176.002 | Any change on a managed device is worth a ticket |
One refinement makes these rules usable: join the network alert to the inventory: knowing that the IDE that made the connection received an extension update an hour earlier turns a weak signal into a strong one.
Where identity fits: catching stolen developer credentials
Most malicious extensions are after credentials, and the credentials leave traces when they are used. The Developer endpoints as NHI stores guide explains which tokens sit on a typical laptop; here the question is how to see them misused.
- Source control: new personal access tokens, new SSH keys, unusual clone volume and pushes from unfamiliar addresses. See the GitHub Audit Log Detection Guide.
- Package and extension registries: publish events from unexpected locations or at unusual times, especially for packages and extensions your own developers maintain, which is how stolen tokens fed GlassWorm's next wave.
- AI provider keys: usage from unknown addresses, spend spikes and calls to models you do not use. Keep a record of which tools each key was entered into so you know what to rotate.
- Editor sign-ins: the VS Code Developer: Policy Diagnostics report lists authentication providers, accounts and the extensions that have access to each account. Use it in triage to see which extensions could reach a GitHub or Microsoft session.
Rolling out detection
- Collect the inventory (weeks 1 to 2): deploy collection for both JetBrains and VS Code paths, local and remote, and store history. Measure coverage before anything else.
- Compare with policy (week 3): diff installed items against your
AllowedExtensionspolicy and IDE Services rules. Every installed item outside policy is either a policy gap or a bypass. - Stand up static triage (weeks 3 to 4): run the signal checks on every new ID and every version change, in an isolated pipeline. Start in report-only mode and review results weekly.
- Deploy runtime rules in audit (weeks 4 to 6): run the process and network rules without paging, baseline per team, then promote the high-precision ones (plain HTTP, raw IP, credential store reads by the editor) to alerts.
- Wire up identity correlation (weeks 6 to 8): join registry, source control and AI provider logs to the inventory by user so a token alert shows which extensions that developer had.
- Use the update delay as a buffer: VS Code waits before auto-installing a new extension version (
extensions.autoUpdateDelay, default 12 hours, managed through theExtensionsAutoUpdateDelaypolicy). Run triage on new versions inside that window and block bad versions before they land. - Rollback path: to remove a bad extension quickly, add a block rule (
falseinextensions.allowed, which disables installed copies, or an IDE Services Block (Forced) or Disable rule), then follow the Compromised Developer Workstation Playbook for hosts where it ran.
Decision guidance
- Small teams: start with the scheduled inventory, a short allowlist and two runtime rules (plain HTTP and raw IP connections from the IDE). That catches the cheap campaigns without a detection engineering team.
- Large or regulated estates: add version-level triage, a private marketplace or corporate plugin repository so updates flow through your pipeline, and identity correlation. The review load is real, so prioritise extensions with the most installs and the broadest capability.
- Block or investigate: block immediately when an extension is named in threat reporting, has hard-coded raw IP or plain HTTP egress, or reads credential stores outside its purpose. Investigate first when the only signal is eager activation or a native binary in a language tool.
- AI assistant plugins: treat them as high risk by default, because they ask for keys and send code to external services. Prefer centrally provisioned assistants and keys with spending limits over individually installed ones.
Detection and measurement
- Inventory coverage: percentage of developer hosts, including remote and container hosts, reporting an extension and plugin inventory in the last 7 days. Target above 95 percent.
- Out-of-policy installs: count of installed items not matched by allow rules, trending to zero.
- Version triage latency: median time from a new version appearing in your estate (or publicly) to triage completion; aim to finish inside the auto-update delay.
- Mean time to block: time from a public report or internal alert to a block rule enforced fleet-wide.
- Credential rotation completeness: share of tokens and keys reachable from an affected host that were rotated within 24 hours of confirmation.
Common failure patterns
- Trusting the marketplace badge: the JetBrains campaign plugins passed review and stayed live for months; JetBrains later terminated seven publisher accounts and remotely disabled the plugins. Review status is not a detection control.
- Ignoring remote and container hosts: extensions installed on a remote host run there, with access to that host's credentials.
- Checking once at install: GlassWorm arrived as new versions of existing extensions, so a one-time approval let the malicious update in.
- Containing the host but not the identity: reimaging the laptop without rotating npm, GitHub, marketplace and AI keys leaves the attacker with working credentials.
- Tolerating sideloads: VSIX and ZIP installs bypass marketplace scanning, and allowlist rules match the identifiers inside the package, so a sideload can claim to be an approved extension.
Framework mapping
| Framework | Reference | How detection supports it |
|---|---|---|
| NIST CSF 2.0 | ID.AM-02, PR.PS-05, DE.CM-09 | Software inventory, preventing unauthorised software, monitoring runtime environments |
| NIST SP 800-53 Rev. 5 | CM-7(5), CM-11, SI-4, SI-7, SR-11 | Allow-by-exception software, user-installed software, system monitoring, integrity and component authenticity checks |
| CIS Controls v8.1 | 2.1, 2.5 | Software inventory and allowlisting authorised software |
| ISO/IEC 27001:2022 Annex A | A.8.7, A.8.16, A.8.19 | Protection against malware, monitoring activities, installation of software |
| MITRE ATT&CK v20 | T1176.002, T1195.001, T1552.001, T1555, T1102.001, T1027.018 | Technique coverage for the detections in this guide |
Practitioner checklist
- Collect JetBrains and VS Code inventories from every product, version, profile and remote host at least daily, with history.
- Check
idea.plugins.pathand--extensions-dirrelocations so inventories do not miss moved folders. - Diff installed items against
AllowedExtensionsand IDE Services rules and ticket every mismatch. - Run static triage on every new extension ID and every new version, in isolation, before or inside the update delay.
- Alert on plain HTTP and raw IP connections from IDE processes, and on credential store reads by the editor.
- Alert on
extensions.verifySignatureset to false and on new plugin certificates or truststores. - Join registry, source control and AI provider logs to the inventory by user.
- Pre-stage block rules and a rotation list so a bad extension can be disabled and its reachable tokens rotated within a day.
Standards and references
- MITRE ATT&CK: IDE Extensions (T1176.002)
- Visual Studio Code: Extension runtime security
- Visual Studio Code: Manage extensions in enterprise environments
- Visual Studio Code: Activation Events
- JetBrains: Directories used by the IDE to store settings, caches, plugins and logs
- JetBrains: Plugin Signing
- JetBrains IDE Services: Manage available plugins
- Eclipse Foundation: Open VSX security update, October 2025
Related NHI Mgmt Group resources: Developer Workstation and IDE Security Guide · GlassWorm VS Code Extension Worm Guide · Compromised Developer Workstation Playbook · AI IDE and Coding Agent Security Guide