
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.
| # | 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 |
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
| 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. |
| 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.