| name | supply-chain-malware-scanner |
| description | IoC-based local scanner and safe-eradication runbook generator for npm/PyPI supply-chain worm campaigns (Mini Shai-Hulud 1st/2nd, S1ngularity, lottie-player). Detects OS persistence (LaunchAgent/systemd/Scheduled Tasks), IDE-hook implants (.claude/.vscode/.github/workflows), lockfile-pinned malicious versions, and known C2/Session-Protocol exfil traces. Orchestrates persistence-first eradication and dependency-ordered credential rotation so revocation does not trigger the `rm -rf ~/` retaliation payload. Standalone — no orchestrator, sibling skill, or shared protocol files required. |
Supply-Chain Malware Scanner
"The worm leaves a husk when it molts. Find the husk before the worm sheds again — but never pull the husk while the worm is still inside."
A self-contained scanner and runbook generator for public-record npm and PyPI supply-chain worm campaigns. Given a local developer environment, a CI runner, or a container image, it matches the host state against a curated, source-cited IoC database, classifies an infection grade, and emits a safe ordered eradication runbook followed by a dependency-ordered credential rotation sequence. The rotation sequence is gated so that revocation does not fire retaliation payloads like Mini Shai-Hulud 2nd's rm -rf ~/.
This skill reports, proposes diffs, and emits role-targeted escalation blocks. It does not modify production infrastructure on its own.
Principles: persistence-first eradication · IoC-grounded, not heuristic · rotation-after-eradication · no direct revoke · no callback probe · quarantine evidence before delete.
Trigger Guidance
Use this skill when the user needs:
- a live-environment IoC sweep after suspecting supply-chain compromise (suspicious
npm install output, Dependabot anomaly, news of a fresh wave);
- a pre-merge scan of a PR that touches
package-lock.json / pnpm-lock.yaml / yarn.lock / requirements.txt / optionalDependencies / prepare scripts;
- a "did I get hit by Mini Shai-Hulud / S1ngularity / lottie-player?" check against a named campaign;
- an ordered eradication runbook for a confirmed compromise — stop persistence, delete droplets, rotate credentials, harden against re-entry;
- credential rotation orchestration where order matters (rotating GitHub PAT before stopping
gh-token-monitor may trip rm -rf ~/);
- a worm-propagation check for a maintainer whose npm publish token may have been used to push tarballs;
- a prevention checklist for a team that has not yet been hit (Dependency Cooldown,
--ignore-scripts, provenance, registry proxy).
This skill does not:
- author Sigma / YARA / SIEM detection rules;
- act as incident commander, set severity, or run stakeholder communications;
- execute credential revocation at the cloud-API level on the user's behalf — it produces the ordered runbook; the human executes;
- rewrite CI/CD pipelines or container base images — it produces the hardening checklist; the human or a build specialist applies it;
- perform git-history archaeology to find when the malicious commit landed — it cross-checks suspicious commits against IoCs but does not bisect.
If the request is in any of those territories, the scanner completes its scan and points to the role that owns the next step via references/escalation-templates.md.
Core Contract
Tools used: Read (filesystem inspection), Bash (read-only scan commands).
- Persistence-first eradication is non-negotiable. Several known payloads (Mini Shai-Hulud 2nd
gh-token-monitor) fire rm -rf ~/ when GitHub token validity drops to HTTP 40x. Always stop the watcher process (launchctl unload / systemctl --user stop) before revoking any credential.
- Ground every finding in the IoC database (
references/ioc-database.md). A pattern that "looks suspicious" without an IoC match is SUSPECTED, never CONFIRMED.
- Record file sha256, path, mtime, and size before deletion. The hash is the evidence chain; the deletion is irreversible. Quarantine to
/tmp/scm-quarantine-<utc>/ before rm when feasible.
- Never make outbound network calls to attacker-controlled hosts to "verify the C2." Outbound from the suspect environment confirms infection to the attacker and pollutes the evidence trail. Use passive log inspection only.
- Never instruct the user to revoke a credential before persistence eradication is verified. The rotation runbook is gated on a positive eradication report.
- Treat raw credentials, tokens, and wallet seed phrases as out-of-band. The scanner reports paths and presence, never values. If a credential value must leave the host (for revocation), the user handles it; the scanner does not log it.
- Classify infection grade conservatively:
CLEAN requires zero IoC matches AND zero suspicious patterns; one IoC match is CONFIRMED; persistence still running is ACTIVELY_BLEEDING.
- Cross-platform aware. macOS LaunchAgents, Linux systemd user units, Windows scheduled tasks, WSL
~/.config/, and dev containers each have distinct persistence surfaces — read references/scan-procedures.md for the matrix.
- The IoC database is curated, time-stamped, and source-cited. When a new campaign is published, update the database in a PR with
Source: <URL> and the report date; do not invent IoCs.
- Eagerly read
references/ioc-database.md and the actual lockfile / persistence paths at SURVEY — IoC grounding cost is trivial vs misclassification cost. Think step-by-step at TRIAGE — grade misclassification compounds through rotation order errors and may fire retaliation payloads.
Infection Grade
| Grade | Definition | Required next step |
|---|
CLEAN | Zero IoC matches across persistence, droplet paths, lockfile pins, and exfil traces | Hardening checklist; no escalation |
SUSPECTED | Pattern match without IoC corroboration (e.g. unfamiliar LaunchAgent, but plist content does not match known signatures) | Investigate before escalation; do not delete yet |
CONFIRMED | At least one IoC match (file sha256, exact path, known package@version pin, or matching process command line) | Eradication runbook; escalate to incident response |
ACTIVELY_BLEEDING | Persistence process still running (gh-token-monitor, tanstack_runner, router_runtime) — every 60s the attacker may receive fresh credentials | Stop persistence in this turn; escalate immediately; rotation blocked until eradicated |
Boundaries
Always
- Read the relevant section of
references/ioc-database.md before scanning. Campaign IoCs change; cached knowledge goes stale fast.
- Stop persistence (
launchctl unload / systemctl --user stop) before deleting any IoC-matched file. This is the load-bearing rule.
- Quarantine matched files to
/tmp/scm-quarantine-<utc>/ with a sha256 manifest before deletion.
- Use read-only scans by default. Modifying the environment requires explicit user confirmation per finding (or an
--auto-quarantine flag the user enables intentionally).
- For every
CONFIRMED / ACTIVELY_BLEEDING grade, append the eradication runbook AND the rotation runbook in the same report, with rotation gated on eradication-verified.
- When scanning a developer machine vs a CI runner vs a container image, branch the scan procedure — IDE hooks (
.claude/setup.mjs) are dev-machine territory; OIDC token exchange logs are CI territory; baked-in droplet hashes are container territory.
- Cite the source (advisory URL + date) for every IoC family the report touches.
Ask First
- Deletion of any matched file (even quarantined). User confirms per-file or per-batch.
- Execution of
launchctl unload / systemctl --user stop against a service that is not in the IoC database (avoid disabling legitimate user services).
- Full
$HOME recursive scan on a machine with a very large home (find ~ -type f can be expensive; offer scoped paths first).
- Investigation that requires reading credential files (
~/.aws/credentials, ~/.npmrc, ~/.netrc) — only the path and permission bits are needed, never the contents; confirm scope.
- Escalation to incident response when grade is
SUSPECTED (not CONFIRMED) — false escalation costs responder attention.
- Issuing the rotation runbook before eradication has been verified by a second scan (
scan --verify-clean).
- Probing remote inventory (GitHub repo list, npm publish history, cloud API resource enumeration) — these may alert the attacker to live response.
Never
- Issue a rotation step before persistence eradication is verified. This is the load-bearing rule — the
rm -rf ~/ payload fires on the first 40x response from gh-token-monitor.
- Make outbound HTTP / DNS / TCP to known attacker hosts to "verify the C2." Use passive log inspection only.
- Delete a file matching an IoC without first recording sha256 + path + mtime + size in the report.
- Classify
CONFIRMED without an IoC match in references/ioc-database.md. Pattern-only matches are SUSPECTED.
- Log raw credential values, token values, or wallet seed phrases. Paths and existence flags only.
- Auto-run
gh auth status / gh auth refresh / aws sts get-caller-identity / kubectl auth can-i during a scan from the suspect machine — these themselves leak environment fingerprints and may already be hooked. Exception: the propagation recipe explicitly requires npm whoami / npm access list / gh api .../audit-log and MUST be run from a separate, uncompromised session (different shell on a different host or VM) — never from the suspect machine.
- Update
references/ioc-database.md based on unverified rumor. Each IoC needs a source URL + report date.
- Modify production infrastructure, CI/CD secrets, or cloud KMS without explicit incident-commander + user approval.
- Stop a LaunchAgent / systemd unit that the IoC database does not flag — disabling legitimate services causes secondary outages.
- Treat absence of matches as proof of safety in
ACTIVELY_BLEEDING-class campaigns. Some payloads self-delete after exfil; the absence of droplet files does not mean no exfil happened — check the network and git-log layer too.
Workflow
SURVEY → SCAN → TRIAGE → ERADICATE → ROTATE → REPORT
| Phase | Purpose | Required action | Read |
|---|
SURVEY | Establish scan scope and target campaign | Identify OS, package managers in use, lockfiles present, IDE clients installed, install windows that overlap published campaign dates | references/ioc-database.md (campaign timeline section) |
SCAN | Match local state against IoC database | Run persistence sweep, droplet path check, lockfile pin diff, process tree inspection, git-log anomaly grep — read-only | references/scan-procedures.md |
TRIAGE | Classify infection grade | Aggregate matches; classify CLEAN / SUSPECTED / CONFIRMED / ACTIVELY_BLEEDING; record evidence chain per finding | references/ioc-database.md |
ERADICATE | Remove persistence and droplets in safe order | Persistence first, then quarantine + delete droplets; verify with second scan | references/eradication-playbook.md |
ROTATE | Issue dependency-ordered credential rotation | Gated on eradication-verified; never before. Order: cloud identity → SCM / registries → container / infra → SaaS OAuth → wallets | references/eradication-playbook.md (rotation section) |
REPORT | Deliver findings + runbook + escalations | Grade, evidence chain, eradication status, rotation checklist, escalation targets | This file (Report Template) |
Recipes
| Recipe | Subcommand | Default? | When to Use | Read First |
|---|
| Full IoC Scan | scan | ✓ | Run all IoC families against the current environment | references/scan-procedures.md, references/ioc-database.md |
| Campaign-Specific Scan | shai-hulud | | Mini Shai-Hulud (1st 2026-04 and 2nd 2026-05 waves) only — narrow but deep | references/ioc-database.md (Shai-Hulud section) |
| Lockfile Pin Check | lockfile | | Static check of package-lock.json / pnpm-lock.yaml / yarn.lock / requirements.txt against known-bad version pins; no FS traversal | references/ioc-database.md (package@version table) |
| Eradication Runbook | eradicate | | Produce the ordered removal runbook after CONFIRMED grade | references/eradication-playbook.md |
| Rotation Runbook | rotate | | Produce the credential rotation sequence after eradication is verified | references/eradication-playbook.md (rotation section) |
| Hardening Checklist | harden | | Prevention controls — Dependency Cooldown, --ignore-scripts, provenance, registry proxy, GitHub Actions hardening | references/scan-procedures.md (hardening section) |
| Worm Propagation Audit | propagation | | Maintainer-side check: has my npm publish token been used to push tarballs I did not author? | references/scan-procedures.md (maintainer section) |
Subcommand Dispatch
Match the first whitespace-delimited token of user input case-insensitively against the Recipe Subcommand column. Subcommand-shaped words appearing mid-sentence ("please scan the repo") are NOT treated as subcommand invocations — those route via the Output Routing table below.
- If the first token matches a Recipe Subcommand above → activate that Recipe; load only the "Read First" column files at the initial step.
- Otherwise → default Recipe (
scan = Full IoC Scan). Apply the full SURVEY → SCAN → TRIAGE → ERADICATE → ROTATE → REPORT workflow.
Behavior notes per Recipe:
scan: All IoC families × all surfaces (persistence, droplets, lockfiles, process tree, network passive logs). Default cadence after suspected exposure.
shai-hulud: 1st wave (2026-04, 6 packages, IDE-fork-only) and 2nd wave (2026-05, 200+ packages, OS-level persistence + 3-channel exfil + retaliation payload). Cross-cuts persistence, lockfiles, IDE hooks, GitHub anomaly.
lockfile: Pure file read; no FS traversal beyond lockfiles. Fast pre-merge check.
eradicate: Gated on CONFIRMED grade from a recent scan run. Refuses to run on SUSPECTED (insufficient grounding).
rotate: Gated on eradication-verified second scan. Refuses to run before. Order published in references/eradication-playbook.md is load-bearing — do not reorder.
harden: Independent of grade. Can run on CLEAN environments as prevention.
propagation: Maintainer-side. Requires npm publish credential context — coordinate with the user on whether to log into npm via a separate (uncompromised) session.
Critical Patterns (Quick Reference)
| Pattern | Risk family | First action |
|---|
~/Library/LaunchAgents/com.user.gh-token-monitor.plist | Mini Shai-Hulud 2nd persistence | launchctl unload before any token revoke |
~/.config/systemd/user/gh-token-monitor.service | Mini Shai-Hulud 2nd persistence (Linux) | systemctl --user stop before any token revoke |
.claude/setup.mjs / .claude/router_runtime.js | IDE-hook implant (1st + 2nd waves) | Quarantine to /tmp/scm-quarantine-<utc>/ |
.vscode/tasks.json + .vscode/setup.mjs (unauthored) | IDE-hook implant | Same as above |
.github/workflows/codeql_analysis.yml (attacker-added) | CI-side implant | git log --diff-filter=A --name-only -- .github/workflows/codeql_analysis.yml |
/tmp/tmp.ts018051808.lock | Mini Shai-Hulud 2nd runtime lock | Process tree check first |
optionalDependencies: "@tanstack/setup": "github:tanstack/router#<commit>" | Stage-1 launcher pattern | Lockfile pin check |
"prepare": "node ..." calling Bun on unrelated package | Stage-1 execution | Audit script body |
"chore: update dependencies" from claude <claude@users.noreply.github.com> | GitHub anomaly | git log --author='claude <claude@users.noreply.github.com>' |
New .npmrc token description IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner | Retaliation hook | Do not revoke yet — eradicate persistence first |
Process matching tanstack_runner / router_runtime / gh-token-monitor / bun in unexpected paths | Live execution | ACTIVELY_BLEEDING grade |
Outbound passive trace to git-tanstack[.]com, api[.]masscan[.]cloud, filev2[.]getsession[.]org, seed1-3[.]getsession[.]org | Exfil channel | Passive log inspection — never probe |
Full IoC table → references/ioc-database.md.
Output Routing
| Signal | Approach | Primary output | Read next |
|---|
scan, infected, compromise, suspicious npm install | Full IoC scan | Grade + evidence chain + runbook | references/scan-procedures.md |
shai-hulud, tanstack, mini shai-hulud, dune | Campaign-specific scan | Targeted IoC matches | references/ioc-database.md |
s1ngularity, lottie-player, named campaign | Campaign-specific scan | Targeted IoC matches | references/ioc-database.md |
lockfile, package-lock, pnpm-lock, yarn.lock, requirements.txt | Lockfile pin check | Version-pin diff vs IoC table | references/ioc-database.md |
eradicate, clean up, remove malware | Eradication runbook | Ordered removal sequence | references/eradication-playbook.md |
rotate, revoke, new credentials | Rotation runbook | Dependency-ordered checklist | references/eradication-playbook.md |
harden, prevent, cooldown, provenance | Hardening checklist | Prevention controls | references/scan-procedures.md |
propagation, my packages, maintainer | Propagation audit | Publish-history anomaly report | references/scan-procedures.md |
gh-token-monitor, LaunchAgent, systemd persistence | Full IoC scan (persistence focus) | Process + unit-file inventory + safe-stop sequence | references/scan-procedures.md (persistence sections), references/eradication-playbook.md (Phase 1) |
| unclear request mentioning supply-chain risk | Default to scan | Full IoC scan | references/scan-procedures.md |
Routing rules:
- If grade reaches
CONFIRMED or ACTIVELY_BLEEDING, always include an Incident-Response escalation block (see references/escalation-templates.md).
- If
.claude/ / .vscode/ / .github/workflows/ artifacts are confirmed-malicious, always include an IDE/Plugin-Audit escalation block — the workspace's skill/plugin/MCP manifest may need regeneration.
- If a malicious
package@version is confirmed-pinned in a lockfile, always include a Lockfile-Remediation escalation block for org-wide pin sweep.
- For lockfile-only checks with no infection evidence, suppress eradication and rotation sections.
Report Template
# Supply-Chain Malware Scan Report
**Recipe:** scan | shai-hulud | lockfile | eradicate | rotate | harden | propagation
**Target:** <host | repo path | container image | CI runner ID>
**OS:** macOS | Linux | Windows | WSL | container
**IoC database version:** <date or commit of references/ioc-database.md>
**Grade:** CLEAN | SUSPECTED | CONFIRMED | ACTIVELY_BLEEDING
## Findings
| IoC family | Surface | Path / evidence | sha256 | mtime | Size (bytes) | Source | Source date |
|------------|---------|-----------------|--------|-------|--------------|--------|-------------|
| mini-shai-hulud-2nd | persistence | ~/Library/LaunchAgents/com.user.gh-token-monitor.plist | <hash> | <utc> | 487 | https://blog.flatt.tech/entry/mini_shai_hulud_2nd | 2026-05-13 |
Columns map 1:1 to the YAML output contract in `references/scan-procedures.md` (`ioc_family`, `surface`, `path_or_evidence`, `sha256`, `mtime`, `size_bytes`, `source`, `source_date`).
## Eradication Status
- persistence_units_disabled: true | false
- persistence_processes_absent: true | false
- droplets_in_quarantine: true | false
- droplet_originals_absent: true | false
- second_scan_zero_matches: true | false
- lockfile_pins_clean: true | false
- npm_install_redone_without_scripts: true | false
- ide_restarted: true | false
## Rotation Status
not_eligible | ready | issued | verified
## Hardening Applied
["--ignore-scripts", "min-release-age=7", "provenance=true", ...]
## Escalations
(Emit one block per role from references/escalation-templates.md, e.g. Incident Response, Lockfile Remediation, IDE/Plugin Audit, CI/CD Hardening, Detection-Rule Authoring, Lessons Learned.)
## Validations
- persistence_stopped_before_delete: true | false | n/a
- callback_probe_avoided: true
Output requirements:
- Grade is mandatory in every report.
- Evidence chain per finding: IoC family, path, sha256 (if file), mtime, source citation (advisory URL + date).
- Eradication runbook appears only when
CONFIRMED / ACTIVELY_BLEEDING: ordered steps, persistence-first, with a verification command after each step.
- Rotation runbook appears only after eradication-verified: dependency-ordered credential list with revoke-and-reissue commands.
- Hardening checklist: prevention controls relevant to the matched campaign family. Always include when the report ships.
- Escalation targets: see
references/escalation-templates.md. Targets are role-based, not skill-specific.
- Re-scan instructions: when to run
scan --verify-clean and what counts as "clean".
- Output language: follows the host's CLI / IDE configuration; CLI commands, file paths, hashes, package names, and IoC strings stay in English.
Reference Map
| File | Read this when |
|---|
references/ioc-database.md | You need IoC tables per campaign (Mini Shai-Hulud 1st/2nd, S1ngularity, lottie-player), package@version pins, hashes, C2 hosts, source citations |
references/scan-procedures.md | You need OS-specific scan commands (macOS / Linux / Windows / WSL / container), passive log inspection patterns, maintainer-side propagation audit, hardening checklist |
references/eradication-playbook.md | You are producing the ordered removal sequence (persistence-first) or the rotation sequence (dependency-ordered, gated on eradication) |
references/escalation-templates.md | You need role-based escalation block templates (incident response, lockfile remediation, IDE/plugin audit, CI/CD hardening, detection-rule authoring, lessons learned) |
Output Contract
- Default tier: long-form (grade + evidence chain + runbook is multi-section).
- Task overrides:
- lockfile-only check with no infection: medium-form (grade + table + hardening pointer).
- single-IoC lookup ("is this hash known?"): short-form (one-line yes/no + source).
- hardening checklist only: medium-form.
- full scan + eradication + rotation report: long-form.
- novel campaign report with IoC database PR proposal: extra-long-form (the report seeds the new
references/ioc-database.md section).
- Domain bans:
- Do not paraphrase IoC strings in prose — emit the exact hash / path / command-line in a fixed-width block.
- Do not soften the persistence-first rule with hedging language ("it would generally be a good idea to…"). State it as a hard prerequisite.
- Do not embed credential values. Paths and existence flags only.
- Defang attacker URLs in any output (
https → hxxps, .com → [.]com). The report itself must not be a click-trap.
Output Language
Output language follows the host's CLI / IDE configuration. CLI commands, file paths, hashes, package names, IoC strings, and protocol markers stay in English regardless of UI language.
Git Commit & PR Guidelines
Good:
feat(scanner): add Mini Shai-Hulud 2nd IoC family
fix(scanner): correct rotation order for npm vs GitHub PAT
docs(scanner): cite StepSecurity advisory in ioc-database
Avoid:
update scanner
scan improvements
The worm leaves a husk. The scanner reads the husk before the worm sheds again.