Il 28 agosto 2026 è stato compromesso il pacchetto npm @7nohe/openapi-react-query-codegen, uno strumento che genera client TypeScript e hook per TanStack Query a partire da schemi OpenAPI.[1][2][3]
L’attaccante non ha avuto bisogno della password npm del maintainer né di un token di pubblicazione a lunga durata.
Il percorso di attacco era nel workflow di release di GitHub Actions.
Il repository ascoltava eventi:
issue_comment
e un commento su una pull request con testo esatto:
npm publish
poteva entrare nel percorso di release senza verificare correttamente se l’autore fosse maintainer, collaborator o un utente GitHub esterno.[1][2]
Il pipeline poi:
faceva checkout del codice della PR
→ eseguiva pnpm install
→ aveva id-token: write
→ pubblicava tramite npm Trusted Publishing
Risultato: 10 versioni malevole sono state pubblicate con npm provenance valida.[1][3]
La lezione più importante è:
La provenance può dimostrare correttamente da dove proviene un build anche quando quel build è malevolo.
C’è inoltre una correzione fondamentale da fare: @tanstack/react-query non è stato compromesso in questo incidente. Il pacchetto attaccato è un code generator indipendente che genera codice per TanStack Query.[6][7]
Stato delle informazioni: 31 agosto 2026.
TL;DR
| Domanda | Risposta verificata |
|---|---|
| Pacchetto compromesso | @7nohe/openapi-react-query-codegen |
| Data | 28 agosto 2026 |
| Versioni malevole | 10 |
| Versioni stabili malevole | 8 |
| Prerelease malevoli | 2 |
@tanstack/react-query compromesso? | No |
| Vettore principale | Workflow GitHub Actions issue_comment configurato in modo insicuro |
| Password maintainer necessaria? | No |
| Long-lived npm token necessario? | No |
| Autorità di publish | id-token: write + npm Trusted Publishing |
| Provenance valida? | Sì |
| Provenance = codice sicuro? | No |
| Percorsi di esecuzione | binding.gyp e, in alcune versioni, preinstall |
| Payload principale | 3FWCvzduYZg.js |
latest attuale | 3.0.2 |
| Severity | Critical, CVSS 9.6 |
| CVE | Nessun CVE noto nell’advisory al 31 agosto |
| Famiglia | Attività associata/derivata da Mini Shai-Hulud |
| Attribuzione dell’operatore certa? | No |
| Risposta prioritaria | Isolare l’host e ruotare tutte le credenziali raggiungibili da una macchina pulita |
Cosa è successo il 28 agosto?
L’advisory GitHub GHSA-9pvf-vcx3-x239 elenca 10 versioni malevole pubblicate all’incirca tra le 20:00 e le 20:21 UTC.[1]
Otto versioni stabili:
0.5.4
0.5.5
1.6.3
1.6.4
2.2.1
2.2.2
3.0.3
3.0.4
e due prerelease:
0.0.0-365d4eb738d3146583431948d3ba6e27a32556be
0.0.0-ec7876d6c917dad516ba69bbfafc948b834bf0ab
Il tag latest ha puntato alla versione malevola 3.0.4 da circa 20:19:29 UTC fino a circa 22:51 UTC, quando è stato ripristinato alla versione pulita 3.0.2.[1]
TanStack Query non è stato compromesso
Titoli come:
TanStack Query hacked
sono imprecisi.
@7nohe/openapi-react-query-codegen è un progetto indipendente.
Genera codice per:
@tanstack/react-query
a partire da una OpenAPI schema.[6]
È un collegamento tecnologico, non una proprietà del progetto TanStack.
La pagina ufficiale npm di @tanstack/react-query identifica un pacchetto distinto, e il postmortem TanStack di maggio indicava espressamente Query tra le famiglie non compromesse.[7][11]
Cosa fa openapi-react-query-codegen?
Genera tra l’altro:
- typed API clients,
- query keys,
queryOptions,infiniteQueryOptions,useQuery,useMutation,- helper suspense,
- helper prefetch e SSR.
Workflow tipico:
OpenAPI schema
↓
openapi-react-query-codegen
↓
TypeScript client
↓
TanStack Query hooks/options
È comunemente una dev dependency.
Questo non la rende meno pericolosa.
Quanto era diffuso il pacchetto?
Il 31 agosto npm mostrava circa 155.000 download settimanali.[6]
Il numero è dinamico e va trattato come fotografia del momento.
Più importante è dove gira un code generator:
- laptop degli sviluppatori,
- CI,
- release pipeline,
- repository con credenziali,
- macchine con strumenti GitHub e cloud.
Sono ambienti ad alto valore per un malware credential-stealing.
L’attacco è iniziato in GitHub Actions
Il workflow ascoltava:
on:
issue_comment:
types: [created]
issue_comment non è di per sé una vulnerabilità.
La catena pericolosa era:
commento esterno
+
nessun controllo di autorizzazione dell’autore
+
checkout del codice della PR
+
esecuzione del codice
+
id-token: write
+
publish
Un solo commento poteva entrare nel release path
Il workflow verificava essenzialmente che:
- il commento appartenesse a una pull request,
- il testo fosse esattamente
npm publish.
Mancava un gate affidabile su ruoli come:
OWNER
MEMBER
COLLABORATOR
Un account GitHub esterno poteva quindi preparare una fork PR e raggiungere il percorso privilegiato con un commento.
Il checkout della fork ha trasformato input in code execution
Dopo il checkout del codice controllato dalla PR, il pipeline eseguiva:
pnpm install
A quel punto il contenuto non trusted non era più semplice input.
Veniva eseguito dentro un release job privilegiato.
GitHub raccomanda di non eseguire codice di pull request non trusted in workflow che possiedono privilegi elevati.[9]
id-token: write ha aperto il percorso verso npm
Il job disponeva di:
permissions:
id-token: write
È una permission legittima quando si usa npm Trusted Publishing con OIDC.[10]
Il problema non era OIDC.
Il problema era che codice controllato dall’attaccante poteva essere eseguito nel job che aveva il diritto di ottenere quella identità.
Non era necessario rubare password o publish token
StepSecurity sottolinea che non era necessario possedere:
- la password npm del maintainer,
- un long-lived npm publish token.
Threat model classico:
ruba il token
→ publish
Threat model moderno:
controlla l’esecuzione del trusted workflow
→ il workflow riceve una identità valida
→ publish
La frontiera di sicurezza si sposta dall’account al CI/CD.
Le release malevole avevano provenance valida
Le versioni sono state pubblicate dal workflow reale con Trusted Publishing, quindi disponevano di provenance valida.[1][3]
La provenance poteva affermare correttamente:
questo artifact è stato pubblicato
da questo workflow
da questo repository
Il problema era che quel workflow stava eseguendo codice non autorizzato.
Provenance non è malware detection
Provenance può dimostrare:
- origine del build,
- workflow identity,
- collegamento al source,
- chain of custody.
Non dimostra automaticamente:
- source code sicuro,
- PR autorizzata,
- workflow logic sicura,
- assenza di malware.
Quindi:
valid provenance
≠
safe package
La documentazione npm descrive Trusted Publishing e provenance come meccanismi di autenticazione e origine, non come scanner universali di malware.[10]
Primo execution path: binding.gyp
Alcune release stabili malevole non avevano un evidente:
"preinstall": "..."
Introducevano invece:
binding.gyp
Il meccanismo GYP/Python veniva abusato per raggiungere os.system() ed eseguire il payload JavaScript.[1][5]
Per i defender è un punto importante.
Una regola che cerca soltanto lifecycle scripts in package.json non basta.
Secondo execution path: preinstall
Le successive versioni stabili malevole aggiungevano anche:
"preinstall": "node 3FWCvzduYZg.js"
Versioni:
0.5.5
1.6.4
2.2.2
3.0.4
Le versioni precedenti:
0.5.4
1.6.3
2.2.1
3.0.3
usavano binding.gyp senza un lifecycle script esplicito.[1]
I due prerelease erano diversi
I due 0.0.0-* prerelease contenevano un riferimento malevolo in preinstall, ma:
"files": ["dist"]
escludeva i file payload dal tarball.[1]
Tutte e 10 sono pubblicazioni malevole, ma non sono artifact tecnicamente identici e non hanno la stessa execution path.
Payload principale: 3FWCvzduYZg.js
I tarball stabili malevoli contenevano:
3FWCvzduYZg.js
con dimensione di circa 4,4–6,4 MB a seconda della versione.[1]
StepSecurity ha rilevato che 0.5.4 è passata da circa 41 KB in 0.5.3 a oltre 5,6 MB.[2]
Un aumento improvviso della package size è quindi un segnale pratico di anomalia supply-chain.
Cosa faceva il payload in runtime?
StepSecurity ha eseguito versioni affette in runner GitHub isolati.[2]
Tra i comportamenti osservati:
Node avvia il payload
→ curl scarica Bun
→ Bun gira da /tmp/trinnyyyy-*
→ viene chiamato gh auth token
→ viene interrogato Git Credential Manager
→ vengono controllati ssh / scp
→ vengono enumerati i processi
→ viene eseguito updater.py temporaneo
Sono stati inoltre osservati accessi a GitHub API e probe verso l’hostname metadata di Google Cloud.[2]
È quindi malware orientato alle credenziali.
Quali secret devono essere considerati esposti?
Il reale impatto dipende dai privilegi del processo.
Se una versione malevola è stata eseguita, vanno considerati potenzialmente esposti tutti i credential accessibili:
GitHub
npm
SSH
cloud
CI/CD
deployment
source repositories
Non significa che ogni secret sia stato effettivamente rubato su ogni host.
Perché una dev dependency può essere critica
Pensare:
è solo codegen
non entra nel browser bundle
è un modello di rischio sbagliato.
Il malware deve soltanto eseguirsi durante:
npm install
pnpm install
yarn install
Le dev dependency girano spesso proprio sulle macchine con più privilegi e più secret.
npm install è una superficie di code execution
I package manager supportano lifecycle hooks come:
preinstall
install
postinstall
prepare
I flow native addon possono inoltre invocare node-gyp.
L’installazione è quindi:
dependency resolution
+
download
+
potential code execution
La supply-chain security deve proteggere anche questa fase.
È stato sicuramente TeamPCP?
JFrog, Socket e altri ricercatori collegano il malware alla famiglia Mini Shai-Hulud e a varianti correlate.[3][4]
Ma l’attribuzione precisa dell’operatore è meno certa.
Possibili scenari includono:
- stesso gruppo,
- accesso residuo,
- reuse del malware,
- copycat.
JFrog evidenzia esplicitamente questa incertezza.[4]
Perché Mini Shai-Hulud continua a comparire?
La famiglia/campagna si concentra su:
- credential theft,
- CI/CD,
- npm,
- GitHub,
- propagazione.
Nel maggio 2026 ricercatori hanno osservato ondate che coinvolgevano TanStack, Mistral, UiPath e altri ecosistemi.[12]
Pattern ricorrente:
entra dal build/release
→ ottieni publish authority
→ pubblica package apparentemente trusted
→ esegui su developer/CI
→ cerca altre credenziali
→ propaga
L’incidente TanStack di maggio era tecnicamente diverso
L’11 maggio 2026 il repository colpito era TanStack Router/Start.[11]
La catena univa:
pull_request_target,- GitHub Actions cache poisoning,
- estrazione dell’OIDC token dalla memoria del runner.
Risultato:
42 pacchetti
84 versioni malevole
Non è lo stesso root cause dell’incidente del 28 agosto.
Anche a maggio Query era pulito
Il postmortem ufficiale TanStack dichiarava:
- affected: Router/Start,
- unaffected: Query, DB, Store, AI, Table, Form, Virtual e altre famiglie.
Quindi:
maggio → Router/Start compromesso
agosto → codegen indipendente per Query compromesso
Nessuno dei due casi giustifica la frase “@tanstack/react-query era infetto”.
L’incidente di maggio ha mostrato un impatto downstream reale
OpenAI ha confermato che due device di dipendenti erano stati colpiti dall’attacco TanStack/Mini Shai-Hulud di maggio.[13]
OpenAI ha riportato:
- attività focalizzata su credentials,
- esfiltrazione limitata di materiale credential da un sottoinsieme di repository,
- nessuna evidenza di accesso a user data,
- nessuna evidenza di compromissione di production systems o IP,
- rotazione di credentials e signing certificates come precauzione.
Una finestra di esposizione breve può quindi produrre conseguenze reali.
Agosto vs maggio
Maggio
fork PR
→ pull_request_target
→ poisoned cache
→ trusted release ripristina cache
→ OIDC token estratto dalla memoria
→ publish
Agosto
fork PR
→ commento pubblico "npm publish"
→ workflow privilegiato fa checkout della PR
→ pnpm install esegue codice attacker
→ il job possiede già id-token: write
→ Trusted Publishing
La catena di agosto è più semplice.
Come verificare se un progetto è stato colpito?
Cerca esattamente le 10 versioni affette nei lockfile e nella cronologia CI.[1]
Controlla:
package-lock.json
pnpm-lock.yaml
yarn.lock
bun.lock
SBOM
CI install logs
dependency caches
Non basta controllare il package.json corrente.
Un range flottante può aver risolto temporaneamente una versione malevola.
IOC principali
3FWCvzduYZg.js
binding.gyp
is_it_this_simple.js
nu.js
/tmp/trinnyyyy-*/bun
/tmp/*/updater.py
Cerca inoltre:
- download inattesi di Bun durante install,
gh auth token,- chiamate anomale a Git credential manager,
- traffico outbound inatteso durante package install,
- esecuzione di node-gyp in package senza motivo per native code.
Cosa fare dopo un’installazione affetta?
GitHub advisory e StepSecurity consigliano di trattare l’host come potenzialmente compromesso.[1][2]
Response minimo:
1. fermare i build
2. isolare l’host
3. usare una macchina separata e pulita
4. ruotare tutte le credenziali raggiungibili
5. invalidare le sessioni
6. controllare GitHub/npm/cloud audit logs
7. eliminare dependency caches
8. scartare artifact costruiti sul runner esposto
9. ricostruire da input puliti
Non ruotare soltanto il token npm.
Quali versioni sono sicure?
GitHub advisory indica 3.0.2 come known-good e afferma che nessuna versione pubblicata prima del 28 agosto è coinvolta in questo incidente.[1]
StepSecurity indica come predecessori puliti:
0.5.3
1.6.2
2.2.0
3.0.2
Il 31 agosto npm mostra di nuovo:
latest = 3.0.2
Cosa ha corretto il maintainer?
Secondo l’advisory upstream:[1]
- rimosso il trigger
issue_comment, - spostate le release su tag push,
- revocato npm Trusted Publisher,
- ruotati i long-lived token,
- ripristinato
latesta3.0.2, - rimosso il dist-tag
pre, - deprecated tutte le 10 versioni malevole,
- richiesta rimozione a npm security.
Snyk classifica a sua volta la compromissione come Critical, con CVSS 9.6, confermando la gravità dell’esecuzione di codice malevolo durante l’installazione.[8] Sono inoltre rilevanti le misure di hardening introdotte da TanStack dopo l’incidente di maggio per cache, workflow e release pipeline.[14]
Come rafforzare GitHub Actions
- Non eseguire untrusted PR code in job privilegiati.
- Aggiungere authorization gate ai workflow comment-driven.
- Usare
id-token: writesolo nel vero publish job. - Separare build e publish.
- Richiedere trusted trigger o approval.
- Pinnare third-party Actions a commit SHA.[9]
Come rafforzare dependency install
Controlli utili:
- lockfiles,
- frozen installs,
- minimum release age/cooldown,
- monitoring delle nuove release,
- package-size anomaly detection,
- lifecycle-script policy,
- CI sandboxata,
- network egress policy,
- malicious-package detection oltre ai CVE,
- SBOM,
- credentials minimi sui runner.
npm Trusted Publishing rimane utile.[10]
Ma:
Trusted Publishing
deve essere accompagnato da:
Trusted Workflow Design
Checklist di produzione
Dependencies
- Lockfile obbligatorio.
- CI blocca cambi lockfile inattesi.
- Minimum release age/cooldown.
- Nuove versioni non entrano subito in produzione.
- Monitoraggio package-size.
- Controllo install scripts.
-
binding.gypinatteso genera alert. - Malware scanning oltre al CVE scanning.
GitHub Actions
- Fork code mai in contesto privilegiato.
-
issue_commentcon authorization gate. -
pull_request_targetsolo se necessario. -
GITHUB_TOKENleast privilege. -
id-token: writesolo dove indispensabile. - Publish richiede trusted trigger.
- Build e publish separati.
- Actions pinnate a SHA.
- L’automazione guidata da commenti ha un test negativo per utenti non autorizzati.
- Il release workflow non fa checkout di codice da fork non trusted.
npm
- Trusted Publisher limitato ai workflow necessari.
- Nessun long-lived publish token superfluo.
- Monitoring realtime delle pubblicazioni.
- Alert per release non pianificate.
- Provenance verificata ma non usata come malware verdict.
- Runbook per deprecation/revocation.
Runner e workstation
- CI runner ephemeral.
- Secrets minimi nel workspace.
- Cloud credentials short-lived.
- SSH keys con scope minimo.
- Outbound traffic monitorato.
- Dependency install senza accesso globale all’infrastruttura.
- EDR/runtime monitoring.
- Possibilità di isolare rapidamente l’host.
Incident response
- Owner definito.
- Ricerca storica nei lockfile.
- Purge rapida delle CI cache.
- Sessioni invalidabili rapidamente.
- Mappa delle credentials accessibili dai runner.
- Rotazione da clean machine.
- Controllo di publish npm non autorizzati.
- Review GitHub audit logs.
Verdetto POLPROG
La compromissione di @7nohe/openapi-react-query-codegen è uno degli esempi più chiari del 2026 di come stanno cambiando gli attacchi software supply-chain.
L’attaccante non ha dovuto:
hackerare npm
rubare la password del maintainer
bypassare 2FA
rubare un classico publish token
È bastato far eseguire codice non trusted dentro un workflow che possedeva già una legittima publish identity.
La frontiera di sicurezza si sposta quindi verso:
CI/CD graph
workflow triggers
artifacts
caches
OIDC
release authorization
dependency execution
Seconda lezione: provenance.
La provenance valida non ha “fallito”. Ha correttamente attestato l’origine di un build malevolo.
Terza lezione: precisione.
TanStack Query stesso non è stato compromesso.
È stato compromesso un tool third-party usato nel suo ecosistema.
Al 31 agosto:
versioni malevole: identificate
advisory: pubblicato
latest: tornato a 3.0.2
release workflow: modificato
La domanda chiave per un team JavaScript non è soltanto:
Usiamo questo package?
Ma:
Nel nostro release pipeline esiste un percorso che consente a un input non trusted di raggiungere un job con diritto di pubblicazione?
Se la risposta non è immediatamente chiara, vale la pena fare ora un audit di GitHub Actions.

