Le 28 août 2026, le paquet npm @7nohe/openapi-react-query-codegen a été compromis. Il génère des clients TypeScript et des hooks TanStack Query à partir de schémas OpenAPI.[1][2][3]
L’attaquant n’a pas eu besoin du mot de passe npm du mainteneur ni d’un token de publication longue durée.
La faille était dans le workflow de release GitHub Actions.
Un événement :
issue_comment
pouvait lancer le chemin de publication lorsqu’un commentaire de pull request contenait exactement :
npm publish
sans contrôle suffisant du rôle de l’auteur.[1][2]
Le pipeline faisait ensuite :
checkout du code du PR
→ pnpm install
→ id-token: write
→ npm Trusted Publishing
Résultat : 10 versions malveillantes ont été publiées avec une provenance npm valide.[1][3]
La leçon centrale est simple :
La provenance peut prouver correctement l’origine d’un build alors que le build lui-même est malveillant.
Autre précision essentielle : @tanstack/react-query n’a pas été compromis dans cet incident. Le paquet attaqué est un générateur tiers utilisé avec TanStack Query.[6][7]
État des informations : 31 août 2026.
TL;DR
| Question | Réponse vérifiée |
|---|---|
| Paquet compromis | @7nohe/openapi-react-query-codegen |
| Date | 28 août 2026 |
| Versions malveillantes | 10 |
| Versions stables malveillantes | 8 |
| Prereleases malveillantes | 2 |
@tanstack/react-query compromis ? | Non |
| Vecteur principal | Workflow GitHub Actions issue_comment mal configuré |
| Mot de passe mainteneur nécessaire ? | Non |
| Long-lived npm token nécessaire ? | Non |
| Autorité de publication | id-token: write + npm Trusted Publishing |
| Provenance valide ? | Oui |
| Provenance = code sûr ? | Non |
| Chemins d’exécution | binding.gyp, plus preinstall pour certaines versions |
| Payload principal | 3FWCvzduYZg.js |
latest actuel | 3.0.2 |
| Sévérité | Critical, CVSS 9.6 |
| CVE | Aucun CVE connu dans l’advisory au 31 août |
| Famille | Activité liée/dérivée de Mini Shai-Hulud |
| Attribution opérateur certaine ? | Non |
| Réponse prioritaire | Isoler l’hôte et tourner les credentials depuis une machine propre |
Que s’est-il passé le 28 août ?
L’advisory GitHub GHSA-9pvf-vcx3-x239 recense 10 versions malveillantes publiées entre environ 20:00 et 20:21 UTC.[1]
Versions stables :
0.5.4
0.5.5
1.6.3
1.6.4
2.2.1
2.2.2
3.0.3
3.0.4
Prereleases :
0.0.0-365d4eb738d3146583431948d3ba6e27a32556be
0.0.0-ec7876d6c917dad516ba69bbfafc948b834bf0ab
Le tag latest pointait vers 3.0.4 d’environ 20:19:29 UTC jusqu’à environ 22:51 UTC, puis a été rétabli sur 3.0.2.[1]
TanStack Query lui-même n’a pas été compromis
Dire « TanStack Query hacked » est incorrect.
@7nohe/openapi-react-query-codegen est un projet indépendant qui génère du code pour :
@tanstack/react-query
C’est une relation technologique, pas une appartenance au projet TanStack.
Le paquet officiel @tanstack/react-query est distinct, et le postmortem de TanStack sur l’incident de mai listait également Query parmi les familles non affectées.[7][11]
À quoi sert le paquet ?
Il génère notamment :
- clients API typés,
- query keys,
queryOptions,infiniteQueryOptions,useQuery,useMutation,- helpers Suspense, prefetch et SSR.
Flux typique :
OpenAPI schema
↓
codegen
↓
TypeScript client
↓
TanStack Query hooks/options
Quelle était sa popularité ?
Le 31 août, npm affichait environ 155 000 téléchargements hebdomadaires.[6]
C’est une métrique dynamique.
Plus important : ce type de dev dependency tourne sur des machines de développeurs et des runners CI, souvent proches de secrets GitHub, SSH, cloud et deployment.
L’attaque a commencé dans GitHub Actions
Le workflow écoutait :
on:
issue_comment:
types: [created]
La chaîne dangereuse était :
commentaire externe
+ pas de contrôle d’autorisation
+ checkout du PR
+ exécution du code
+ id-token: write
+ publish
Un commentaire pouvait entrer dans le chemin de release
Le workflow vérifiait essentiellement que le commentaire concernait un PR et contenait exactement npm publish.[1][2]
Il manquait une vérification robuste de type :
OWNER
MEMBER
COLLABORATOR
Un compte externe pouvait donc atteindre le job privilégié.
Le checkout du fork a transformé des données en code exécuté
Le pipeline checkoutait le code du PR puis exécutait :
pnpm install
Le contenu contrôlé par l’attaquant s’exécutait alors dans un contexte privilégié.
GitHub déconseille explicitement d’exécuter du code de PR non fiable dans des workflows disposant de privilèges élevés.[9]
id-token: write a ouvert la voie à npm
Le job possédait :
permissions:
id-token: write
Cette permission est légitime pour npm Trusted Publishing via OIDC.[10]
Le problème n’était pas OIDC, mais l’exécution de code non fiable dans un job autorisé à obtenir cette identité.
Aucun vol de mot de passe npm n’était nécessaire
StepSecurity souligne que l’attaquant n’avait pas besoin d’un mot de passe mainteneur ni d’un token de publication longue durée.[2]
Ancien modèle :
voler un token → publier
Nouveau modèle :
contrôler l’exécution du workflow de confiance
→ obtenir une identité légitime
→ publier
Les releases malveillantes avaient une provenance valide
Elles ont été publiées par le véritable workflow avec Trusted Publishing, donc avec des attestations de provenance valides.[1][3]
La provenance disait correctement :
cet artefact vient
de ce repo
via ce workflow
Mais elle ne disait pas que le code checkouté avait été autorisé.
Provenance n’est pas un scanner de malware
La provenance peut vérifier l’origine et la chaîne de build.
Elle ne garantit pas :
- que le source est sain,
- que le PR est autorisé,
- que le workflow est sûr,
- que l’artefact est exempt de malware.
Donc :
valid provenance ≠ safe package
Premier chemin d’exécution : binding.gyp
Certaines versions n’avaient aucun preinstall visible.[1]
Elles ajoutaient plutôt :
binding.gyp
Le mécanisme GYP/Python était détourné afin d’atteindre os.system() et lancer le JavaScript malveillant.[1][5]
Une règle de sécurité qui ne cherche que preinstall/postinstall est donc insuffisante.
Deuxième chemin : preinstall
D’autres versions stables ajoutaient :
"preinstall": "node 3FWCvzduYZg.js"
Versions concernées :
0.5.5
1.6.4
2.2.2
3.0.4
Les versions stables précédentes utilisaient binding.gyp sans lifecycle script explicite.[1]
Les deux prereleases étaient différentes
Les deux versions 0.0.0-* avaient un preinstall malveillant, mais :
"files": ["dist"]
a exclu les fichiers payload des tarballs.[1]
Les 10 publications sont malveillantes, mais leurs chemins d’exécution ne sont pas identiques.
Payload principal : 3FWCvzduYZg.js
Les tarballs stables contenaient un fichier obfusqué :
3FWCvzduYZg.js
de l’ordre de 4,4 à 6,4 Mo.[1]
StepSecurity a montré que 0.5.4 est passé d’environ 41 Ko dans 0.5.3 à plus de 5,6 Mo.[2]
Les variations brutales de taille sont donc un signal utile.
Comportement runtime
Dans des runners isolés, StepSecurity a observé :[2]
Node lance le payload
→ curl télécharge Bun
→ Bun s’exécute dans /tmp/trinnyyyy-*
→ gh auth token
→ Git Credential Manager
→ recherche ssh/scp
→ énumération de processus
→ updater.py temporaire
Des accès à l’API GitHub et au hostname metadata Google Cloud ont aussi été observés.
Quels secrets doivent être considérés exposés ?
Si une version affectée s’est exécutée, tout credential accessible au processus doit être considéré comme potentiellement exposé :
GitHub
npm
SSH
cloud
CI/CD
deployment
Cela ne signifie pas que chaque secret a forcément été exfiltré sur chaque hôte.
Pourquoi une dev dependency peut être critique
« Ce n’est qu’un outil de codegen » n’est pas une défense.
Le malware n’a besoin que de s’exécuter pendant :
npm install
pnpm install
yarn install
Les environnements de développement et CI contiennent souvent les credentials les plus intéressants.
Installer un package est une surface d’exécution
Les package managers prennent en charge :
preinstall
install
postinstall
prepare
et des flows comme node-gyp.
L’installation est donc :
résolution
+
téléchargement
+
exécution potentielle
TeamPCP est-il certainement responsable ?
JFrog, Socket et d’autres chercheurs relient ce code à Mini Shai-Hulud et ses dérivés.[3][4]
Mais l’identité exacte de l’opérateur n’est pas démontrée de façon définitive.
Réutilisation de code, accès résiduel ou copycat restent possibles.[4]
Pourquoi Mini Shai-Hulud revient-il ?
Cette famille d’attaques vise les credentials, CI/CD, GitHub et package registries.[12]
Le modèle récurrent :
compromettre le chemin de release
→ obtenir des droits de publication
→ publier un package crédible
→ s’exécuter chez développeurs/CI
→ voler d’autres credentials
L’incident TanStack de mai était différent
Le 11 mai 2026, le repo Router/Start de TanStack a été compromis.[11]
La chaîne combinait :
pull_request_target,- cache poisoning,
- extraction d’un token OIDC de la mémoire du runner.
Résultat :
42 packages
84 versions malveillantes
Query était aussi sain en mai
TanStack a explicitement déclaré que Query faisait partie des familles non compromises.[11]
Mai :
Router/Start compromis
Août :
générateur tiers pour Query compromis
L’incident de mai a eu de vraies conséquences downstream
OpenAI a confirmé que deux appareils d’employés avaient été affectés par l’incident de mai.[13]
OpenAI a décrit une activité centrée sur les credentials et une exfiltration limitée de matériel credential, tout en indiquant ne pas avoir trouvé de preuve d’accès aux données utilisateurs ni de compromission de systèmes de production ou de propriété intellectuelle.[13]
Août vs mai
Mai
fork PR
→ pull_request_target
→ cache empoisonné
→ release de confiance
→ token OIDC extrait
→ publish
Août
fork PR
→ commentaire public "npm publish"
→ workflow privilégié
→ pnpm install
→ id-token: write
→ Trusted Publishing
La voie d’août est plus simple.
Comment vérifier son exposition ?
Cherchez les 10 versions affectées dans :[1]
package-lock.json
pnpm-lock.yaml
yarn.lock
bun.lock
SBOM
logs CI
dependency caches
Ne regardez pas uniquement le package.json actuel.
IOCs principaux
Indicateurs publics :[1][2][5]
3FWCvzduYZg.js
binding.gyp
is_it_this_simple.js
nu.js
/tmp/trinnyyyy-*/bun
/tmp/*/updater.py
Cherchez aussi des téléchargements Bun inattendus, gh auth token, appels Git credential manager et exécution node-gyp anormale.
Remédiation
Si une version affectée s’est exécutée :[1][2]
1. arrêter les builds
2. isoler l’hôte
3. utiliser une machine propre
4. tourner tous les credentials accessibles
5. invalider les sessions
6. examiner les audit logs
7. purger les caches
8. rejeter les artifacts du runner exposé
9. reconstruire proprement
Quelles versions sont propres ?
L’advisory GitHub indique 3.0.2 comme known-good et précise que les versions antérieures au 28 août ne sont pas touchées par cet incident.[1]
StepSecurity cite :
0.5.3
1.6.2
2.2.0
3.0.2
comme prédécesseurs propres.[2]
Le 31 août, npm affiche de nouveau latest = 3.0.2.[6]
Qu’a corrigé le mainteneur ?
Selon l’advisory :[1]
- suppression du trigger
issue_comment, - releases sur tag push,
- révocation du Trusted Publisher npm,
- rotation des tokens longue durée,
- retour de
latestà3.0.2, - suppression du dist-tag
pre, - deprecation des 10 versions,
- demande de suppression à npm.
Snyk classe également cette compromission comme Critical, avec un score CVSS de 9.6, ce qui confirme la gravité de l’exécution de code malveillant à l’installation.[8] Les mesures de hardening ajoutées par TanStack après l’incident de mai sont aussi pertinentes pour les caches, workflows et pipelines de release.[14]
Durcir GitHub Actions
- Ne jamais exécuter du code de fork dans un job privilégié.
- Ajouter une vérification d’autorisation sur les commentaires.
- Limiter
id-token: writeau vrai job de publication. - Séparer build et publish.
- Utiliser un trusted trigger ou approval.
- Pinner les Actions tierces à un commit SHA.[9]
Durcir l’installation des dépendances
Contrôles utiles :
- lockfiles,
- frozen installs,
- minimum release age/cooldown,
- monitoring des nouvelles versions,
- anomalies de taille,
- policy sur lifecycle scripts,
- CI sandboxé,
- network egress limité,
- détection de packages malveillants en plus des CVE,
- SBOM,
- credentials minimaux.
Trusted Publishing reste utile.[10]
Mais il doit être associé à un Trusted Workflow Design.
Checklist de production
Dependencies
- Lockfile obligatoire.
- CI bloque les changements inattendus du lockfile.
- Release cooldown/minimum age.
- Pas de nouvelle version directement en production.
- Monitoring des variations de taille.
- Inspection des install scripts.
-
binding.gypinattendu signalé. - Détection malware en plus du scan CVE.
GitHub Actions
- Aucun code de fork dans un contexte privilégié.
- Authorization gate sur
issue_comment. -
pull_request_targetseulement si nécessaire. -
GITHUB_TOKENminimal. -
id-token: writeuniquement là où nécessaire. - Trusted trigger pour publish.
- Build séparé du publish.
- Actions pinnées par SHA.
- L’automatisation par commentaire a un test négatif pour les utilisateurs non autorisés.
- Le workflow de release ne checkout jamais le code d’un fork non fiable.
npm
- Trusted Publisher limité aux workflows nécessaires.
- Pas de long-lived publish tokens inutiles.
- Monitoring des publications.
- Alertes sur releases imprévues.
- Provenance vérifiée mais jamais considérée comme verdict malware.
- Runbook de deprecation/revocation.
Runners
- Runners CI éphémères.
- Secrets minimaux.
- Cloud credentials courte durée.
- SSH keys à scope réduit.
- Monitoring outbound.
- Installation sans accès à toute l’infrastructure.
- EDR/runtime monitoring.
- Isolation rapide possible.
Incident response
- Owner défini.
- Recherche historique des lockfiles possible.
- Purge des caches CI rapide.
- Sessions invalidables.
- Carte des credentials accessibles depuis les runners.
- Rotation depuis une machine propre.
- Vérification des publications npm non autorisées.
- Revue des GitHub audit logs.
Verdict POLPROG
L’incident @7nohe/openapi-react-query-codegen montre très clairement la nouvelle forme des attaques supply-chain.
L’attaquant n’a pas eu besoin de hacker npm, de voler un mot de passe mainteneur, de contourner le 2FA ou de voler un ancien token de publication.
Il a suffi d’exécuter du code non fiable dans un workflow qui possédait déjà une identité de publication légitime.
La frontière de sécurité se déplace donc vers :
CI/CD
workflow triggers
OIDC
release authorization
dependency execution
La deuxième leçon est la provenance.
Elle n’a pas menti : elle a correctement attesté l’origine d’un build malveillant.
La troisième leçon est la précision.
TanStack Query lui-même n’a pas été compromis.
C’est un outil tiers de son écosystème qui l’a été.
Au 31 août, les versions malveillantes sont connues, l’advisory est publié, latest est revenu sur 3.0.2 et le workflow a été modifié.[1][6]
La question pour les équipes JavaScript n’est donc pas seulement :
Utilisions-nous ce package ?
Mais surtout :
Un input non fiable peut-il atteindre chez nous un job disposant de droits de publication ?
Si la réponse n’est pas immédiatement claire, il faut auditer GitHub Actions.

