Reference

Every command, and what it will not do

snare is about 2,200 lines of bash. No framework, no npm install — deliberately, because npm is the attack surface. The core heuristic is language-agnostic; the config list is tuned for the JavaScript ecosystem, because that is where this family lives.

Start here

The guided one

If you do not want to choose between the commands below, this runs the whole sequence and asks you at each step. It is the same work, in the order that actually helps.

Guided clean-up
snare respond                   walk through everything, one question at a time
snare respond --status          where you got to
snare respond --reset           start the walkthrough again

Six steps: check the machine, rotate credentials, clean the machine, find what is infected, fix it, tell your collaborators — then a verification pass that re-reads rather than trusting what just happened. Anything that writes to a repository defaults to no, and the choice to purge history is asked once rather than per repository. Progress is saved, so stopping and resuming is fine.

It refuses to run unattended. With no terminal, or CI set, it exits rather than prompting where nobody can answer — use the individual commands there.

Find it

Scanning

Scanning
snare scan                      scan the current directory
snare scan repo [path]          one clone: working tree, all branches, full history
snare scan repo [path] --new    only findings that are not in the baseline
snare scan orgs                 list every org/account you can reach, with repo counts
snare scan orgs --all           include individual accounts, not just organisations
snare scan github               every repo you can reach, via the API (no cloning)
snare scan github --pick        choose which orgs to scan, from a list
snare scan github --owner a,b   only these orgs/accounts (comma-separated)
snare scan github --mine        only repos you own
snare scan github --all-branches
snare doctor                    this machine, persistence spots, and your GitHub account
snare selftest                  prove detection still works against known samples

Scanning everything is often the wrong first move. A real account reaches further than people expect — one we tested sees 22 organisations and roughly 950 repositories, which is several thousand API calls. scan orgs lists what is reachable and how big each owner is, so you can start with the ones that matter; scan github --pick shows the same list and lets you choose from it. The repo counts are the number --owner will actually scan, not an estimate.

Read this before trusting a clean result. scan github reads branch tips through the API. It cannot see history, and it fetches a bounded set of files. A payload that was committed and later removed, or one hidden somewhere it does not look, will not appear. scan repo against a real clone is the thorough check — it walks every branch and every commit. The infection that started all of this survived a clean 127-repository API sweep and was only caught by scan repo.

selftest exists because four defects once made snare report clean on infected repositories. It builds a scratch repo of known-bad and known-good samples, runs the real scanner, and exits non-zero on any mismatch. If it fails, do not trust any clean result until it passes.

Stop it coming in

The gate, for you and for coding agents

Gate · agents
snare agents install            gate every clone, pull, install and run an agent does
snare agents status | uninstall
snare agents install --project  also this repo's CLAUDE.md / AGENTS.md and a project hook
snare gate -- <command>         scan first; exit 0 means safe to run it
snare gate allow [dir [branch]|url]  you looked and it is yours: allow what was flagged,
                                on disk or still incoming (a branch, a pull, a clone)
snare gate allowed | forget     review or withdraw what you allowed

A block is what snare suspects, not a verdict. It lists each finding as the file, the line and what is suspected, so you can look — it can be a false alarm. Then you decide: snare gate allow if it is yours, or snare respond to let snare take charge. Approvals live in ~/.snare, never in the repository, and lapse if the file changes. A coding agent cannot approve its own block: it is told to stop and show you, and an agent that tries is refused.

The gate answers one question before a command runs: does it bring code in or execute it, and is that code clean? git clone is cloned to a quarantine directory and scanned first. git pull, merge, rebase and checkout fetch — which runs nothing — and scan the incoming tree in a throwaway worktree before a file lands. Installs and npm run scan the directory they would run in. It fails closed: if the scan cannot finish, the command does not run.

For coding agents, snare agents install adds a Claude Code PreToolUse hook — enforced, so the agent cannot skip it — and a short instruction block in CLAUDE.md, AGENTS.md and GEMINI.md for agents without hooks. A block reads "FIRE ON THE MOUNTAIN", tells the agent to stop and tell you, and raises a desktop notification. Ordinary commands like git status cost about 30ms. install.sh and snare update show what it would change and ask first; after a yes, snare update keeps it current, and snare agents uninstall removes it and keeps it removed.

Every ecosystem

Known-malicious packages: npm, Python, Rust, Go and more

Deps
snare deps [dir]                 lockfiles, node_modules and virtualenvs against the list
snare deps --system              also your global Python, pipx, npm and cargo installs
snare deps fix [dir]             remove each one, or replace it with a version not listed
snare deps update | status       refresh / show the known-malware list

The list is OpenSSF's malicious-packages database — about 117,000 packages across npm, PyPI, crates.io, Go, RubyGems, Packagist, NuGet and Maven — condensed every six hours into one small file. Every snare downloads the same file and matches it on its own machine, so nothing about your packages is ever sent. Lockfiles read: npm, pnpm, yarn, bun, requirements*.txt, poetry, uv, pdm, pylock.toml, Pipfile, Cargo.lock, go.sum, Gemfile.lock, composer.lock and NuGet's packages.lock.json.

The gate uses the same list: npm i x@1.2.3, npx, pip install x==1.0, uv add, cargo add, go get, gem install and composer require are stopped when the version is listed, and a repository's lockfiles are checked before npm install or pip install -r. It also treats the files other languages run on their own as routes: setup.py, conftest.py, build.rs, sitecustomize.py, .pth files (Python runs them at every start — the LiteLLM 1.107.0 payload was one) and //go:generate lines.

Stop it running

Guard and shield

Guard · shield
snare guard install             kill the loader on sight, from login onwards
snare guard scan                one-shot check of this machine
snare guard status | log | stop | start | uninstall

snare shield install            scan before npm/pnpm/yarn/bun/npx and git clone run
snare shield status | uninstall
snare shield check [path]       the fast check on its own (~22ms)

The guard is a background process (launchd on macOS, a systemd user unit on Linux, a logon Scheduled Task on Windows). It polls once per second, so a payload can still act before it is caught, and it cannot see code already running inside another process.

The shield wraps package managers in your shell so a scan happens before they can execute anything. Honest limits: it is shell-only, so it cannot see VS Code's own folderOpen task — that is the guard's job — and anyone can bypass it with command npm or SNARE_SKIP_SHIELD=1. It is a seatbelt, not a sandbox.

Stop it spreading

Hook and CI

Hook · CI
snare hook install              block a push that carries a payload
snare hook install --with-pre-commit
snare hook status | uninstall

snare ci install                write a GitHub Actions workflow into this repo
snare ci status                 is the workflow installed, and is the check required?
snare ci print                  print the workflow without writing it

This family injects into commits from an already-infected machine, so the payload leaves before anyone scans a repository. The hook is the only feature that prevents that rather than detecting it afterwards. It also refuses the push when the machine itself has a live detection, because a clean repo pushed from an infected machine can still carry the payload out.

GitHub.com cannot reject a push outright — server-side pre-receive hooks are an Enterprise feature. A required status check is the practical equivalent: the merge button stays disabled until the scan passes. ci install writes the workflow, but you must tick it as required under Settings → Branches. Until you do, it reports and blocks nothing — snare ci status will tell you when that is the case.

Clean up

Fix, rotate and notify

Fix · rotate · notify
snare fix <owner/repo>                        dry run (always backs up first)
snare fix <owner/repo> --push                 clean branch tips and push
snare fix <owner/repo> --purge-history --push erase it from all history
snare fix --all                               every repo the last scan flagged
snare fix --pick                              choose orgs, scan them, then fix
snare fix --pick --purge-history --push       ... and erase it from all history
snare fix --owner a,b --push                  the same, without the prompt

snare rotate                    what to revoke, in order, plus a GitHub audit
snare notify <owner/repo>       list collaborators and preview the notice
snare notify <owner/repo> --issue | --mail

Rotate before you clean. Stealing credentials is what this family is for; the file in your repository is delivery, not payload. Removing it does not un-steal a token. snare rotate lists what to revoke in order — npm write tokens first, because a stolen one lets the worm publish trojanised versions of your other packages under your name — and audits your GitHub account for the repositories and secret-exporting workflows this family leaves behind. The full sequence is on the response page.

fix --pick is the whole clean-up in one pass: choose organisations from the same list scan orgs prints, scan them, then remediate what was found. It is a dry run unless you add --push, and the destructive flags stay on the command line rather than hiding inside a friendlier verb — force-pushing rewritten history across an organisation should be legible in your shell history. --owner a,b is the same thing without the prompt, for a script or a timer. With --purge-history it checks for git-filter-repo before scanning, rather than after asking you to choose and then failing on the first repository.

--purge-history rewrites every commit, so every SHA changes. Collaborators must re-clone, not pull, and forks and pull-request refs still hold the old objects. fix --all is a dry run by default and requires you to type the repository count to confirm, because it can force-push.

Keep watch

Reports, baseline and update

Reports · baseline · update
snare report                    scan, save a timestamped report
snare report --json             machine-readable; exits non-zero on findings
snare schedule install --daily  run it on a timer and notify on findings
snare schedule status | uninstall

snare baseline set              accept the current findings as known
snare baseline status | clear

snare update                    pull the latest version
snare update --check            is a newer version available?
snare update --relink           point ~/.local/bin/snare at this checkout
snare version                   version and commit

Reports write to $SNARE_HOME/reports and exit non-zero when something is found, so a timer or a CI job can act on the result. There is deliberately no email: SMTP credentials in a security tool are a liability, and a report file plus a native notification does the same job with nothing worth stealing.

Accepting a finding into a baseline silences it — it does not make it safe. Only baseline what you have actually read.

snare update matters more than it looks. Every clone made before 27 August 2026 contains defects that made the working-tree scan report clean on infected repositories. If you installed snare before then, update before you trust it, and run snare selftest afterwards. The changelog lists every one of them.

Limits

What snare does not do

A security tool that oversells itself is worse than no tool at all.

The guard polls once per second, so a payload can still act before it is caught. It cannot see code already running inside another process, and it will miss variants built on different infrastructure. Removing the malware from the repository is the actual fix — a running guard is never permission to open a repo you do not trust.

Scanning through the GitHub API sees branch tips only. A payload that was committed and later deleted survives in history; use snare scan repo on a clone to catch that.

Rewriting history does not touch forks, pull-request refs, or objects still reachable by SHA. Ask GitHub Support to garbage-collect after a purge.