Security scanner for VS Code extensions. Detects malicious extensions before installation by analyzing code patterns, indicators of compromise, and known malware signatures.
VS Code extensions run with full trust and the same permissions as the editor itself. Malicious extensions have been found in both the official Microsoft marketplace and OpenVSX:
- Credential theft - SSH keys, browser cookies, password manager databases
- Cryptocurrency theft - Wallet files, clipboard hijacking for addresses
- Source code exfiltration - Stealing proprietary code via Discord webhooks
- Cryptominers and RATs - CoinIMP miners, ScreenConnect RATs, multi-stage loaders
- Self-propagation - GlassWorm spread by modifying installed extensions
The scanner runs 9 first-class detection modules against each extension. The legacy
package module selector remains as a compatibility alias for manifest, execution,
deps, and intel.
Examines package.json, extension manifest behavior, runtime execution patterns, and
package-related threat intelligence.
| Check | What It Detects | Severity |
|---|---|---|
| Blocklist | Extension ID matches known malicious extensions | Critical |
| Wildcard activation | activationEvents: ["*"] - runs on every action |
High |
| Startup activation | onStartupFinished - runs at VS Code launch |
Medium |
| Theme with code | Theme extension that has a main entry point |
High |
| Malicious npm packages | Dependencies matching known malware package names | Critical |
| Malicious npm versions | Resolved/bundled dependency versions matching known compromised releases | Critical |
| Typosquatting | Dependencies within edit distance 1-2 of popular packages (lodash, axios, etc.) | High |
| Lifecycle scripts | preinstall/postinstall scripts with suspicious patterns |
Critical/Medium |
| Hidden task execution | VS Code tasks executed with suppressed task UI | High |
| GitHub SHA execution | npx/npm exec against github:owner/repo#sha |
Critical |
| Startup execution | onStartupFinished paired with task/process launch behavior |
High |
Matches against curated threat intelligence from zoo/iocs/.
| Check | Detection Method | Severity |
|---|---|---|
| Malware hashes | SHA256 hash of files matches known samples | Critical |
| C2 domains | Domain extraction matched against blocklist | Critical |
| C2 IPs | IPv4 extraction (excludes private ranges) matched against blocklist | Critical |
| GitHub C2 | GitHub API/raw URLs referencing known C2 accounts | Critical |
| Crypto wallets | BTC (legacy/SegWit/Bech32), ETH, Monero, Solana address patterns | High |
OXC-based parsing to detect structural patterns that regex can't catch.
| Rule | What It Detects | Severity |
|---|---|---|
AST_EVAL_DYNAMIC |
eval(variable) - non-literal argument |
High |
AST_FUNCTION_CONSTRUCTOR |
new Function(string) - runtime code generation |
High |
AST_DYNAMIC_REQUIRE |
require(variable) - computed module loads |
Medium |
AST_DYNAMIC_IMPORT |
import(variable) - computed dynamic imports |
Medium |
AST_PROCESS_BINDING |
process.binding() - Node.js internals access |
High |
AST_GLOBAL_THIS_EVAL |
globalThis.eval - indirect eval access |
High |
Detects obfuscation techniques used to hide malicious intent.
| Rule | Detection Method | Severity |
|---|---|---|
OBFUSCATION_HIGH_ENTROPY |
Shannon entropy >5.5 bits/char (200-char windows) | Medium |
ZERO_WIDTH_CHARS |
Zero-width spaces/joiners hiding code (U+200B-200D) | High |
VARIATION_SELECTOR |
GlassWorm-style hidden data in variation selectors (U+FE00-FE0F) | Critical |
BIDI_OVERRIDE |
Trojan Source attacks using bidirectional overrides (U+202A-202E) | Critical |
UNICODE_ASCII_ESCAPE |
\u00XX escapes for normal ASCII chars (obfuscation) |
Medium |
CYRILLIC_HOMOGLYPH |
Cyrillic letters that look like Latin (а/a, е/e, с/c) | High |
OTHER_INVISIBLE_CHARS |
Soft hyphens, combining marks, format controls | Medium |
INVISIBLE_CODE_EXECUTION |
Invisible chars near eval/Function/exec |
Critical |
JavaScript obfuscation patterns (hex variables, fromCharCode arrays, eval+decode, packers) are now detected via YARA rules for better accuracy.
External YARA-X engine for complex pattern matching. Rules loaded from zoo/signatures/yara/. Requires yr CLI (brew install yara-x).
| Rule File | Detects |
|---|---|
blockchain_c2.yar |
Solana RPC C2, memo parsing, blockchain-based command channels |
blockchain_c2_extended.yar |
Ethereum smart contract C2 via ethers.js |
code_execution.yar |
eval, Function constructor, child_process patterns |
credential_harvesting.yar |
NPM/GitHub/SSH credential theft, .npmrc access |
crypto_wallet_targeting.yar |
MetaMask, Phantom, Exodus wallet extension targeting |
data_exfiltration.yar |
Discord webhooks, SSH key theft, browser data exfil |
google_calendar_c2.yar |
Google Calendar API abuse for C2 communication |
multi_stage_attacks.yar |
Dropper chains, reverse shells, keylogger patterns |
native_addon_loader.yar |
Suspicious .node native addon loading patterns |
obfuscation_patterns.yar |
Hex variables, fromCharCode, packed/encoded code |
persistence_macos.yar |
LaunchAgent persistence, Apple masquerading, Login Items |
powershell_attacks.yar |
Hidden windows, -ExecutionPolicy Bypass, AMSI evasion |
rat_capabilities.yar |
SOCKS proxy, VNC, remote command execution |
rmm_tool_delivery.yar |
ScreenConnect, AnyDesk, TeamViewer delivery via extensions |
self_propagation.yar |
GlassWorm-style worm propagation via extension modification |
unicode_stealth.yar |
Invisible Unicode, variation selectors, homoglyphs |
Detects when extensions send data to external analytics, crash-reporting, or APM services.
| Check | What It Detects | Severity |
|---|---|---|
| SDK imports | Known telemetry packages (Sentry, Mixpanel, PostHog, AppInsights) | High/Medium |
| Endpoint URLs | URLs matching known telemetry services from zoo/telemetry/ |
High/Medium |
| Telemetry paths | API paths like /collect, /track, /ingest, /metrics |
High/Medium |
| Data collection | Patterns near telemetry code (machine_id, user_id, file_paths) | Informational |
Opt-out detection: Severity is reduced from High to Medium when extensions respect user preferences:
vscode.env.isTelemetryEnabled- VS Code's global telemetry setting- Manifest configuration - Extension exposes a telemetry toggle setting
- Code conditional - Checks a user preference before sending
Every finding includes context to help quickly determine if it's a real threat:
- Legitimate uses - Why this pattern exists in benign extensions
- Red flags - What makes the same pattern suspicious in context
- Severity - Critical/high/medium/low based on risk and intent signals
- Location - File path and line number for the match
The goal is rich context for human/agent review, not just pass/fail.
brew install yara-x
npm install -g @trailofbits/vsix-auditRequires Node.js 22 or later and YARA-X for YARA rule scanning.
Scan an extension for security issues:
vsix-audit scan ./extension.vsix
vsix-audit scan publisher.extension-name
vsix-audit scan openvsx:publisher/extension-nameDownload an extension for offline analysis:
vsix-audit download ms-python.python
vsix-audit download ms-python.python@2024.1.0 -o ./downloads
vsix-audit download openvsx:redhat/javaShow extension metadata:
vsix-audit info ./extension.vsix
vsix-audit info ./extension-folderDisplays: name, publisher, version, activation events, entry points, contributions, dependencies, file count, and size.
| Prefix | Registry |
|---|---|
| (none) | VS Code Marketplace (default) |
marketplace: |
VS Code Marketplace (explicit) |
openvsx: |
Open VSX Registry |
cursor: |
Cursor Extension Marketplace |
| Option | Description |
|---|---|
-o, --output <format> |
Output format: text, json, or sarif (default: text) |
-s, --severity <level> |
Minimum severity to report: low, medium, high, critical (default: low) |
-m, --module <names> |
Comma-separated modules to run; package aliases manifest,execution,deps,intel |
-r, --recursive |
Recursively scan all .vsix files in a directory |
-j, --jobs <n> |
Number of parallel scans (default: 4, used with --recursive) |
--no-network |
Disable network-based checks |
--no-cache |
Bypass cache, download fresh |
--force |
Re-download even if cached |
--all-registries |
Scan from all registries (Marketplace + OpenVSX + Cursor) |
--threat-intel <mode> |
Threat intelligence mode: local or none (default: local) |
--no-threat-intel |
Disable threat-intelligence datasets while keeping generic detections |
--strict |
Exit with an error if scan coverage is degraded |
--require-yara |
Exit with an error if YARA scanning cannot run |
vsix-audit reports coverage problems as findings and in JSON metadata. Examples include skipped ZIP entries, duplicate normalized archive paths, manifest entry points that were missing or skipped, AST parser recovery errors, and unavailable scanner modules.
Use --strict for production gating when an incomplete scan should fail closed. Use --require-yara when YARA coverage is mandatory in CI.
Downloaded extensions are cached for faster subsequent scans:
| Platform | Cache Location |
|---|---|
| macOS | ~/Library/Caches/vsix-audit/ |
| Linux | $XDG_CACHE_HOME/vsix-audit/ or ~/.cache/vsix-audit/ |
Extensions are organized by registry (marketplace/, openvsx/, cursor/).
Set VSIX_AUDIT_CACHE_DIR to override the cache location, which is useful for CI and sandboxed environments.
Cache management commands:
vsix-audit cache path # Print cache directory
vsix-audit cache list [--json] # List cached extensions
vsix-audit cache clear [pattern] # Clear cache (optional glob pattern)
vsix-audit cache info <ext-id> # Show cached versionsExamples:
# First scan downloads to cache
vsix-audit scan ms-python.python
# → Downloaded ~/.cache/vsix-audit/marketplace/ms-python.python-2024.1.0.vsix
# Second scan uses cache
vsix-audit scan ms-python.python
# → Using cached ~/.cache/vsix-audit/marketplace/ms-python.python-2024.1.0.vsix
# Force re-download
vsix-audit scan --force ms-python.python
# Bypass cache entirely
vsix-audit scan --no-cache ms-python.python
# Clear specific extensions
vsix-audit cache clear ms-python.*Text (default) - Human-readable report for terminal output.
JSON - Machine-readable results for integration with other tools.
SARIF - Static Analysis Results Interchange Format for CI/CD integration.
| Code | Meaning |
|---|---|
| 0 | No findings |
| 1 | Findings detected |
| 2 | Error during scan |
In --strict mode, exit code 2 is also used for degraded scanner coverage. With --require-yara, exit code 2 is used when YARA scanning cannot run.
The zoo/ directory contains threat intelligence for detection:
| Directory | Contents |
|---|---|
zoo/blocklist/ |
Known malicious extension IDs with campaign attribution |
zoo/iocs/ |
SHA256 hashes, C2 domains/IPs, crypto wallets, malicious npm packages and versions |
zoo/signatures/ |
YARA rules for credential harvesting, RAT behavior, self-propagation |
zoo/telemetry/ |
Known telemetry service domains (analytics, crash-reporting, APM) |
IOCs sourced from: GlassWorm, Evelyn, TigerJack, OctoRAT, WhiteCobra, Shiba, MUT-9332, FAMOUS CHOLLIMA, HardHatRAT, SnowShoNo, SleepyDuck, PokemonMiner, TheseVibesAreOff, SoliditySquatters, ReversingLabs-Dec2025
Malware samples are in a separate private repository (trailofbits/vsix-zoo) to avoid Dependabot alerts and AV triggers.
# Clone the samples repo (requires access)
git clone git@github.com:trailofbits/vsix-zoo.git ../vsix-zoo
# Run tests with samples
VSIX_ZOO_PATH=../vsix-zoo/samples npm testPinned benign extension metadata lives in test-corpus/clean/manifest.json. The VSIX files are downloaded locally and ignored by git.
# Download pinned clean corpus artifacts and verify SHA-256 hashes
npm run corpus:download
# Scan the local clean corpus
vsix-audit scan test-corpus/clean --recursive --no-network
# Generate a false-positive report
npm run corpus:report
# Fail if any never-in-clean findings appear
npm run corpus:checkgit clone https://github.com/trailofbits/vsix-audit.git
cd vsix-audit
npm install
prek install # pre-commit hooks
npm run check # typecheck + lint + testAGPL-3.0