Dix mille étoiles et un commit

Le 14 juillet, xAI a publié le code source de Grok Build sous licence Apache 2.0, et ce matin le dépôt dépassait les dix mille étoiles. La publication faisait suite à un test au niveau du réseau qui avait constaté que la version 0.2.93 de l'outil téléversait un dépôt suivi, ainsi que l'intégralité de son historique git sous forme de bundle, vers un stockage cloud contrôlé par xAI, y compris un dépôt que l'assistant avait reçu l'instruction de ne pas lire. Nous avons traité ce constat le 14 juillet, et la consigne était alors de renouveler tous les secrets que l'outil avait pu voir. Cette consigne tient toujours.

Publier le code ressemble à la réponse la plus forte possible à un constat de ce type. C'est le geste que fait une organisation d'ingénierie quand elle estime que les traces la disculpent, et c'est ainsi qu'il est lu. La question utile n'est donc pas de savoir si xAI a eu raison de publier. Elle est plus étroite et plus pratique. Si vous alliez maintenant sur ce dépôt pour vous faire une idée de ce que l'outil a fait sur vos machines, que pourriez-vous réellement établir ?

Très peu de choses, en réalité, et les raisons sont structurelles plutôt que malveillantes. Le dépôt porte un seul commit, signé par un bot le 16 juillet à 05:46 UTC, avec pour message qu'il publie le harnais et le TUI en open source. Un seul commit, cela veut dire aucun historique. Pas d'avant, pas de diff, pas de suite de modifications, rien à comparer. Dix mille étoiles sur un dépôt sans historique, ce sont dix mille personnes qui approuvent un geste.

Ce que le dépôt ne peut pas vous dire

Commencez par la seule question que le constat a réellement soulevée. Où le code est-il allé ? Les téléversements partaient vers un bucket Google Cloud Storage nommé dans le binaire distribué. Dans le code source publié, ce nom n'apparaît pas. Il est lu depuis une variable d'environnement définie à la compilation, résolue au moment de la construction du binaire. Celui qui compile la publication fournit la destination, et la source ne dit rien de ce vers quoi le build distribué pointait. Le code publié ne peut pas répondre à la question, dans un sens comme dans l'autre.

Regardez ensuite la fonction qui effectuait le téléversement. Elle est toujours là, dans le crate du shell, et elle ne fait plus rien. Elle prend ses paramètres, les marque comme inutilisés, attend sur le canal, jette le résultat et renvoie un échec indiquant que l'envoi de l'état de session est indisponible. Le corps a été retiré tandis que les alentours sont restés. Les points d'appel sont intacts, le manifeste liste toujours les dépendances, les utilitaires de stockage sont toujours compilés dans le binaire. Ce que vous pouvez lire, c'est le squelette du mécanisme avec le mécanisme en moins.

C'est aussi la deuxième fois que le correctif est appliqué à un endroit que vous ne pouvez pas inspecter. Les téléversements d'origine ont été arrêtés le 13 juillet par un drapeau côté serveur, pas par une version cliente, ce qui était le point le plus tranchant de la première affaire. Le binaire que votre équipe avait relu et épinglé n'a jamais été ce qui décidait de ce qui quittait la machine. Aujourd'hui le code est public, la destination reste une entrée définie à la compilation et l'interrupteur reste sur le serveur. La surface auditable et la surface qui décide n'ont pas été rapprochées d'un pouce.

Quatre fichiers ont déménagé, un n'est pas arrivé

Il reste un détail, et il faut l'énoncer avec soin car il se surinterprète facilement. Les chaînes de caractères contenues dans le binaire 0.2.93 distribué nomment un crate appelé xai-data-collector, contenant cinq fichiers source. Le client de stockage, la couche de stockage cloud, la file d'attente, un observateur de circuit breaker et un traceur d'accès aux fichiers. Le dépôt publié ne contient aucun crate portant ce nom. Le même ensemble de fichiers vit dans un crate appelé xai-file-utils, et quatre des cinq correspondent. Le cinquième, le traceur d'accès aux fichiers, n'est pas du tout dans le code source publié.

Ce que cela établit et ce que cela n'établit pas, c'est tout l'enjeu. Cela n'établit pas d'intention. Renommer un crate en passant d'un nom qui décrit la collecte de données à un nom qui décrit la manipulation de fichiers est exactement ce que fait une équipe pendant un nettoyage de publication, et retirer un module qui n'est plus branché à rien relève de l'entretien courant. Il n'y a ici aucune preuve de dissimulation et nous n'en alléguons aucune. Il vaut aussi la peine de noter l'asymétrie de la matière elle-même. Le côté binaire repose sur l'extraction de chaînes par un tiers, tandis que le côté source peut être vérifié par quiconque clone le dépôt.

Ce que cela établit, c'est que la publication ne peut pas tenir lieu de trace. Le composant dont le nom décrit le plus directement le suivi des accès aux fichiers d'un utilisateur est absent, ses frères et soeurs sont présents sous un autre nom, et il n'existe aucun historique de commits dans lequel examiner l'un ou l'autre de ces faits. Les explications innocentes et les explications coupables produisent un dépôt identique. C'est là le problème quand on traite une publication comme une preuve. Cela ne fonctionne que si l'artefact permet de distinguer les deux, et celui-ci ne le permet pas.

L'affirmation qui n'a jamais été faite

Rendons justice au chercheur, car la rigueur de ce côté-ci de l'affaire est la raison pour laquelle elle tient. L'analyse au niveau du réseau a été menée avec un proxy d'interception face à un binaire épinglé, sur un dépôt jetable garni de fichiers canari, et elle est reproductible. Il existe un harnais public, et n'importe qui peut l'exécuter. La mesure était nette. Un dépôt de douze gigaoctets a produit 5,10 gibioctets de trafic de téléversement contre 192 kilo-octets de conversation réelle avec le modèle, un rapport proche de vingt-huit mille pour un. Le fichier canari que l'assistant avait reçu l'instruction de ne pas lire est ressorti intact du bundle capturé.

Et puis le chercheur a écrit noir sur blanc ce que le test ne prouvait pas. Que téléverser n'est pas entraîner, puisque seule la transmission a été mesurée. Qu'une capture à trois gigaoctets n'avait pas été conservée. Qu'une conclusion antérieure était fausse, parce qu'une lecture réseau limitée au périmètre du processus avait manqué des téléversements partant directement vers des adresses Google, et que cette conclusion a été retirée. Un constat qui publie ses propres limites et sa propre rétractation est un constat exploitable.

Mettez cela en regard des réponses. xAI a déclaré que la confidentialité lui tient profondément à coeur, qu'elle respecte le choix de ses clients, et que pour les équipes en rétention de données nulle aucune trace ni donnée de code n'est jamais conservée. Elon Musk a dit que toutes les données utilisateur téléversées jusqu'ici seront complètement et intégralement supprimées, et qu'il n'en restera absolument rien. Ces affirmations sont peut-être vraies. Aucune d'entre elles n'est vérifiable, et la suppression ne peut pas être contrôlée depuis l'extérieur. Le schéma est constant sur tout cet épisode. Les affirmations testables venaient de la personne qui avait le proxy, et celles qui ne le sont pas venaient du fournisseur.

Trois questions avant de déclarer l'affaire réglée

La leçon généralisable n'a rien à voir avec xAI, et elle resservira d'ici la fin du mois. Ouvrir le code est devenu une réponse standard à un constat de sécurité, et c'en est une bonne quand l'artefact porte des preuves. Ce n'est pas automatique. Alors quand un fournisseur répond à un constat concernant vos données en publiant du code, posez-lui trois questions avant de fermer le ticket.

Puis-je voir l'historique ? Un commit initial écrasé est une photographie, pas un enregistrement. Si le dépôt commence le jour de la publication, il ne peut pas montrer ce qui a changé, et ce qui est examiné est précisément un changement. L'artefact publié correspond-il au binaire distribué ? Si le moindre comportement qui compte est fixé à la compilation ou retenu sur un serveur, la source est la description d'une possibilité, pas de ce qui a tourné sur vos machines. Le code contient-il le composant que le constat a nommé ? Si la chose précise qui a été trouvée n'est pas dans le dépôt, son absence n'est pas une réponse, quelle qu'en soit la cause.

Rien de tout cela ne plaide pour garder le code fermé, et rien de tout cela n'est une accusation. Cela plaide pour une distinction que votre processus d'achat ne fait probablement pas encore. Une publication est un acte de divulgation, et un audit est un processus qui produit une conclusion. Ce ne sont pas des substituts, ce ne sont même pas des cousins proches, et un fournisseur qui a fait le premier n'a pas fait le second. Les équipes qui traitent un lien vers un dépôt comme un dossier clos commettent une erreur de catégorie, et c'est le genre d'erreur qui ne se voit qu'après coup, une fois le constat suivant tombé.