
Script de shell para detectar indicadores de ataque a la cadena de suministro de TanStack npm (CVE-2026-45321 / GHSA-g7cv-rxg3-hmpx)
Detecta indicadores del ataque a la cadena de suministro npm de TanStack en una máquina de desarrollo, un repositorio o un runner de CI. Script Bash, imagen Docker y acción de GitHub: el mismo motor, tres puntos de entrada.
CVE-2026-45321 / GHSA-g7cv-rxg3-hmpx
El 11 de mayo de 2026, atacantes publicaron 84 versiones maliciosas en 42 paquetes @tanstack/* mediante envenenamiento de caché de GitHub Actions. La carga útil roba tokens de GitHub, tokens de npm, credenciales de AWS/GCP/Azure e instala un demonio de interruptor de hombre muerto que borra ~/ en el momento en que se revoca el token robado.
Elige el punto de entrada que coincida con dónde te encuentras.
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
La imagen es multi-arquitectura (linux/amd64, linux/arm64) e incluye prueba de procedencia firmada mediante OIDC de GitHub. Verifica antes de ejecutar:
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 flujo de trabajo completo y endurecido se encuentra en
.github/workflows/tanstack-check.yml
y es el mismo flujo de trabajo que este repositorio ejecuta contra sí mismo.
| # | Verificación | Descripción |
|---|---|---|
| 1 | Interruptor de hombre muerto | ~/.local/bin/gh-token-monitor.sh, LaunchAgents + LaunchDaemons (macOS), unidades de usuario y sistema systemd (Linux), procesos en ejecución |
| 2 | Persistencia | archivos rc del shell, crontab de usuario/sistema, /etc/cron.*, arranque automático XDG, core.hooksPath global de git |
| 3 | Credenciales | vars de entorno sensibles, ~/.npmrc, ~/.yarnrc.yml, ~/.aws/credentials, ~/.config/gh/hosts.yml, ~/.netrc, ~/.docker/config.json, ~/.kube/config, enumeración de archivos .env* |
| 4 | IOC de red | procesos node en ejecución con conexiones TCP salientes establecidas (heurístico, revisión manual) |
| 5 | Escaneo de repositorio | dependencias declaradas en package.json + versiones fijadas en archivos de bloqueo (package-lock.json, yarn.lock, pnpm-lock.yaml) |
| 6 | Instalado | node_modules/@tanstack/*/package.json verificado contra la lista de versiones maliciosas conocidas, luego contra time[<version>] del registro npm cuando está --online |
| 7 | Pistas de Actions | .github/workflows/* escaneado en busca de pull_request_target, falta de --ignore-scripts, uso de actions/cache |
| 8 | IOC de Mini Shai-Hulud | archivos de carga útil en node_modules (router_init.js, tanstack_runner.js), @tanstack/setup como dependencia opcional, manipulación de configuración de herramientas de IA, dominios C2 en el código fuente, patrón de autor y rama del atacante, tokens de npm marcados con rescate, paquetes secundarios propagados por gusano, más heurísticas de nivel informativo para formas de carga útil desconocidas |
Con --online, el script también obtiene el aviso GHSA en tiempo de ejecución para que la cobertura de detección siga el estado vivo del aviso en lugar de una lista fija de versiones.
Familia afectada: @tanstack/router, @tanstack/react-router,
@tanstack/history, @tanstack/start, @tanstack/eslint-plugin-router, y
paquetes relacionados.
Familias limpias (no afectadas): @tanstack/react-query,
@tanstack/react-table, @tanstack/react-virtual, @tanstack/form,
@tanstack/store.
# Verificación rápida (directorio home del usuario actual)
bash check.sh
# Modo online: consulta el aviso GHSA y la hora de publicación del registro npm
bash check.sh --online
# Escanear un directorio específico
bash check.sh --scan-dir /ruta/a/tus/repos
# Salida JSON para integración CI/SIEM
bash check.sh --json > findings.json
# Sugerir comandos de fijación para repos afectados (simulación)
bash check.sh --scan-dir /ruta/a/repos --fix
# Proporcionar una lista extendida de versiones maliciosas conocidas
bash check.sh --bad-versions-file ./extra-bad-versions.txt
| Flag | Descripción |
|---|---|
--scan-dir DIR | Directorio raíz a escanear (por defecto: $HOME). Rechaza escanear directorios del sistema como /, /etc, /usr. |
--online | Consulta el aviso GHSA y time[<version>] del registro npm para confirmar el compromiso. Desactivado por defecto: opt-in. |
--ghsa GHSA-ID | Sobrescribe el ID del aviso usado en --online (por defecto GHSA-g7cv-rxg3-hmpx). |
--attack-window-start ISO, --attack-window-end ISO | Sobrescribe la ventana de tiempo de publicación usada en --online. |
--fix | Imprime comandos de fijación recomendados para repos afectados (simulación por defecto). |
--apply | Con --fix: opt-in para ejecución. Actualmente aún rechaza update a ciegas; se requiere fijación manual. |
--yes, -y | Omite la petición de confirmación para --apply. |
--json | Emite hallazgos como JSON a stdout (implica --quiet). |
--bad-versions-file F | Agrega entradas (pkg@version, una por línea) a la lista de versiones maliciosas conocidas. |
--no-color | Desactiva colores ANSI. |
--quiet, -q | Suprime la salida por verificación, imprime solo el resumen. |
-h, --help | Muestra ayuda. |
| Código | Significado |
|---|---|
| 0 | Limpio: sin indicadores dentro del alcance escaneado |
| 1 | Solo advertencias: se recomienda revisión |
| 2 | Fallos: indicadores de compromiso presentes |
| 3 | Error de herramienta/uso |
Este repositorio se defiende contra la misma clase de ataque que detecta. Concretamente:
uses: de GitHub Action está fijado a un SHA de commit de 40 caracteres, no a una etiqueta flotante.permissions: {} en el nivel superior; los trabajos conceden solo lo que necesitan.actions/checkout se invoca con persist-credentials: false para que no quede ningún remoto git autenticado en $GITHUB_WORKSPACE.step-security/harden-runner es el primer paso de cada flujo de trabajo y monitorea el tráfico de salida del runner.main; los resultados se cargan como SARIF.gh attestation.check.sh.sha256 que cubre todos los artefactos instalables.La política completa está en SECURITY.md.
No uses curl | bash para ninguna herramienta de seguridad, incluida esta. Siempre descarga, verifica el checksum, inspecciona y luego ejecuta.
━━ 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.
NO revoques tu token de GitHub antes de detener el demonio: el interruptor de hombre muerto ejecutará
rm -rf ~/en el momento en que se revoque el token.
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.El script bash necesita:
bash 4+jq (recomendado) — análisis de package-lock.jsonpython3 (alternativa opcional) — se usa cuando jq está ausentecurl — requerido para --onlinelsof o ss (opcional) — heurística de IOC de redLa imagen Docker incluye todo lo anterior. Úsala en sistemas donde no puedas o no quieras instalar estas herramientas.
Las incidencias y solicitudes de extracción son bienvenidas. Ten en cuenta que este repositorio usa CODEOWNERS: todos los PR requieren revisión de un mantenedor.
MIT.