Clonare un repository non esegue automaticamente il suo codice. Questa distinzione va fatta subito, perché è facile trasformare una notizia di sicurezza in una frase ad effetto. Il problema comincia dopo: quando apriamo quel progetto in un ambiente di sviluppo, avviamo un assistente AI, accettiamo una configurazione di workspace o installiamo le dipendenze. A quel punto alcuni file, che credevamo soltanto di dover leggere, possono diventare istruzioni operative.
La differenza è enorme. Un repository non è più soltanto un insieme di sorgenti da compilare: può contenere regole per l’editor, hook per gli agenti, task, workflow e script di installazione. Ognuno di questi meccanismi ha un momento preciso in cui passa da dato a comando. Ed è proprio quel passaggio che chi sviluppa dovrebbe imparare a riconoscere.
Il 26 ottobre 2026 JFrog Security Research ha documentato una campagna collegata ai pacchetti npm testmetest: 42 repository GitHub di terze parti, distribuiti fra 11 proprietari legittimi, erano stati preparati con meccanismi di esecuzione malevoli. Non è un esperimento teorico su un repository inventato per una demo.
Quarantadue repository armati non significano quarantadue infezioni
La precisione dei termini qui conta. I ricercatori hanno trovato 42 repository compromessi e predisposti per l’esecuzione. Non hanno dimostrato che ogni repository abbia infettato una macchina, che ogni visitatore abbia eseguito il payload o che ogni passaggio della catena sia arrivato fino alla pubblicazione di un nuovo pacchetto malevolo. Sono fatti diversi, e confonderli rende meno utile anche una notizia vera.
La ricerca descrive una campagna che combina pacchetti npm, credenziali GitHub e npm sottratte, repository di sviluppatori legittimi e componenti capaci di tentare nuove pubblicazioni attraverso la CI. JFrog ha ricostruito anche collegamenti con altri artefatti di attacco, ma ha esplicitamente evitato di attribuire con certezza l’operazione agli autori di precedenti campagne chiamate Miasma. È una cautela che vale la pena mantenere.
Per chi lavora con codice di clienti, plugin WordPress, applicazioni web o strumenti agentici, il dato interessante non è il numero da mettere nel titolo. È che un repository autentico, sotto un account autentico, può contenere un comportamento che il suo proprietario non ha autorizzato. Il nome del maintainer non sostituisce il controllo di ciò che viene eseguito.
Il punto in cui un file di progetto diventa un comando
Una configurazione per l’IDE ha spesso un aspetto rassicurante: JSON, nomi di task, opzioni dell’assistente, qualche riferimento a un file locale. Ma non tutti quei file sono semplice documentazione. Alcuni descrivono comandi da avviare all’inizio di una sessione, durante un task o in risposta a un’azione dello sviluppatore.
Nella campagna analizzata da JFrog compaiono configurazioni rivolte a Claude Code, Gemini CLI, Cursor e Visual Studio Code. I percorsi coinvolgono, fra gli altri, .claude/settings.json, .gemini/settings.json, .cursor/mcp.json e .vscode/tasks.json. Il loro comportamento non è identico: ogni prodotto ha regole proprie, controlli di fiducia e autorizzazioni. Non basta aprire qualunque cartella perché tutti questi file vengano eseguiti. Ma se lo strumento consente l’hook e il progetto è considerato attendibile, quella configurazione può avviare un processo.
Questo cambia la domanda che facciamo quando proviamo un progetto. Non soltanto “il codice sorgente è sicuro?”, ma “quali processi autorizzo prima ancora di iniziare a leggere il codice?”. Un agente che ha accesso al terminale e ai file locali non cancella questa responsabilità. La rende più importante, perché può attivare automazioni con meno passaggi visibili all’utente.
Il confine di fiducia non passa dal logo di GitHub né dal nome dell’assistente AI. Passa dal primo comando che autorizzi a eseguire nel tuo ambiente.
Il file esiste, ma la ricerca non lo vede
La seconda parte della vicenda è ancora più istruttiva. I ricercatori hanno individuato file JavaScript da circa 12,9 a 13,7 MB usati come contenitori di un loader offuscato. Dimensioni sufficienti, nel caso documentato, a escluderli dai normali risultati di GitHub Code Search e a rendere inefficaci alcuni percorsi abituali della Contents API.
Un controllo basato su “cerco questa stringa e non trovo niente” poteva quindi restituire un risultato tranquillizzante mentre il file era presente nel repository. JFrog ha dovuto esaminare gli oggetti Git tramite gli alberi e i blob per recuperare ciò che la ricerca non mostrava. Le varianti esterne erano diverse, ma dopo la decodifica riconducevano allo stesso componente malevolo.
Qui c’è una lezione che supera GitHub: un indice di ricerca non è un inventario completo. Lo sappiamo già quando progettiamo database, log e sistemi di osservabilità. Eppure, davanti a un repository, tendiamo a trattare l’assenza di un risultato come prova dell’assenza del problema. Non lo è. Un controllo di sicurezza deve sapere quali file esistono davvero, quali sono cambiati e quali possono essere richiamati da una configurazione eseguibile.
Non c’è solo l’IDE: anche installare dipendenze può avviare codice
La campagna sfruttava anche percorsi meno appariscenti dell’ecosistema Node.js. In alcune versioni, l’esecuzione passava attraverso binding.gyp, senza un normale script install immediatamente riconoscibile in package.json. Altre varianti impiegavano addon nativi precompilati e un file dal nome innocuo, .napi_cache, che nascondeva il contenuto eseguibile.
JFrog descrive controlli ambientali capaci di far rimanere inattive alcune varianti in contesti di analisi, CI e sandbox, mentre l’esecuzione poteva procedere su una macchina di sviluppo ordinaria. Non serve riprodurre qui i dettagli del loader: ciò che interessa è il modello di rischio. Il comportamento effettivo di una dipendenza non coincide sempre con gli script più evidenti del suo manifest. E una scansione fatta in un ambiente può non osservare lo stesso comportamento che comparirebbe su un altro.
È anche il motivo per cui bloccare la versione di una dipendenza non equivale, da solo, a garantire il comportamento del codice. Un lockfile aiuta la riproducibilità; non sostituisce la provenienza, la verifica degli artefatti e l’isolamento dell’esecuzione.
Il vero perimetro è fatto di permessi, credenziali e automatismi
Se un processo avviato dal progetto può leggere le credenziali dello sviluppatore, utilizzare token di pubblicazione o modificare workflow CI, il problema non resta confinato alla cartella scaricata. Può propagarsi attraverso identità e autorizzazioni che l’ambiente mette a disposizione. La ricerca JFrog mostra proprio il tentativo di trasformare l’accesso a repository e registri in nuove pubblicazioni e modifiche malevole.
Questo non significa che gli agenti AI siano la causa dell’attacco. I meccanismi di installazione e di supply chain esistevano prima. Gli agenti aggiungono nuovi punti di attivazione, nuove configurazioni eseguibili e spesso una maggiore facilità nel delegare comandi. Chiamare tutto “prompt injection” sarebbe sbagliato: qui si parla soprattutto di hook, processi, file e permessi, non di una frase malevola che convince un modello.
Lo stesso ragionamento vale per la sandbox. Come ho già approfondito parlando di confini di rete e isolamento degli agenti, dichiarare un ambiente isolato non dimostra automaticamente che tutti i suoi percorsi di uscita siano sotto controllo. Bisogna verificare cosa il processo vede, cosa può eseguire e a quali servizi può autenticarsi.
Cosa controllare prima di fidarsi di un progetto
Non serve trasformare ogni checkout in un’indagine forense. Serve distinguere l’esplorazione del codice dalla sua esecuzione e applicare qualche controllo ripetibile, soprattutto quando il repository è nuovo, poco conosciuto o arriva da una dipendenza di terze parti.
- Aprire prima senza eseguire: esaminare le configurazioni del workspace e degli agenti prima di approvare hook, task e comandi automatici. Le protezioni variano da strumento a strumento.
- Controllare i punti di avvio: non fermarsi a
package.json; verificare script di installazione,binding.gyp, addon nativi, task IDE e workflow CI inattesi. - Inventariare davvero i file: usare l’albero Git e la storia delle modifiche quando la ricerca testuale non mostra file grandi o sospetti. La Code Search è un aiuto, non una prova di completezza.
- Separare identità e ambienti: provare progetti non fidati in un ambiente ristretto, senza token personali o credenziali di produzione montate per comodità.
- Limitare i privilegi: assegnare a agenti, pipeline e token solo le autorizzazioni necessarie, soprattutto quelle di pubblicazione e modifica dei repository.
- Trattare i sospetti come incidenti: se un hook o un pacchetto è stato realmente eseguito, isolare e verificare il sistema prima di considerare sufficiente la sola rimozione del file.
Queste misure non garantiscono che un attacco non possa riuscire. Riducono però la quantità di fiducia concessa per impostazione predefinita e rendono più facile capire dove un evento sospetto avrebbe potuto agire. È una differenza pratica, non uno slogan sulla sicurezza.
La fiducia non si eredita dal repository
La vicenda testmetest è utile proprio perché non richiede di immaginare un’AI ribelle o un exploit fantascientifico. Bastano strumenti legittimi, un progetto compromesso, automatismi configurabili e un controllo incompleto dei file. La parte sofisticata dell’attacco è stata sfruttare i punti ciechi tra questi componenti.
Quando integriamo un nuovo agente o apriamo il repository di qualcuno, la domanda più importante non è se lo strumento sia potente. È se abbiamo definito in modo esplicito quale codice può essere eseguito, con quali credenziali e dopo quale verifica. Se non sappiamo rispondere, non abbiamo un sistema più autonomo: abbiamo soltanto spostato la decisione in un posto dove la vediamo meno.
Domande frequenti
Basta clonare un repository GitHub per eseguire il malware?
No. Il comando di clonazione non esegue automaticamente gli script del repository. Il rischio nasce quando IDE, agenti, task o installazioni di dipendenze attivano codice presente nel progetto, secondo configurazioni e permessi effettivi.
Perché GitHub Code Search può non mostrare un file malevolo?
La ricerca indicizza un sottoinsieme dei contenuti secondo limiti e criteri tecnici. Nel caso testmetest alcuni file JavaScript molto grandi non comparivano nei normali risultati; JFrog li ha individuati esaminando gli alberi e i blob Git.
Gli hook degli assistenti AI partono sempre all'apertura del progetto?
No. Dipende dal prodotto, dalla configurazione, dai controlli di fiducia e dalle autorizzazioni. Proprio per questo è necessario verificare gli hook presenti nel workspace prima di approvarne l’esecuzione.
Un lockfile o npm audit sono sufficienti per proteggere la supply chain?
No. Il lockfile aiuta a fissare le versioni e npm audit individua problemi noti nel proprio perimetro, ma non sostituiscono la verifica degli script, degli addon nativi, delle configurazioni IDE, dei permessi e della provenienza degli artefatti.
Qual è la prima precauzione con un repository sconosciuto?
Esplorarlo senza avviare automaticamente task o installazioni, controllare configurazioni e dipendenze e usare un ambiente isolato privo di credenziali di produzione. Se è già stato eseguito codice sospetto, trattare il sistema come potenzialmente compromesso.
