
Script shell per rilevare gli indicatori di attacco alla supply chain di TanStack npm (CVE-2026-45321 / GHSA-g7cv-rxg3-hmpx)
Rileva indicatori dell'attacco alla supply-chain npm di TanStack su una macchina sviluppatore, un repository o un runner CI. Script Bash, immagine Docker e GitHub Action — stesso motore, tre punti di ingresso.
CVE-2026-45321 / GHSA-g7cv-rxg3-hmpx
L'11 maggio 2026, gli aggressori hanno pubblicato 84 versioni malevole su 42 pacchetti @tanstack/* tramite avvelenamento della cache di GitHub Actions. Il payload ruba token GitHub, token npm, credenziali AWS/GCP/Azure e installa un demone dead-man's switch che cancella ~/ nel momento in cui il token rubato viene revocato.
Scegli il punto di ingresso che corrisponde alla tua situazione.
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 run --rm -v "$PWD":/scan \
ghcr.io/fabriziosalmi/tanstack-compromise-checker:v1.0.0 \
/scan true fail tanstack-findings.json '' GHSA-g7cv-rxg3-hmpx
L'immagine è multi-architettura (linux/amd64, linux/arm64) e include una provenienza di build firmata tramite GitHub OIDC. Verifica prima di eseguire:
gh attestation verify \
oci://ghcr.io/fabriziosalmi/tanstack-compromise-checker:v1.0.0 \
--repo fabriziosalmi/tanstack-compromise-checker
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
Un flusso di lavoro completo e rinforzato si trova in .github/workflows/tanstack-check.yml ed è lo stesso flusso di lavoro che questo repository esegue su se stesso.
Con --online, lo script recupera anche l'avviso GHSA in fase di esecuzione, in modo che la copertura di rilevamento segua lo stato live dell'avviso anziché un elenco di versioni hard-coded.
Famiglia interessata: @tanstack/router, @tanstack/react-router, @tanstack/history, @tanstack/start, @tanstack/eslint-plugin-router e pacchetti correlati.
Famiglie pulite (non interessate): @tanstack/react-query, @tanstack/react-table, @tanstack/react-virtual, @tanstack/form, @tanstack/store.
# 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
| Codice | Significato |
|---|---|
| 0 | Pulito — nessun indicatore nell'ambito scansionato |
| 1 | Solo avvisi — revisione consigliata |
| 2 | Fallimenti — indicatori di compromesso presenti |
| 3 | Errore dello strumento/di utilizzo |
Questo repository si difende dalla stessa classe di attacchi che rileva. Nello specifico:
uses: di GitHub Action è bloccato su un commit SHA di 40 caratteri, non su un tag fluttuante.permissions: {} al livello superiore; i job concedono solo ciò di cui hanno bisogno.actions/checkout viene invocato con persist-credentials: false in modo che nessun remote git autenticato rimanga in $GITHUB_WORKSPACE.step-security/harden-runner è il primo passo di ogni flusso di lavoro e monitora il traffico in uscita dal runner.main; i risultati vengono caricati come SARIF.gh attestation.check.sh.sha256 che copre tutti gli artefatti installabili.La policy completa è in SECURITY.md.
Non eseguire curl | bash per nessuno strumento di sicurezza, incluso questo. Scarica sempre, verifica il checksum, ispeziona, poi esegui.
━━ 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.
NON revocare il token GitHub prima di fermare il demone — il dead-man's switch eseguirà
rm -rf ~/nel momento in cui il token viene revocato.
launchctl bootout gui/$UID ~/Library/LaunchAgents/com.user.gh-token-monitor.plistlaunchctl unload ~/Library/LaunchAgents/com.user.gh-token-monitor.plistsystemctl --user stop gh-token-monitor && systemctl --user disable gh-token-monitorrm -f ~/.local/bin/gh-token-monitor.sh~/.aws/credentials, ~/.kube/config, ~/.config/gh/hosts.yml, ~/.npmrc.Lo script bash necessita di:
bash 4+jq (consigliato) — analisi di package-lock.jsonpython3 (fallback opzionale) — usato quando jq è assentecurl — richiesto per --onlinelsof o ss (opzionale) — euristica IOC di reteL'immagine Docker include tutto quanto sopra. Usala su sistemi dove non puoi o non vuoi installare questi strumenti.
Issue e pull request sono benvenuti. Nota che questo repository utilizza CODEOWNERS: tutte le PR richiedono una revisione da parte di un manutentore.
MIT.
| # | Check | Descrizione |
|---|
| 1 | Dead-man's switch | ~/.local/bin/gh-token-monitor.sh, LaunchAgents + LaunchDaemons (macOS), unità systemd utente + sistema (Linux), processi in esecuzione |
| 2 | Persistenza | file rc della shell, crontab utente/sistema, /etc/cron.*, avvio automatico XDG, git globale core.hooksPath |
| 3 | Credenziali | variabili d'ambiente sensibili, ~/.npmrc, ~/.yarnrc.yml, ~/.aws/credentials, ~/.config/gh/hosts.yml, ~/.netrc, ~/.docker/config.json, ~/.kube/config, enumerazione di file .env* |
| 4 | IOC di rete | processi node in esecuzione con connessioni TCP in uscita stabilite (euristica, revisione manuale) |
| 5 | Scansione repository | dipendenze dichiarate in package.json + versioni bloccate nei file di blocco (package-lock.json, yarn.lock, pnpm-lock.yaml) |
| 6 | Installato | node_modules/@tanstack/*/package.json controllato rispetto all'elenco di versioni note come malevole, poi rispetto al registry npm time[<versione>] quando --online |
| 7 | Suggerimenti Actions | .github/workflows/* scansionato per pull_request_target, --ignore-scripts mancante, utilizzo di actions/cache |
| 8 | IOC Mini Shai-Hulud | file payload in node_modules (router_init.js, tanstack_runner.js), @tanstack/setup come optionalDependency, manomissione configurazione strumenti AI, domini C2 nel codice sorgente, pattern di commit author e branch degli aggressori, token npm marcati con riscatto, pacchetti secondari propagati da worm, più euristiche di livello informativo per forme di payload sconosciute |
| Flag | Descrizione |
|---|
--scan-dir DIR | Directory principale da scansionare (default: $HOME). Rifiuta di scansionare directory di sistema come /, /etc, /usr. |
--online | Interroga l'avviso GHSA e time[<versione>] del registry npm per confermare il compromesso. Disattivato per impostazione predefinita — adesione esplicita. |
--ghsa GHSA-ID | Sostituisci l'ID dell'avviso utilizzato in --online (default GHSA-g7cv-rxg3-hmpx). |
--attack-window-start ISO, --attack-window-end ISO | Sostituisci la finestra di tempo di pubblicazione utilizzata in --online. |
--fix | Stampa i comandi di pin raccomandati per i repository interessati (dry-run per impostazione predefinita). |
--apply | Con --fix: adesione esplicita all'esecuzione. Attualmente rifiuta ancora update cieco; è richiesto il pin manuale. |
--yes, -y | Salta il prompt di conferma per --apply. |
--json | Emette i risultati come JSON su stdout (implica --quiet). |
--bad-versions-file F | Aggiunge voci (pkg@versione, una per riga) all'elenco di versioni note come malevole. |
--no-color | Disabilita i colori ANSI. |
--quiet, -q | Sopprime l'output per controllo, stampa solo il riepilogo. |
-h, --help | Mostra l'aiuto. |