{"id":50358655,"url":"https://github.com/pacphi/shai-hulud-auditor","last_synced_at":"2026-05-30T00:01:30.113Z","repository":{"id":358726628,"uuid":"1242778014","full_name":"pacphi/shai-hulud-auditor","owner":"pacphi","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-18T19:57:00.000Z","size":55,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-18T21:55:47.590Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pacphi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":"audit.js","citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-18T18:43:03.000Z","updated_at":"2026-05-18T19:57:04.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/pacphi/shai-hulud-auditor","commit_stats":null,"previous_names":["pacphi/shai-hulud-auditor"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/pacphi/shai-hulud-auditor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pacphi%2Fshai-hulud-auditor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pacphi%2Fshai-hulud-auditor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pacphi%2Fshai-hulud-auditor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pacphi%2Fshai-hulud-auditor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pacphi","download_url":"https://codeload.github.com/pacphi/shai-hulud-auditor/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pacphi%2Fshai-hulud-auditor/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33675019,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-29T02:00:06.066Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-30T00:01:29.273Z","updated_at":"2026-05-30T00:01:30.107Z","avatar_url":"https://github.com/pacphi.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🐛 shai-hulud-auditor\n\n\u003e 🛡️ A zero-dependency Node script that hunts for the **Mini Shai-Hulud / Shai-Hulud 2.0** npm supply-chain compromise (Apr–May 2026) — across one repo, a whole folder of repos, or any git URL.\n\u003e\n\u003e **Tracking:** [CVE-2026-45321](https://borecraft.com/news/tanstack-mini-shai-hulud-cve-2026-45321-guide.html) · Snyk **GHSA-g7cv-rxg3-hmpx** · Mini Shai-Hulud waves of **29 April – 13 May 2026**.\n\n---\n\n## ⚠️ Read this first\n\n- **🪪 SLSA provenance signatures pass on these packages.** The attacker used a stolen OIDC token to publish through the legitimate TanStack pipeline, so `npm audit signatures` and provenance attestation checks **report green on compromised versions**. You must check the version list, not the signature. ([Wiz][wiz-readme], [VentureBeat][vb-readme])\n- **🪤 The watchdog wipes `$HOME` on token revocation.** Image / snapshot the host before touching credentials. Order is: **disarm persistence → re-audit → rotate** — see [MACOS.md](MACOS.md) / [LINUX.md](LINUX.md).\n- **📅 If you ran `npm install` / `npm ci` between 29 April and 13 May 2026**, re-audit. That's the Mini Shai-Hulud publish window — anything resolved inside it should be re-checked, including CI build agents. ([Snyk][snyk-readme], [Carthage][carth-readme])\n- **🤖 Driving an AI coding assistant?** [PROMPTS.md](PROMPTS.md) has 12 copy-pasteable prompts that walk an assistant through every check below — per-repo audit, host triage, lockfile-history forensics, workflow secret / OIDC inspection, CI hardening PRs — with the watchdog warning baked in so it can't accidentally trigger the wiper.\n\n[wiz-readme]: https://www.wiz.io/blog/mini-shai-hulud-strikes-again-tanstack-more-npm-packages-compromised\n[vb-readme]: https://venturebeat.com/security/shai-hulud-worm-172-npm-pypi-packages-valid-provenance-ci-cd-audit\n[snyk-readme]: https://snyk.io/blog/tanstack-npm-packages-compromised/\n[carth-readme]: https://carthageelectronics.com/npm-supply-chain-crisis-mini-shai-hulud-may-2026/\n\n---\n\n## 📚 Related docs\n\n- [MACOS.md](MACOS.md) — host-level remediation on macOS (LaunchAgent, APFS snapshots, reinstall)\n- [LINUX.md](LINUX.md) — host-level remediation on Linux (systemd, btrfs/LVM/ZFS, reimage)\n- [HARDENING.md](HARDENING.md) — preventive controls for CI build agents and dev workstations\n- [TEMPLATES.md](TEMPLATES.md) — comms templates for paused releases, IR updates, stakeholder briefings\n- [PROMPTS.md](PROMPTS.md) — copy-pasteable prompts for driving an AI assistant through the same triage\n- [scripts/triage-macos.sh](scripts/triage-macos.sh) · [scripts/triage-linux.sh](scripts/triage-linux.sh) — read-only persistence inventories\n- [scripts/remediate-repo.sh](scripts/remediate-repo.sh) — repo-level cleanup helper (refuses to run while host persistence is present)\n\n---\n\n## 🤔 Why you want this\n\nThe Shai-Hulud worm slipped malicious versions into **170+ packages** across TanStack, Mistral, UiPath, OpenSearch, and friends. The payload exfiltrates tokens **and** ships a watchdog that wipes `$HOME` if it detects revocation. Not great. 😬\n\n`shai-hulud-audit` tells you, fast:\n\n- ✅ Is anything in my lockfiles a known-bad `package@version`?\n- ⚠️ Does any `package.json` even *name* an at-risk package?\n- 🕳️ Is the `@tanstack/setup` orphan-commit IOC sitting in my `node_modules`?\n- 🪤 Are the persistence hooks (`~/.claude/setup.mjs`, `gh-token-monitor.service`, …) on this machine?\n\nNo installs. No dependencies. One file. Run it everywhere.\n\n---\n\n## ✨ Features\n\n- 📦 **npm + pnpm** lockfile support (`package-lock.json` v1/v2/v3, `pnpm-lock.yaml` v5/v6/v9)\n- 🌲 **Recursive** — point it at a folder of repos and get a per-project verdict\n- 🌐 **Clone-and-scan** — give it a GitHub / GitLab / Bitbucket / SSH / generic git URL and it shallow-clones to a temp dir, audits, then cleans up\n- 🧪 Detects the `@tanstack/setup` optionalDependency + `router_init.js` IOCs\n- 🖥️ Checks machine-level persistence paths\n- 🎯 Clean exit codes for CI: `0` clean, `1` findings, `2` error\n- 🪶 Zero runtime deps — just Node ≥ 16\n\n---\n\n## 🚀 Quick start\n\n```bash\n# Audit the current directory\nnode audit.js\n\n# Audit one specific project\nnode audit.js ~/code/my-app\n\n# Audit a whole folder of repos\nnode audit.js ~/code\n\n# Audit a remote repo without cloning it yourself\nnode audit.js https://github.com/your-org/your-repo.git\n```\n\nMake it executable if you like:\n\n```bash\nchmod +x audit.js\n./audit.js .\n```\n\n---\n\n## 🎛️ Modes\n\n### 1. 📁 Single project\n\n```bash\nnode audit.js ./my-app\n```\n\nLooks for `package-lock.json`, `pnpm-lock.yaml`, and `package.json` in the given directory.\n\n### 2. 🌲 Recursive (a \"repo of repos\")\n\n```bash\nnode audit.js ~/workspace\n```\n\nWalks the tree, skipping `node_modules`, `.git`, `dist`, `build`, `.next`, `.turbo`, `.cache`, hidden dirs, and symlinks. Each project gets its own line:\n\n```\n[clean]       repoB                  (npm, 842 pkgs)\n[COMPROMISED] frontend/web           (pnpm, 1503 pkgs)\n    lockfile: @tanstack/react-router@1.169.5\n[COMPROMISED] services/api           (package.json-only, 0 pkgs)\n    package-json: at-risk name \"@mistralai/mistralai\" declared in dependencies (\"^2.2.3\") — compromised versions: 2.2.3, 2.2.4\n```\n\n### 3. 🌐 Git URL (clone + scan + cleanup)\n\n```bash\nnode audit.js https://github.com/your-org/your-repo.git\nnode audit.js git@github.com:your-org/your-repo.git\nnode audit.js https://gitlab.com/group/project.git\nnode audit.js https://bitbucket.org/team/repo.git\n```\n\nPerforms `git clone --depth 1` into `os.tmpdir()/shai-hulud-audit-XXXX/`, runs the full audit, and removes the temp dir on exit (including `SIGINT`/`SIGTERM`). 🧹\n\n---\n\n## 📊 What you get\n\n```\nMini Shai-Hulud Audit\nDate:    2026-05-18T18:36:48.926Z\nIOC set: 170 package names, 344 versions\n\nDiscovering projects\n  Found 12 project directories.\n\nPer-project audit\n  [clean]       .\n  [COMPROMISED] packages/router      (pnpm, 1487 pkgs)\n      lockfile: @tanstack/router-core@1.169.5\n  ...\n\nMachine-level persistence hooks\n  No known persistence hooks detected.\n\nSummary\n  Projects scanned:     12  (npm: 4, pnpm: 7, other: 1)\n  Resolved packages:    18342\n  Compromised projects: 1\n  Total findings:       1\n  Persistence hits:     0\n```\n\n---\n\n## 🤖 In CI\n\n```yaml\n- name: Shai-Hulud audit\n  run: node audit.js .\n```\n\nPipeline fails (`exit 1`) the instant anything matches. 🟥\n\n---\n\n## 🤖 With an AI assistant\n\nIf you're already working with Claude Code / Cursor / Aider / Copilot Workspace, [PROMPTS.md](PROMPTS.md) ships 12 self-contained prompts you can paste verbatim:\n\n| # | Prompt | When to use |\n|---|---|---|\n| 1 | Audit a single repo | Starting cold on any repo |\n| 2 | Triage the host for persistence | Starting cold on any machine |\n| 3 | Pin past IOCs + refresh lockfile | Auditor flagged a `package-json` finding |\n| 4 | Audit workflows for OIDC / secrets | Need to know if CI could have leaked tokens |\n| 5 | Check GitHub run history in window | Reconstructing what ran between 29 Apr–13 May |\n| 6 | Lockfile-history forensics | Prove the worm never resolved in this repo |\n| 7 | Repo secret / variable inventory | Decide what to rotate |\n| 8 | Pre-commit safety check | About to push remediation commits |\n| 9 | Build a CI hardening PR | Applying HARDENING.md §1 in one PR |\n| 10 | Stakeholder comms drafting | Slack post / leadership note / IR status |\n| 11 | Post-incident retrospective | After the all-clear |\n| 12 | Re-audit on a schedule | LaunchAgent or systemd timer for weekly checks |\n\nEvery prompt that touches credentials or persistence repeats the **watchdog warning** verbatim and asks the assistant to **show a diff before any destructive action** — so even a misconfigured agent can't accidentally `rm -rf` or revoke tokens.\n\n---\n\n## 🚨 If it finds something\n\n🛑 **Do NOT immediately revoke npm tokens or delete files.**\n\nThe malware ships a token-monitor watchdog that triggers a `$HOME` wipe when it detects revocation. Order of operations matters: **disarm persistence → re-audit → rotate credentials → reinstall.**\n\nStep-by-step playbooks:\n\n- 🍎 **macOS:** [MACOS.md](MACOS.md) + `bash scripts/triage-macos.sh`\n- 🐧 **Linux:** [LINUX.md](LINUX.md) + `bash scripts/triage-linux.sh`\n- 🔧 **Repo-level cleanup (cross-platform):** `bash scripts/remediate-repo.sh /path/to/repo`\n- 🛡️ **Preventing the next wave:** [HARDENING.md](HARDENING.md)\n- 📣 **Briefing stakeholders / pausing a release:** [TEMPLATES.md](TEMPLATES.md)\n- 🤖 **Have an AI assistant drive it for you:** [PROMPTS.md](PROMPTS.md) — start with **#1 Audit a single repo** and **#2 Triage the host for persistence**, then follow the sequence the prompts spell out.\n\nQuick summary:\n\n1. 🖼️ Image / snapshot the affected machine first.\n2. 🧯 Disconnect it from the network.\n3. 🪤 Quarantine the persistence artefacts BEFORE touching any tokens.\n4. 🧼 Rotate credentials from a **clean** environment afterward.\n\n---\n\n## 🧠 How it works (60-second tour)\n\nThe script is one file, organised top-to-bottom in six sections:\n\n1. **IOC data** — the compromised `package@version` list + persistence paths\n2. **Audit primitives** — pure `isCompromised(name, version)` checks\n3. **Lockfile parsers** — npm JSON + pnpm YAML (regex, no deps) → flat `{name, version}` lists\n4. **Project auditor** — runs every check against one directory, returns a structured result\n5. **Discovery** — recursive walker + git-URL clone helper\n6. **Reporting / main** — CLI, per-project lines, summary\n\nWant to extend the IOC list? Edit the `COMPROMISED` map at the top. That's it. ✂️\n\n---\n\n## 🙏 Credits\n\n- **[Peter Hollis](https://github.com/peter-hollis-orkastrate)** — originated the version of the audit script this repo is built on, including the IOC inventory and persistence-path checks. The Orkastrate pre-release audit checklist that informed [HARDENING.md](HARDENING.md) and [TEMPLATES.md](TEMPLATES.md) also came from his work.\n- The security researchers and vendors whose IOC writeups are cited throughout [MACOS.md](MACOS.md), [LINUX.md](LINUX.md), and [HARDENING.md](HARDENING.md) — StepSecurity, Snyk, Wiz, Expel, Arctic Wolf, Phoenix Security, Picus, Elastic, VentureBeat, Carthage Electronics, Intrudify, Datadog, Orca, Guardz, Socprime, Corgea, Microsoft Security, Unit 42, Socket.\n\n---\n\n## 📜 License\n\nUse it, fork it, ship it. Stay safe out there. 🌵\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpacphi%2Fshai-hulud-auditor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpacphi%2Fshai-hulud-auditor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpacphi%2Fshai-hulud-auditor/lists"}