Le serveur Kanban de Cline : un détournement WebSocket cross-origin menant à l'exécution de code
Une divulgation de mai 2026 montre que le serveur WebSocket local du Kanban de Cline ne vérifie aucune origine — n'importe quel site visité peut lire l'espace de travail et injecter des commandes dans un agent actif.
De quoi s’agit-il ?
Le 8 mai 2026, un avis de sécurité GitHub a divulgué une faille de détournement de WebSocket cross-origin dans le tableau Kanban livré avec l’agent de code Cline. Le paquet npm kanban, lancé par la CLI cline, démarre un serveur WebSocket local sur 127.0.0.1:3484 et accepte toute connexion entrante sans vérifier l’en-tête Origin ni exiger de jeton. Le NVD a publié l’entrée le 1er juin 2026 et l’a classée critique (CVSS 9.6) ; l’enrichissement ADP de la CISA, le 3 juin, l’a marquée comme exploitable via un PoC, avec un impact technique total. À la date de publication, aucun correctif n’était disponible : toutes les versions jusqu’à Cline 2.13.0 incluse sont concernées.
La technique est ancienne, la cible est neuve. Le détournement de WebSocket cross-site (CSWSH) figure depuis des années dans le corpus de l’OWASP. Ce qui a changé, c’est ce qui se trouve derrière le socket : un agent de code autonome doté d’un terminal actif et d’un accès complet au système de fichiers, joignable depuis n’importe quel onglet de navigateur.
Comment ça marche
Les navigateurs appliquent la politique de même origine à fetch et XMLHttpRequest, mais les requêtes d’upgrade WebSocket ne sont pas soumises au CORS. Une page située sur https://un-site.example peut ouvrir ws://127.0.0.1:3484/... et le navigateur mènera la poignée de main à son terme. La seule protection possible est que le serveur valide l’en-tête Origin lors de l’upgrade — ce que le serveur Kanban de Cline ne faisait pas.
Trois points de terminaison étaient exposés sans validation d’origine ni authentification. Un socket d’état du runtime diffuse un instantané complet de l’espace de travail dès qu’un client se connecte : chemins du système de fichiers, branche git, titres et descriptions de tâches, et messages en direct de l’agent. Un socket d’E/S de terminal écrit des octets bruts directement dans le pseudo-terminal de l’agent. Un socket de contrôle peut arrêter les tâches actives. Conceptuellement, le gestionnaire d’upgrade vulnérable ressemblait à ceci :
server.on("upgrade", (request, socket, head) => {
// Le chemin est vérifié, mais l'en-tête Origin ne l'est jamais,
// donc une page cross-origin mène la poignée de main à terme.
runtimeStateHub.handleUpgrade(request, socket, head, { ... });
});
La chaîne est ensuite la suivante : se connecter depuis une page malveillante et lire l’instantané diffusé ; guetter un événement task_sessions_updated signalant un agent en cours d’exécution ; puis envoyer du texte au socket de terminal en le terminant par un retour chariot. L’agent traite le texte injecté comme une entrée utilisateur, et le \r le valide exactement comme si le développeur avait appuyé sur Entrée — transformant l’injection de texte en exécution de la commande [REDACTED shell command]. Aucune interaction n’est requise, hormis avoir le Kanban ouvert et visiter la mauvaise page.
Exemple de prompt
L’extrait ci-dessous montre pourquoi le détournement fonctionne : le serveur local termine la poignée de main WebSocket sans vérifier l’en-tête Origin, si bien qu’une page ouverte dans n’importe quel onglet peut l’atteindre. La charge est caviardée — ceci est destiné aux défenseurs.
# Cross-Site WebSocket Hijack on a local agent server (illustrative, defensive)
# A page the developer visits opens a socket the browser never blocks:
ws = connect("ws://127.0.0.1:3484/[terminal-endpoint]")
# Injected text + a carriage return would submit as agent input:
ws.send("[hidden instruction]\r") # [payload redacted]
# Root cause: the server accepts the upgrade without checking Origin.
# Defense: validate the Origin header on every WebSocket upgrade,
# require a per-session token, and bind to a random loopback port.
Pourquoi c’est important
Voici une illustration concrète d’un schéma qui se répète à mesure que les agents s’installent sur le poste de travail : un service local puissant, lié à la boucle locale, considéré comme de confiance parce que « seul localhost peut l’atteindre ». La boucle locale n’est pas une frontière de sécurité face à un navigateur que l’utilisateur exécute déjà. N’importe quelle régie publicitaire, dépendance compromise ou lien malveillant peut héberger le JavaScript. La seule atteinte à la confidentialité — chemins du dépôt, noms de branches non publiées, prompts de tâches et conversations de l’agent — constitue déjà une fuite sérieuse pour les développeurs travaillant sur du code privé, et l’étape d’injection dans le terminal l’élève à une exécution de code à distance, sur macOS, Linux et Windows.
Défenses
Les recommandations de l’avis se généralisent à tout serveur d’agent local :
- Validez l’en-tête
Originà chaque upgrade WebSocket. Rejetez tout ce qui n’est pas l’origine de l’interface de l’agent lui-même. C’est le correctif qui referme la classe entière. - Exigez un jeton de session imprévisible. Générez un secret aléatoire au démarrage du serveur, transmettez-le à l’interface légitime au chargement de la page, et réclamez-le comme paramètre de connexion. Les pages cross-origin ne peuvent pas le deviner.
- Authentifiez les sockets sensibles indépendamment. Les canaux d’E/S de terminal et de contrôle des tâches doivent vérifier l’appelant, et non présumer que localhost égale confiance.
- Liez le service à un port haut aléatoire et/ou à un socket de domaine unix quand c’est possible, et ne diffusez jamais de secrets dans un instantané non sollicité à la connexion.
- En tant qu’utilisateur, tant que vous n’êtes pas sur une version corrigée : évitez d’exécuter le tableau Kanban en naviguant sur des sites non fiables, et traitez toute interface d’agent en localhost comme accessible depuis Internet.
Statut
| Élément | Valeur |
|---|---|
| Référence | CVE-2026-44211 / GHSA-5c57-rqjx-35g2 |
| Classe | CSWSH (CWE-1385) + absence d’authentification (CWE-306) |
| Concerné | Cline <= 2.13.0 (paquet npm kanban) |
| CVSS 3.1 | 9.6 critique (AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H) |
| Divulgation | Avis GitHub 2026-05-08 ; NVD 2026-06-01 |
| Correctif | Aucun disponible à la date de divulgation |
| Exploitation | PoC public (SSVC CISA : impact technique total) |
Dates clés : 5 mai 2026 — réservation du CVE. 8 mai 2026 — publication de l’avis GitHub. 1er juin 2026 — entrée NVD. 3 juin 2026 — enrichissement ADP de la CISA.