
Shell-Skript zum Erkennen von Indikatoren für den TanStack npm Supply-Chain-Angriff (CVE-2026-45321 / GHSA-g7cv-rxg3-hmpx)
# tanstack-compromise-checker
Erkennen Sie Indikatoren des TanStack npm Supply-Chain-Angriffs auf einem Entwicklungsrechner, einem Repository oder einem CI-Runner. Bash-Skript, Docker-Image und GitHub Action – gleiche Engine, drei Einstiegspunkte.
**CVE-2026-45321 / [GHSA-g7cv-rxg3-hmpx](https://github.com/advisories/GHSA-g7cv-rxg3-hmpx)**
Am 11. Mai 2026 veröffentlichten Angreifer 84 schädliche Versionen über 42 `@tanstack/*` Pakete via GitHub Actions Cache-Vergiftung. Die Nutzlast stiehlt GitHub-Tokens, npm-Tokens, AWS-/GCP-/Azure-Anmeldeinformationen und installiert einen **Dead-Man's-Switch**-Daemon, der `~/` löscht, sobald der gestohlene Token widerrufen wird.
## Schnellstart
Wählen Sie den Einstiegspunkt, der zu Ihrer Situation passt.
### macOS / Linux / WSL2 / Git Bash
```sh
TAG=v1.0.0
curl -fsSLO https://github.com/fabriziosalmi/tanstack-compromise-checker/releases/download/$TAG/check.sh
curl -fsSLO https://github.com/fabriziosalmi/tanstack-compromise-checker/releases/download/$TAG/check.sh.sha256
sha256sum -c check.sh.sha256
bash check.sh --online
```
### Docker – funktioniert auf jedem Betriebssystem, das Docker ausführt (inkl. Windows)
```sh
docker run --rm -v "$PWD":/scan \
ghcr.io/fabriziosalmi/tanstack-compromise-checker:v1.0.0 \
/scan true fail tanstack-findings.json '' GHSA-g7cv-rxg3-hmpx
```
Das Image ist Multi-Arch (`linux/amd64`, `linux/arm64`) und wird mit einer via GitHub OIDC signierten Build-Provenienz ausgeliefert. Überprüfen Sie vor der Ausführung:
```sh
gh attestation verify \
oci://ghcr.io/fabriziosalmi/tanstack-compromise-checker:v1.0.0 \
--repo fabriziosalmi/tanstack-compromise-checker
```
### GitHub Action – blockieren Sie kompromittierte Abhängigkeiten in CI
```yaml
permissions: {}
jobs:
check:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: fabriziosalmi/tanstack-compromise-checker@v1
with:
scan-dir: .
online: 'true'
fail-on: fail
```
Ein vollständig gehärteter Workflow befindet sich unter [`.github/workflows/tanstack-check.yml`](https://github.com/fabriziosalmi/tanstack-compromise-checker/blob/main/.github/workflows/tanstack-check.yml) und ist derselbe Workflow, den dieses Repository gegen sich selbst ausführt.
## Was das Skript prüft
| # | Check | Description |
|---|-------|-------------|
| 1 | Dead-man's switch | `~/.local/bin/gh-token-monitor.sh`, LaunchAgents + LaunchDaemons (macOS), systemd Benutzer- + System-Units (Linux), laufende Prozesse |
| 2 | Persistence | Shell-RC-Dateien, Benutzer-/System-Crontab, `/etc/cron.*`, XDG-Autostart, globales Git `core.hooksPath` |
| 3 | Credentials | sensible Umgebungsvariablen, `~/.npmrc`, `~/.yarnrc.yml`, `~/.aws/credentials`, `~/.config/gh/hosts.yml`, `~/.netrc`, `~/.docker/config.json`, `~/.kube/config`, Auflistung von `.env*`-Dateien |
| 4 | Network IOC | laufende `node`-Prozesse mit etablierten ausgehenden TCP-Verbindungen (heuristisch, manuelle Überprüfung) |
| 5 | Repo scan | in `package.json` deklarierte Abhängigkeiten + in Lockdateien festgelegte Versionen (`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`) |
| 6 | Installed | `node_modules/@tanstack/*/package.json` wird mit der Liste bekannter schädlicher Versionen abgeglichen, dann mit der npm-Registry `time[<version>]` bei `--online` |
| 7 | Actions hints | `.github/workflows/*` wird gescannt auf `pull_request_target`, fehlendes `--ignore-scripts`, Nutzung von `actions/cache` |
| 8 | Mini Shai-Hulud IOCs | Payload-Dateien in `node_modules` (`router_init.js`, `tanstack_runner.js`), `@tanstack/setup` als optionalDependency, Manipulation von KI-Tool-Konfigurationen, C2-Domains im Quellcode, Angreifer-Commit-Autor + Branch-Muster, mit Lösegeld markierte npm-Tokens, sekundäre wurmverbreitete Pakete, plus Info-Level-Heuristiken für unbekannte Payload-Formen |
Mit `--online` ruft das Skript zur Laufzeit auch die GHSA-Advisory ab, sodass die Erkennungsabdeckung dem Live-Zustand der Advisory folgt, anstatt einer fest codierten Liste von Versionen.
**Betroffene Familie**: `@tanstack/router`, `@tanstack/react-router`, `@tanstack/history`, `@tanstack/start`, `@tanstack/eslint-plugin-router` und verwandte Pakete.
**Saubere Familien** (nicht betroffen): `@tanstack/react-query`, `@tanstack/react-table`, `@tanstack/react-virtual`, `@tanstack/form`, `@tanstack/store`.
## Verwendung
```sh
# Quick check (current user's home)
bash check.sh
# Online mode: query the GHSA advisory and npm registry publish-time
bash check.sh --online
# Scan a specific directory
bash check.sh --scan-dir /path/to/your/repos
# JSON output for CI/SIEM integration
bash check.sh --json > findings.json
# Suggest pin commands for affected repos (dry-run)
bash check.sh --scan-dir /path/to/repos --fix
# Provide an extended known-bad list
bash check.sh --bad-versions-file ./extra-bad-versions.txt
```
### Alle Optionen
| Flag | Description |
|------|-------------|
| `--scan-dir DIR` | Root-Verzeichnis zum Scannen (Standard: `$HOME`). Weigert sich, Systemverzeichnisse wie `/`, `/etc`, `/usr` zu scannen. |
| `--online` | Ruft die GHSA-Advisory und das `time[<version>]` der npm-Registry ab, um die Kompromittierung zu bestätigen. Standardmäßig deaktiviert – Opt-in. |
| `--ghsa GHSA-ID` | Überschreibt die Advisory-ID, die bei `--online` verwendet wird (Standard `GHSA-g7cv-rxg3-hmpx`). |
| `--attack-window-start ISO`, `--attack-window-end ISO` | Überschreibt das Veröffentlichungszeitfenster, das bei `--online` verwendet wird. |
| `--fix` | Gibt empfohlene Pin-Befehle für betroffene Repos aus (standardmäßig Trockenlauf). |
| `--apply` | Mit `--fix`: Opt-in zur Ausführung. Verweigert derzeit noch blindes `update`; manuelles Pinning ist erforderlich. |
| `--yes`, `-y` | Überspringt die Bestätigungsaufforderung für `--apply`. |
| `--json` | Gibt Ergebnisse als JSON auf stdout aus (impliziert `--quiet`). |
| `--bad-versions-file F` | Fügt Einträge (`pkg@version`, eine pro Zeile) zur Liste bekannter schädlicher Versionen hinzu. |
| `--no-color` | Deaktiviert ANSI-Farben. |
| `--quiet`, `-q` | Unterdrückt die Ausgabe pro Prüfung, gibt nur Zusammenfassung aus. |
| `-h`, `--help` | Zeigt Hilfe an. |
### Exit-Codes
| Code | Meaning |
|------|---------|
| 0 | Sauber – keine Indikatoren im gescannten Bereich |
| 1 | Nur Warnungen – Überprüfung empfohlen |
| 2 | Fehler – Kompromittierungsindikatoren vorhanden |
| 3 | Tool-/Anwendungsfehler |
## Sicherheitshaltung
Dieses Repository schützt sich gegen dieselbe Angriffsklasse, die es erkennt. Konkret:
- Jede GitHub Action `uses:` ist auf einen 40-Zeichen-Commit-SHA festgelegt, nicht auf ein schwebendes Tag.
- Das Docker-Basis-Image ist auf einen unveränderlichen Multi-Arch-Manifest-Digest festgelegt.
- Jeder Workflow deklariert `permissions: {}` auf oberster Ebene; Jobs gewähren nur, was sie benötigen.
- `actions/checkout` wird mit `persist-credentials: false` aufgerufen, sodass kein authentifiziertes Git-Remote in `$GITHUB_WORKSPACE` verbleibt.
- [`step-security/harden-runner`](https://github.com/step-security/harden-runner) ist der erste Schritt jedes Workflows und überwacht den Runner-Ausgang.
- [OpenSSF Scorecard](https://github.com/ossf/scorecard) läuft wöchentlich und bei jedem Push auf `main`; Ergebnisse werden als SARIF hochgeladen.
- Jede Veröffentlichung veröffentlicht eine [SLSA-Build-Provenienz-Bescheinigung](https://slsa.dev/spec/v1.0/provenance) für das Docker-Image. Jeder kann es offline mit `gh attestation` verifizieren.
- Jede Veröffentlichung enthält eine `check.sh.sha256`, die alle installierbaren Artefakte abdeckt.
Die vollständige Richtlinie befindet sich in [`SECURITY.md`](https://github.com/fabriziosalmi/tanstack-compromise-checker/blob/main/SECURITY.md).
**Verwenden Sie nicht** `curl | bash` für irgendein Sicherheitstool, einschließlich dieses. Laden Sie immer herunter, überprüfen Sie die Prüfsumme, inspizieren Sie und führen Sie dann aus.
## Beispielausgabe
```
━━ 1 / 8 DEAD-MAN'S SWITCH ARTEFACTS
✔ No dead-man's switch artefacts found
━━ 2 / 8 PERSISTENCE VECTORS
✔ No persistence vectors detected
━━ 3 / 8 TOKEN / CREDENTIAL EXPOSURE
✔ No credential exposure detected
━━ 4 / 8 NETWORK INDICATORS
✔ No node TCP connections currently established (or none observable)
━━ 5 / 8 REPO SCAN — package.json + lockfiles
ℹ Found 12 package.json + 8 lockfile(s)
✔ No compromised family declared in any package.json
✔ No compromised pins found in any lockfile
━━ 6 / 8 INSTALLED node_modules — direct version check
✔ No compromised packages in installed node_modules
━━ 7 / 8 GITHUB ACTIONS HARDENING HINTS
✔ No risky GitHub Actions patterns detected (in scanned scope)
━━ 8 / 8 MINI SHAI-HULUD PAYLOAD + AUXILIARY IOCs
✔ No Mini Shai-Hulud payload / auxiliary IOCs detected
━━ SUMMARY
Passed: 8 Warnings: 0 Failed: 0
Scope : /Users/alice/dev
✔ No indicators of compromise within scope: /Users/alice/dev
Note: a clean result for /Users/alice/dev does not certify the rest of the system.
```
## Wenn Sie kompromittiert sind
> **Widerrufen Sie Ihren GitHub-Token NICHT, bevor Sie den Daemon stoppen** – der Dead-Man's-Switch wird `rm -rf ~/` ausführen, sobald der Token widerrufen wird.
1. Stoppen Sie zuerst den Daemon:
- macOS 14+: `launchctl bootout gui/$UID ~/Library/LaunchAgents/com.user.gh-token-monitor.plist`
- macOS legacy: `launchctl unload ~/Library/LaunchAgents/com.user.gh-token-monitor.plist`
- Linux: `systemctl --user stop gh-token-monitor && systemctl --user disable gh-token-monitor`
2. Entfernen Sie das Skript: `rm -f ~/.local/bin/gh-token-monitor.sh`
3. **Dann** widerrufen und rotieren: GitHub-Token, npm-Token, AWS-/GCP-/Azure-Anmeldeinformationen, SSH-Schlüssel, SSH-Agent.
4. Überprüfen Sie das GitHub-Überwachungsprotokoll: <https://github.com/settings/security-log>
5. Überprüfen Sie `~/.aws/credentials`, `~/.kube/config`, `~/.config/gh/hosts.yml`, `~/.npmrc`.
## Abhängigkeiten
- `bash` 4+
- `jq` (empfohlen) – `package-lock.json` Parsen
- `python3` (optionaler Fallback) – wird verwendet, wenn `jq` fehlt
- `curl` – erforderlich für `--online`
- `lsof` oder `ss` (optional) – Netzwerk-IOC-Heuristik
Das Docker-Image bündelt alle oben genannten. Verwenden Sie es auf Systemen, auf denen Sie diese Tools nicht installieren können oder möchten.
## Mitwirken
Issues und Pull-Requests sind willkommen. Beachten Sie, dass dieses Repository CODEOWNERS verwendet: Alle PRs benötigen eine Überprüfung durch einen Betreuer.
## Referenzen
- [Offizieller Postmortem von TanStack](https://tanstack.com/blog/npm-supply-chain-compromise-postmortem)
- [GitHub Advisory GHSA-g7cv-rxg3-hmpx](https://github.com/advisories/GHSA-g7cv-rxg3-hmpx)
- [Snyk-Analyse](https://snyk.io/blog/tanstack-npm-packages-compromised/)
- [Wiz Blog – Mini Shai-Hulud](https://www.wiz.io/blog/mini-shai-hulud-strikes-again-tanstack-more-npm-packages-compromised)
- [StepSecurity-Bericht](https://www.stepsecurity.io/blog/mini-shai-hulud-is-back-a-self-spreading-supply-chain-attack-hits-the-npm-ecosystem)
- [Orca Security – 160+ Pakete](https://orca.security/resources/blog/tanstack-npm-supply-chain-worm/)
## Lizenz
MIT.