Diecimila stelle e un solo commit

Il 14 luglio xAI ha pubblicato il sorgente di Grok Build sotto licenza Apache 2.0, e questa mattina il repository aveva superato le diecimila stelle. La pubblicazione è arrivata dopo un test a livello di traffico di rete che aveva rilevato la versione 0.2.93 dello strumento mentre caricava un repository tracciato, e la sua intera cronologia git sotto forma di bundle, su uno storage cloud controllato da xAI, incluso un repository che all'assistente era stato detto di non leggere. Abbiamo trattato quel rilevamento il 14 luglio, e l'indicazione di allora era di ruotare ogni segreto che lo strumento avesse mai visto. Quell'indicazione resta valida.

Pubblicare il codice sembra la risposta più forte possibile a un rilevamento del genere. È la mossa che compie un'organizzazione di ingegneria quando crede che il registro la scagioni, ed è così che viene letta. Quindi la domanda utile non è se xAI abbia fatto bene a pubblicare. È più circoscritta e più pratica: se adesso andaste su quel repository per farvi un'idea di che cosa lo strumento abbia fatto sulle vostre macchine, che cosa potreste effettivamente stabilire?

Molto poco, a quanto pare, e le ragioni sono strutturali piuttosto che sinistre. Il repository porta un solo commit, firmato da un bot il 16 luglio alle 05:46 UTC, con il messaggio che pubblica in open source l'harness e la TUI. Un solo commit significa nessuna cronologia. Non c'è un prima, non c'è un diff, non c'è una sequenza di modifiche, non c'è niente da confrontare. Diecimila stelle su un repository senza cronologia sono diecimila persone che approvano un gesto.

Che cosa il repository non può dirvi

Partiamo dall'unica domanda che il rilevamento ha davvero sollevato: dove è finito il codice? I caricamenti sono andati verso un bucket di Google Cloud Storage il cui nome compare nel binario distribuito. Nel sorgente pubblicato quel nome non c'è. Viene letto da una variabile d'ambiente definita in fase di compilazione, risolta quando il binario viene costruito. Chi compila la release fornisce la destinazione, e il sorgente tace su dove fosse puntata la build distribuita. Il codice pubblicato non può rispondere alla domanda, in nessuna delle due direzioni.

Poi guardate la funzione che effettuava il caricamento. È ancora lì, nel crate della shell, e non fa più nulla. Prende i suoi parametri, li marca come inutilizzati, attende sul canale, butta via il risultato e restituisce un errore che segnala come il caricamento dello stato di sessione non sia disponibile. Il corpo è stato rimosso mentre il contorno è rimasto: i punti di chiamata sono intatti, il manifest elenca ancora le dipendenze, gli helper dello storage sono ancora compilati dentro. Quello che potete leggere è lo scheletro del meccanismo, con il meccanismo tolto.

È anche la seconda volta che la correzione viene applicata in un punto che non potete ispezionare. I caricamenti originari sono stati fermati il 13 luglio da un flag lato server, non da una release del client, ed era questa la parte più tagliente della prima storia: il binario che il vostro team aveva esaminato e bloccato a una versione non è mai stato la cosa che decideva che cosa uscisse dalla macchina. Ora il codice è pubblico, la destinazione è ancora un input definito in fase di build e l'interruttore è ancora sul server. La superficie che si può controllare e la superficie che decide non sono state avvicinate in alcun modo.

Quattro file sono passati, uno non è arrivato

C'è un ultimo dettaglio, e va enunciato con cura perché è facile leggerci dentro troppo. Le stringhe presenti nel binario 0.2.93 distribuito nominano un crate chiamato xai-data-collector, che contiene cinque file sorgente: il client di storage, il livello di cloud storage, la coda, un observer del circuit breaker e un file access tracker. Il repository pubblicato non contiene alcun crate con quel nome. Lo stesso insieme di file vive in un crate chiamato xai-file-utils, e quattro dei cinque corrispondono. Il quinto, il file access tracker, nel sorgente pubblicato non c'è affatto.

Che cosa questo stabilisce e che cosa non stabilisce è tutto il punto. Non stabilisce un'intenzione. Rinominare un crate da un nome che descrive la raccolta di dati a uno che descrive la gestione di file è esattamente ciò che fa un team durante il riordino di una release, e togliere un modulo che non è più collegato a nulla è ordinaria manutenzione. Qui non ci sono prove di occultamento e non ne stiamo affermando alcuno. Vale anche la pena notare l'asimmetria nelle prove stesse: il lato binario poggia sull'estrazione di stringhe fatta da terzi, mentre il lato sorgente può essere verificato da chiunque cloni il repository.

Quello che stabilisce è che la release non può funzionare come registro. Il componente il cui nome descrive nel modo più diretto il tracciamento degli accessi ai file di un utente è assente, i suoi fratelli sono presenti sotto un altro nome, e non esiste una cronologia dei commit in cui l'uno o l'altro fatto possa essere esaminato. Spiegazioni innocenti e spiegazioni colpevoli producono un repository identico. È questo il problema del trattare una pubblicazione come una prova: funziona solo se l'artefatto sa distinguere tra le due, e questo non lo sa fare.

L'affermazione che non è mai stata fatta

Va dato credito al ricercatore, perché il rigore su quel lato della storia è la ragione per cui regge. L'analisi a livello di traffico di rete è stata condotta con un proxy di intercettazione contro un binario bloccato a una versione, su un repository usa e getta popolato con file canary, ed è riproducibile: esiste un harness pubblico, e chiunque può eseguirlo. La misura era netta. Un repository da dodici gigabyte ha prodotto 5,10 gibibyte di traffico in caricamento contro 192 kilobyte di conversazione effettiva con il modello, un rapporto vicino a ventottomila a uno. Il file canary che all'assistente era stato detto di non leggere è riemerso intatto dal bundle catturato.

E poi il ricercatore ha messo per iscritto ciò che il test non ha dimostrato. Che caricare non è addestrare, perché è stata misurata solo la trasmissione. Che una cattura da tre gigabyte non è stata conservata. Che una conclusione precedente era sbagliata, perché una lettura di rete circoscritta al processo non aveva visto i caricamenti diretti verso indirizzi Google, e quella conclusione è stata ritirata. Un rilevamento che pubblica i propri limiti e la propria ritrattazione è un rilevamento che potete usare.

Mettete tutto questo a confronto con le risposte. xAI ha dichiarato di tenere profondamente alla privacy, di rispettare la scelta del cliente e che, per i team che usano la zero data retention, nessun dato di traccia o di codice viene mai conservato. Elon Musk ha detto che tutti i dati utente caricati fino a oggi saranno cancellati completamente e del tutto, e che non ne rimarrà assolutamente nulla. Può darsi benissimo che sia vero. Nessuna di queste affermazioni è verificabile, e la cancellazione non è controllabile dall'esterno. Lo schema lungo tutto l'episodio è coerente: le affermazioni che si possono testare sono venute dalla persona con il proxy, e quelle che non si possono testare sono venute dal fornitore.

Tre domande prima di dirlo risolto

La lezione generalizzabile non ha nulla a che fare con xAI, e servirà di nuovo entro il mese. Aprire il codice è diventata una risposta standard a un rilevamento di sicurezza, ed è una buona risposta quando l'artefatto porta con sé delle prove. Non lo fa automaticamente. Quindi, quando un fornitore risponde a un rilevamento sui vostri dati pubblicando codice, ponetegli tre domande prima di chiudere il ticket.

Posso vedere la cronologia? Un commit iniziale schiacciato è una fotografia, non un registro. Se il repository comincia il giorno della pubblicazione, non può mostrare che cosa è cambiato, e la cosa sotto esame è precisamente un cambiamento. L'artefatto pubblicato corrisponde al binario distribuito? Se un qualsiasi comportamento che conta viene fissato in fase di build o tenuto su un server, il sorgente è la descrizione di una possibilità, non di ciò che ha girato sulle vostre macchine. Il codice contiene il componente nominato dal rilevamento? Se la cosa specifica che è stata trovata non è nel repository, la sua assenza non è una risposta, qualunque ne sia la causa.

Niente di tutto questo è un argomento per tenere il codice chiuso, e niente di tutto questo è un'accusa. È un argomento a favore di una distinzione che il vostro processo di procurement probabilmente non traccia ancora: la pubblicazione è un atto di divulgazione, mentre un audit è un processo che produce una conclusione. Non sono sostituti, non sono nemmeno parenti stretti, e un fornitore che ha fatto la prima cosa non ha fatto la seconda. I team che trattano il link a un repository come un caso chiuso stanno commettendo un errore di categoria, ed è il tipo di errore che si vede solo a posteriori, dopo il rilevamento successivo.