Une illustration abstraite générée sur un fond gris-bleu pâle : un grand panneau sombre gris ardoise se dresse à gauche du centre ; un fin faisceau cyan pâle le traverse horizontalement et s’épanouit sur la droite en un large cône de lumière à travers un champ de petits blocs géométriques. La plupart des blocs sont dans une ombre froide ; un groupe près du centre du faisceau brille d’un ambre chaud. Une légère grille de plan et de fins arcs concentriques se trouvent derrière. Aucun texte, chiffre, personne, marque ou logo d’organisation n’apparaît. Le logo OrcaRouter est intégré dans le coin inférieur droit.
AI Safety Incidents

Clinejection : comment un titre de ticket a atteint le token npm de Cline

Auteur

Rowan Sterling

Date de publication

Derniers modèles · 20Voir tous les modèles →
Benchmarks : Artificial Analysis · mis à jour quotidiennement
Retour à tous les articles

Le 9 février 2026, le chercheur en sécurité Adnan Khan a publié un compte rendu d'une faille qu'il avait signalée en privé à Cline le 1er janvier : l'assistant de codage IA open source avait connecté un trieur automatisé de tickets, construit sur l'action GitHub claude-code-action d'Anthropic, à un prompt qui collait directement tout titre de ticket GitHub entrant dans les instructions du modèle. N'importe quel compte GitHub pouvait le déclencher en ouvrant un ticket, et dans la reproduction de Khan, un titre formulé comme une instruction a amené l'agent à exécuter une commande shell au sein du workflow. Il a ensuite retracé le chemin depuis ce point d'appui, en passant par l'empoisonnement du cache de GitHub Actions, jusqu'aux identifiants de publication pour le VS Code Marketplace, OpenVSX et npm. Huit jours après que Cline a corrigé le workflow, un jeton de publication npm que Cline pensait avoir renouvelé a été utilisé pour publier cline 2.3.0, qui installait OpenClaw sur chaque machine ayant installé le paquet. L'entrée complète, avec ses niveaux de gravité et de confiance, se trouve dans le Orca AI Incident Archive, à côté des enregistrements voisins de la chaîne d'approvisionnement de la même période.

Deux dates, pas une seule

L’archive date cet enregistrement du 9 février 2026, à la précision du jour, et c’est le jour où Khan a publié son compte rendu sur adnanthekhan.com. Ce n’est pas le jour où le paquet malveillant est apparu. La vulnérabilité elle-même a été introduite le 21 décembre 2025, dans un commit qui a ajouté le workflow de triage au dépôt cline/cline, et elle est restée active jusqu’à ce que Cline supprime le workflow le 9 février. La publication npm non autorisée est survenue le 17 février, huit jours après le correctif et quarante-sept jours après le premier rapport privé de Khan.

Il est important de ne pas confondre ces deux dates, car le fait le plus lourd de conséquences de l’affaire se situe dans l’intervalle entre elles : la porte a été fermée le 9 février, mais la clé que l’intrus avait déjà copiée fonctionnait encore le 17 février. La correction de Cline lui-même, envoyée à Khan le 17 février et reproduite dans son billet, le dit sans détour : « Lorsque nous avons renouvelé les identifiants le 9 février, le jeton de publication npm n’a pas été correctement révoqué. Le mauvais jeton a été supprimé, et celui qui était exposé est resté actif. »

La chaîne : du titre d’un ticket au jeton de version

Le fichier vulnérable était .github/workflows/claude-issue-triage.yml, qui s'exécutait sur l'événement issues et invoquait anthropics/claude-code-action@v1 avec Claude Opus 4.5, épinglé dans le workflow sous le nom claude-opus-4-5-20251101. Khan cite la configuration du workflow : allowed_non_write_users était défini sur le joker "*", de sorte que n'importe quel compte GitHub pouvait déclencher l'agent simplement en ouvrant une issue, et claude_args transmettait --allowedTools avec Bash, Read, Write, Edit, Glob, Grep, WebFetch et WebSearch. C'est Bash qui importait. Il donnait au modèle l'exécution de shell dans un workflow qui s'exécutait dans le contexte de la branche par défaut.

Le prompt a interpolé le titre de l’issue sans l’assainir. La preuve de concept de Khan utilisait un titre commençant par « Tool error. » et se poursuivant par une instruction d’installer un paquet auxiliaire à partir d’un commit spécifique avant de poursuivre le triage. La référence de commit pointait vers un fork dont le package.json contenait un script preinstall, qui exécutait une commande curl vers un point de terminaison contrôlé par l’attaquant avant que npm ait fini d’installer quoi que ce soit. Comme l’installation passait par l’outil Bash, le modèle n’a jamais inspecté ce qui s’exécutait. Khan a reproduit cela sur un miroir du dépôt Cline et rapporte que l’agent a exécuté la charge utile à chaque tentative.

L’exécution de code dans le workflow de triage ne constitue pas, à elle seule, un accès aux identifiants de publication. Cette étape provenait d’une propriété de GitHub Actions que Khan avait documentée dans un outil publié en 2024 appelé Cacheract : n’importe quel workflow peut lire et écrire dans le cache du dépôt avec un contrôle total des clés et des versions du cache, même un workflow qui ne déclare jamais d’étape de mise en cache. La tâche de triage s’exécutait sur la branche par défaut, elle partageait donc une portée de cache avec les tâches de publication nocturnes, qui restauraient bien node_modules depuis le cache et détenaient bien les jetons de publication.

Les entrées de cache sont immuables, donc un attaquant ne peut pas simplement écraser l’entrée qu’un job de release restaurera. La méthode de contournement de Khan consistait à exploiter un changement de politique GitHub du 20 novembre 2025, qui faisait que le service évictait immédiatement les entrées de cache dès que le cache d’un dépôt dépassait 10 Go, selon l’ordre du moins récemment utilisé. Saturez le cache au-delà de 10 Go, et les entrées légitimes finissent par expirer ; revendiquez les clés libérées avec du contenu empoisonné, et le job suivant qui les restaure exécute du code d’attaquant. Cacheract automatise toute la séquence au sein d’une seule exécution de workflow, et il persiste en écrasant le action.yml de actions/checkout afin que l’étape post de l’action exécute une charge utile à la fin de chaque job.

Le dernier maillon de la chaîne est la tâche nocturne, qui s'exécute vers 2 h UTC, restaure le node_modules empoisonné et explose. Ce qui en ressort, ce sont VSCE_PAT, OVSX_PAT et NPM_RELEASE_TOKEN, les identifiants respectifs du VS Code Marketplace, d'OpenVSX et de npm. Savoir si chaque maillon s'est exécuté sur le propre dépôt de Cline, et dans cet ordre, est une question distincte, traitée ci-dessous.

A self-built HTML card titled 'Six steps from an issue title to a release token', badged 'Clinejection · GHSA-9ppg-jx86-fqw7', subtitled 'The chain as Adnan Khan describes it, with the workflow configuration he quotes'. Six numbered rows read: 1, Issue opened on cline/cline, the title carries the instructions and allowed_non_write_users is set to the wildcard; 2, claude-issue-triage.yml runs, claude-code-action with Bash, Read, Write, Edit, Glob, Grep, WebFetch and WebSearch; 3, npm install from a fork commit, the fork's package.json carries a preinstall script and the model runs it through Bash; 4, Cache flooded past 10 GB, GitHub's LRU eviction removes the legitimate entries and frees their keys; 5, Poisoned entries claim the keys, the nightly jobs restore attacker content as node_modules; 6, Nightly job runs at about 2 AM UTC, VSCE_PAT, OVSX_PAT and NPM_RELEASE_TOKEN leave the runner. A footer reads 'Source: Adnan Khan, Clinejection, 9 February 2026; Cline advisory GHSA-9ppg-jx86-fqw7.' The OrcaRouter logo is composited in the bottom-right corner.

Pourquoi les identifiants de la nightly étaient des identifiants de production

L'objection évidente est que les versions nightly et de production devraient détenir des secrets différents. La réponse de Khan est que la séparation était nominale. OpenVSX et le VS Code Marketplace lient un jeton de publication à une identité d'éditeur, et non à une extension individuelle, et les extensions Cline stable et nightly ont toutes deux été publiées sous la même identité, que Khan identifie sur OpenVSX comme saoudrizwan. Sur npm, le modèle de jetons à granularité fine est lié à un package, et Cline a publié ses builds CLI stable et nightly dans le même package cline. Un jeton émis pour la tâche nightly pouvait donc publier l'artefact de production, ce qui est exactement ce qui s'est produit huit jours plus tard.

La propre présentation de l’impact par Khan repose sur l’échelle : il cite l’annonce de Cline selon laquelle celle-ci a dépassé les 5 millions d’installations, et note qu’une mise à jour d’extension poussée au moyen d’identifiants volés s’exécute dans le contexte de chaque développeur dont l’éditeur se met à jour automatiquement, avec les autorisations de ce développeur et les clés, jetons et code source éventuellement présents sur la machine.

Ce qui a été réellement publié, et ce que cela a fait

L'artefact publié était cline 2.3.0, et il était de petite taille. Les horodatages propres au registre npm situent la 2.3.0 à 11:26:51 UTC le 17 février 2026 et la 2.4.0 propre à 19:23:08 UTC le même jour, une exposition que l'avis Cline estime à environ huit heures, jusqu'à ce que la 2.3.0 soit dépréciée à 11 h 30 PT. Le package.json du paquet comportait une ligne ajoutée, un script postinstall libellé npm install -g openclaw@latest. L'avis de Cline, GHSA-9ppg-jx86-fqw7, indique qu'aucun autre fichier du paquet n'a été modifié et que dist/cli.mjs est identique octet pour octet à la version légitime 2.2.3.

OpenClaw est un véritable framework d’agents IA open source, pas un logiciel malveillant, et l’avis de sécurité de Cline le dit explicitement : il s’agit d’un projet légitime, mais son installation n’était ni autorisée ni voulue. Endor Labs, qui a analysé le tarball, a jugé l’impact global faible au motif que l’installation n’a pas démarré le démon Gateway d’OpenClaw. L’analyse de StepSecurity mesure l’autre versant : une installation globale laisse le framework sur le disque avec les identifiants et les droits d’accès aux fichiers dont dispose l’utilisateur qui l’installe, et la conception d’OpenClaw, une passerelle locale persistante dotée d’une API opérateur, en fait un élément de grande valeur à y trouver. Les deux évaluations ne sont pas contradictoires. L’une décrit ce qui s’est exécuté pendant la fenêtre ; l’autre décrit ce qui a été laissé derrière ensuite.

Le volume est le chiffre qui doit être traité avec prudence. StepSecurity a rapporté que la version compromise a été téléchargée environ 4 000 fois avant d’être dépréciée, et The Register a repris ce chiffre. Endor Labs a estimé le nombre de téléchargements mensuels de cline, du 19 janvier au 17 février, à 418 545, ce qui décrit le trafic normal du paquet plutôt que la build malveillante. Aucun des deux chiffres ne compte les machines : un nombre de téléchargements n’est pas un décompte des développeurs affectés, et aucune partie n’a publié de chiffre sur la base d’installation pour 2.3.0. L’équipe Threat Intelligence de Microsoft a déclaré sur X avoir observé une « légère mais notable augmentation » des installations d’OpenClaw le 17 février, provenant du script d’installation de Cline, comme l’a rapporté The Hacker News.

La fiche d'avis GitHub pour GHSA-9ppg-jx86-fqw7 ne comporte ni CVE ni CWE, et sa gravité est Low. Cette note concerne la charge utile bénigne, et non le vol d'identifiants, et elle détonne à côté de la note critique de l'archive pour l'incident lui-même. L'avis indique 2.3.0 comme version affectée et 2.4.0 comme correctif.

La divulgation à laquelle Cline n'a pas répondu

La chronologie de Khan avant publication est la partie du dossier qui porte le moins sur le modèle et le plus sur le processus. Il a soumis un GHSA via le signalement privé de vulnérabilités de GitHub le 1er janvier 2026 et a envoyé un courriel au contact de sécurité indiqué sur la page de confiance de Cline le jour même. Il a envoyé un suivi à un développeur de Cline le 8 janvier. Il a tenté d’envoyer un message privé au PDG de Cline sur X le 18 janvier. Il a écrit de nouveau le 7 février et n’a reçu qu’un numéro de ticket automatisé. Il a publié le 9 février.

Cline a fusionné le correctif, la pull request 9211, environ trente minutes à une heure plus tard. La PR supprime trois fichiers de workflow, claude-issue-triage.yml, claude-pr-review.yml et cline-pr-review.yml, et retire la mise en cache de publish.yml, publish-nightly.yml, npm-nightly.yaml et npm-main.yaml, soit sept fichiers au total, les étapes d'installation devenant inconditionnelles puisqu'il n'y a désormais plus de correspondance de cache sur laquelle s'appuyer.

La réponse publique de Cline du 10 février, citée dans la publication de Khan, a accepté le constat et énuméré des mesures d’atténuation immédiates : la suppression des workflows de revue par bot des tickets et des PR, la suppression d’actions/cache des workflows de publication gérant les identifiants de version, et la rotation des identifiants concernés, notamment les jetons de bot, de fournisseur et de publication. Elle a également reconnu la gestion : « nous reconnaissons que notre délai de réponse à votre rapport initial était inacceptable. Un rapport soumis le 1er janvier n’aurait pas dû nécessiter une divulgation publique le 9 février pour être traité. »

Le 11 février, Cline a déclaré avoir effectué une rotation complète de VSCE_PAT, OVSX_PAT et NPM_RELEASE_TOKEN, et qu’un audit de chaque version publiée sur les trois canaux de distribution entre le 21 décembre 2025 et le 9 février 2026 avait établi que chaque version Marketplace et OpenVSX correspondait à une release GitHub taguée et que les 41 versions npm étaient conformes aux sources. Cline a conclu qu’aucune version non autorisée n’avait été publiée « pendant la fenêtre d’exposition alléguée ». Six jours plus tard, Cline a rectifié le compte rendu, dans le message cité ci-dessus : le jeton npm n’avait pas été révoqué, et la 2.3.0 avait été publiée avec celui-ci.

A self-built HTML card titled 'From private report to public disclosure', subtitled 'Khan's pre-publication timeline, and what followed'. Eight dated rows read: 21 Dec 2025, vulnerable claude-issue-triage.yml committed; 1 January 2026, GHSA filed and email sent to Cline's listed security contact; 8 January 2026, follow-up email to a Cline developer, no reply; 18 January 2026, direct message to Cline's CEO on X, no reply; 7 February 2026, final email, automated ticket number only; 9 February 2026, public disclosure and PR 9211 merged within about an hour; 11 February 2026, Cline reports full rotation and a clean audit; 17 February 2026, cline 2.3.0 published with a still-valid npm token. A footer reads 'Times and dates as recorded by Adnan Khan; the 17 February publish timestamp is corroborated by the npm registry.' The OrcaRouter logo is composited in the bottom-right corner.

Établi, attribué, non résolu

• Établi. La configuration du workflow est publique et a été publiée avant le correctif. Khan a reproduit l’injection de prompt sur un miroir du dépôt. Cline a fusionné la remédiation et publié un avis qui confirme la publication de la 2.3.0, la ligne postinstall ajoutée, le binaire CLI inchangé, la fenêtre de huit heures et l’échec du jeton. Les horodatages du registre npm pour 2.3.0 et 2.4.0 correspondent aux heures de l’avis. L’archive classe le dossier comme critique, avec un niveau de confiance A, un préjudice réel enregistré et aucun indicateur de contestation.

• Attribué, mais non établi. L’identité de l’auteur de la publication de la 2.3.0 reste une attribution ouverte. L’analyse forensique publiée par Michael Bargury et un outil d’investigation automatisé appelé Raptor le 19 février cite un compte GitHub, glthub-actions, et rapporte avec un haut degré de confiance que ce compte a forké cline/cline, ouvert l’issue 8904 le 28 janvier 2026 à 03:51 UTC avec le titre injecté, et utilisé un commit de fork pour exécuter une charge utile depuis un gist. Khan a confirmé que les comptes gcbrun et Dev11940518 lui appartiennent et que glthub-actions n’est pas le sien. Le compte avait déjà été supprimé ou suspendu au moment de la publication de l’analyse, et Cline n’a nommé personne. Considérez l’identité comme une conclusion émanant d’un tiers.

• La controverse porte sur la nature, non sur les faits. Bargury évalue l’épisode comme compatible avec une démonstration de recherche en sécurité plutôt qu’avec une campagne malveillante, citant la charge utile bénigne et l’historique de l’acteur vis-à-vis d’un dépôt d’automatisation New Relic. Cline qualifie cela de publication non autorisée, et Khan écrit que l’acteur ayant publié la 2.3.0 est « vraisemblablement » celui qui a empoisonné les caches. Personne n’a démontré d’intention de nuire, et personne n’a démontré qu’il n’y en avait pas.

• Non résolu. La note de prépublication de Khan lui-même se montre prudente sur un point que les articles ultérieurs aplatissent parfois : entre le 31 janvier et le 3 février 2026, plusieurs exécutions nocturnes ont échoué sur une étape Post Checkout qui ne produisait aucune sortie, ce qui constitue l'indicateur de compromission de Cacheract, et le workflow de triage est la piste la plus évidente, mais il n'est pas prouvé qu'il ait été le point d'entrée de cette activité. Aucune partie n'a non plus publié de preuves que les jetons Marketplace ou OpenVSX aient été utilisés ; le seul artefact que quiconque ait produit est le build npm.

A self-built HTML card titled 'What the record supports', subtitled 'One column is documented, one is other people's findings, one is simply open', split into three columns. The first, headed Established, lists four items: the triage workflow interpolated issue titles into the prompt and allowed any GitHub account to trigger it, with Bash among the tools; Khan reproduced the injection on a mirror of the repository; Cline's advisory GHSA-9ppg-jx86-fqw7 confirms cline 2.3.0, the added postinstall line, the unchanged CLI binary and an eight-hour window; npm registry timestamps place 2.3.0 at 11:26 UTC and 2.4.0 at 19:23 UTC on 17 February 2026. The second, headed Attributed, lists three items: the account glthub-actions and issue 8904 come from a third-party forensic write-up, not from Cline; that the actor copied Khan's public proof-of-concept repository is an inference from timing and from his own statement; that the publish was a research demonstration rather than an attack is one analyst's assessment. The third, headed Unresolved, lists three items: whether the triage workflow was the entry point for the late-January cache anomalies; whether any Marketplace or OpenVSX credential was used, with no artefact published; how many distinct machines installed 2.3.0, since about 4,000 is a download count. A footer reads 'Sources: Adnan Khan, Clinejection, 9 February 2026; Cline, GHSA-9ppg-jx86-fqw7; StepSecurity, 17 February 2026; Endor Labs, 18 February 2026; Michael Bargury and Raptor, 19 February 2026.' The OrcaRouter logo is composited in the bottom-right corner.

Qu’est-ce qui a changé par la suite ?

La remédiation s'est appliquée sur plusieurs couches à la fois, et il vaut la peine de nommer chacune d'elles, car elles répondent à des aspects différents de la défaillance. Le workflow accessible à un tiers a été supprimé plutôt que restreint. La mise en cache a été retirée des quatre workflows de release qui détiennent des secrets de publication, et non de l'ensemble du dépôt, et c'est précisément ce changement qui casse le pivot. Les identifiants ont été renouvelés et, après la correction du 17 février, le bon jeton npm a été révoqué. L'avis de Cline indique que la publication sur npm s'appuie désormais sur la provenance OIDC via GitHub Actions, et l'analyse des métadonnées de StepSecurity montre pourquoi cela comptait : les versions légitimes portaient déjà des attestations de provenance, et la 2.3.0 faisait exception, publiée par un compte humain. La faille tenait à ce que la voie du jeton de longue durée restait ouverte parallèlement à la publication de confiance, ce qui est exactement ce qu'Endor Labs estime que les mainteneurs devraient fermer. Cline a publié l'avis GHSA-9ppg-jx86-fqw7, déprécié la 2.3.0 avec le message indiquant qu'elle ne doit pas être installée, et orienté les utilisateurs vers la 2.4.0 ou une version ultérieure.

Les recommandations structurelles de Khan vont plus loin que ce qui a été mis en œuvre et méritent d’être consignées comme les siennes, et non comme celles de Cline : retirer Bash, Write et Edit de tout flux de travail de triage et ne lui laisser que la lecture de fichiers et les appels spécifiques à GitHub CLI dont il a besoin ; ne jamais restaurer un cache dans une tâche qui détient des secrets de publication de production ; et donner aux versions nocturnes leur propre espace de noms de registre et une identité de publication dédiée hors production, afin qu’un jeton nightly volé ne puisse pas du tout publier une version stable.

L’écart de huit jours entre le correctif et la publication malveillante est le détail qui rend ce dossier digne d’être conservé. Chaque étape de la chaîne relevait d’une technique connue ; Khan avait publié l’outil d’empoisonnement de cache, et les recherches PromptPwnd d’Aikido Security de décembre 2025 documentaient le même schéma de mauvaise configuration du triage. Ce qui les a assemblés pour en faire un incident de chaîne d’approvisionnement, c’est un flux de travail qui permettait au texte d’un inconnu d’atteindre un shell, un cache partagé avec des tâches qui détenaient des clés de publication, et une rotation de jetons qui a omis un identifiant. Chacun des trois correctifs aurait suffi à briser la chaîne à lui seul ; le troisième est arrivé avec huit jours de retard.