Skip to content
| Marketplace
Sign in
Visual Studio Code>Linters>ByteGuardNew to Visual Studio Code? Get it now.
ByteGuard

ByteGuard

Tseegii Tselmeg

|
5 installs
| (0) | Free
Reads bytes, not filenames. Scans a workspace the moment you open it for supply-chain malware: disguised binaries, invisible Unicode, auto-running tasks, loader markers. Review-and-delete cleanup, plus VS Code and npm/yarn/pnpm integrity checks.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

ByteGuard

Reads bytes, not filenames.

A VS Code extension that scans a workspace the moment you open it for PolinRider / GlassWorm supply-chain malware, lets you review and delete what it finds, and checks that VS Code and your Node toolchain have not been patched.

It exists because of a real infection. The entry point was public/fonts/fa-solid-400.woff2 — a "font" that was actually whitespace-padded JavaScript, launched by a .vscode/tasks.json task with runOn: folderOpen. Every scanner that trusted the file extension walked straight past it. ByteGuard checks what a file contains, never what it is called.

Requires VS Code 1.85 or newer. Runs in untrusted workspaces — you want the scan to happen before you trust a folder.

What it catches

Disguised binaries .woff2, .png, .wasm and 12 other types whose magic bytes don't match the extension — the font-file vector
Loader indicators 22 regex rules: victim tags, app-vscode-eval, inz artifacts, C2 paths and hosts, the stage-2 XOR key, build markers, eval(Buffer.from(…))
Auto-running tasks .vscode/tasks.json entries with runOn: folderOpen, hidden output, or an interpreter pointed at a binary-named file
npm lifecycle hooks postinstall and friends that pipe curl to a shell, use node -e, or execute binary-named files
Trojan-Source Unicode Bidi controls left unbalanced on a line, so what a reviewer reads is not what compiles, plus zero-width and private-use characters, and joiners wedged inside an identifier
Editor & toolchain VS Code's out/main.js, plus npm, yarn, pnpm and Claude Code — the loader reinfects from these, so a clean editor alone proves nothing

Critical findings raise a modal. Everything lands in the Problems panel, the ByteGuard output channel, and a status-bar item.

Install

git clone https://github.com/n0m4dz/ByteGuard && cd ByteGuard
npm install --ignore-scripts
npm run compile
npx @vscode/vsce package --no-dependencies
code --install-extension byteguard-1.0.0.vsix

Press F5 to run it in an Extension Development Host instead.

Commands

Command What it does
ByteGuard: Scan Workspace Rescan now
ByteGuard: Deep Scan Same scan including node_modules, vendor/ and minified bundles. Run it after any npm install you did not fully trust
ByteGuard: Check Editor and Toolchain Integrity VS Code out/ plus npm / yarn / pnpm / Claude Code
ByteGuard: Open Rules File Opens the most specific rules file that exists
ByteGuard: Create Workspace Rules Scaffolds .byteguard/rules.json

Review and delete

The modal's Review & delete button opens a checklist: one row per file, showing which rules fired and why. You tick what goes, then confirm the count. Nothing you did not tick is touched.

Only files that are payload in their entirety are pre-ticked — a "font" that is really a script. A tasks.json with real build tasks or a package.json you still need is listed but left unticked, because the fix there is to remove one entry by hand. Unicode findings are never offered: an invisible character is a property of a line, not a reason to delete a file.

Deletion is permanent — files do not go to the Trash. A record of path, rules, size and SHA-256 is written to ~/.byteguard/deleted/, but never the contents. The workspace is rescanned after.

A patched editor or toolchain is never deleted. Reinstall VS Code from code.visualstudio.com and reinstall Node; deleting main.js mid-session breaks the install without removing the persistence.

Rules

Rules are JSON, merged in four layers — later wins:

  1. rules/default.rules.json (shipped)
  2. ~/.config/byteguard/rules.json (personal)
  3. <workspace>/.byteguard/rules.json (per project — commit it)
  4. the byteguard.rulesFile setting

JSONC comments and trailing commas are fine; the bundled schema gives autocomplete.

{
  "version": 1,
  "rules": [
    // add a rule
    { "id": "project.cdn_key_leak", "severity": "critical",
      "pattern": "X-API-KEY\\s*[:=]\\s*['\"][A-Za-z0-9]{20,}", "message": "Hardcoded CDN key" },
    // change only the severity of a shipped rule — pattern and message carry over
    { "id": "ioc.curl_pipe_sh", "severity": "medium" },
    // turn one off
    { "id": "ioc.xor_decode_loop", "enabled": false }
  ],
  // silence a known-good file: file glob + rule glob
  "allow": [
    { "file": "**/Pods/Mantle/**", "rule": "unicode.*", "reason": "upstream LRM in headers" }
  ]
}

Rule fields: id, severity (critical | high | medium), pattern (JS regex source), flags, message, files (globs limiting the rule), enabled.

Three things that surprise people:

  • Reusing an id replaces fields, it does not append. A new pattern overwrites the old one.
  • The masquerade, tasks, unicode and npmHooks blocks replace each key wholesale. Listing three extensions leaves only those three checked — paste the full default list and edit it. Extensions with no known magic bytes (.svg, for one) have no effect at all.
  • allow entries from every layer are concatenated and cannot be removed, only narrowed.

Prefer an allow entry over "enabled": false — disabling a rule turns it off everywhere.

Security docs flag themselves

Any file quoting an indicator matches the rule that hunts for it, so an incident report or a runbook lights up with criticals that are all its own prose. That is the rules working. This repo handles its own case in .byteguard/rules.json: every entry names a single file, never a directory, so a payload planted beside one is still caught. npm test proves that by planting one in each allowed location.

Settings

Setting Default
byteguard.scanOnOpen true Scan when a folder opens
byteguard.checkSelfOnStartup true Check editor and toolchain at startup
byteguard.maxFileSizeKB 2048 Skip larger files
byteguard.excludeGlobs node_modules, .git, Pods, build, dist, .dart_tool, vendor, *.min.js Paths to skip
byteguard.deepScanExcludeGlobs **/.git/** Paths to skip during a deep scan
byteguard.rulesFile "" Extra rules file, applied last

Limits

  • Each rule reports its first match per file — a finding tells you the file is dirty, not how many times. The Unicode check caps at 20 findings per file.
  • The Unicode check is aimed at code review, so it skips generated lines over 2000 characters and ignores characters that ordinary writing needs: ZWNJ and ZWJ (Persian, Arabic, Devanagari, emoji), variation selectors, and balanced bidi isolates around right-to-left strings. A regex character range whose endpoint happens to be a bidi code point can still produce a false positive — two did, across 64,000 files in 80 extensions.
  • ZWNJ and ZWJ are the exception to that exception when they sit between two ASCII identifier characters (unicode.identifier_joiner, high). Orthographic joiners are flanked by their own script's letters and emoji joiners by emoji, so this placement only occurs inside an identifier — where fetch\u200cData renders exactly like fetchData and binds to something else. Unlike a zero-width space, which is not a valid identifier part and throws at parse time, this one runs. Zero hits across the same 64,000 files.
  • The default scan skips node_modules and vendor for speed. That is exactly where a compromised dependency lives — that is what Deep Scan is for.
  • Symlinks are not followed.
  • The toolchain check covers ~/.nvm, ~/.npm, ~/.local/share/claude, /usr/local and /opt/homebrew. Installs elsewhere, and other editors, are not checked.
  • Detection is signature- and heuristic-based. A clean scan means these checks found nothing. It is not proof of safety.

Keep "task.allowAutomaticTasks": "off" and npm config set ignore-scripts true regardless.

Zero runtime dependencies

Node built-ins and the vscode API only. A supply-chain scanner with a supply chain of its own would be a poor joke.

MIT licensed. Contributions welcome — especially new indicators.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft