Per decenni, il test statico di sicurezza delle applicazioni (SAST) è stato uno dei metodi più efficaci per scalare la revisione del codice.
Ma quando abbiamo sviluppato Codex Security, abbiamo fatto una scelta progettuale precisa: non siamo partiti importando un report di analisi statica e chiedendo all’agente di fare triage. Abbiamo progettato il sistema perché parta dal repository stesso, dalla sua architettura, dai confini di fiducia e dal comportamento previsto, e validi ciò che trova prima di coinvolgere una persona.
Il motivo è semplice: le vulnerabilità più difficili di solito non riguardano il flusso dei dati. Si verificano quando il codice sembra applicare un controllo di sicurezza, ma quel controllo non garantisce davvero la proprietà su cui il sistema fa affidamento. In altre parole, la sfida non è solo tracciare come i dati si muovono in un programma, ma capire se le difese nel codice funzionano davvero.
Il SAST viene spesso descritto come una pipeline lineare: identificare una sorgente di input non attendibile, tracciare i dati nel programma e segnalare i casi in cui raggiungono un sink sensibile senza sanificazione. È un modello elegante e copre molti bug reali.
In pratica, il SAST deve introdurre approssimazioni per restare gestibile su larga scala, soprattutto in codebase reali con indirezioni, dispatch dinamico, callback, reflection e flussi di controllo complessi legati ai framework. Queste approssimazioni non sono una critica al SAST: riflettono i limiti del ragionare sul codice senza eseguirlo.
Questo, da solo, non spiega perché Codex Security non parte da un report SAST.
Il punto cruciale è cosa accade dopo aver tracciato correttamente una sorgente fino a un sink.
Anche quando l’analisi statica traccia correttamente l’input attraverso più funzioni e livelli, deve comunque rispondere alla domanda che determina davvero se esiste una vulnerabilità:
Considera un caso tipico: il codice chiama qualcosa come sanitize_html() prima di rendere contenuti non attendibili. Un analizzatore statico può verificare che la sanificazione sia stata eseguita. Di solito però non può stabilire se quel tipo di sanificazione sia davvero adeguata al contesto specifico di rendering, al motore di template, al comportamento di codifica e alle trasformazioni successive.
È qui che la situazione si complica. Il problema non è solo se i dati raggiungono un sink. Si tratta di stabilire se i controlli nel codice effettivamente condizionino il valore nel modo in cui il sistema presume che lo facciano.
In altre parole, c’è una grande differenza tra “il codice chiama una sanificazione” e “il sistema è sicuro.”
Ecco uno schema che si presenta continuamente nei sistemi reali.
Un’applicazione web riceve un payload JSON, estrae redirect_url, lo valida rispetto a una regex di allowlist, lo decodifica dall’URL e passa il risultato a un gestore di reindirizzamento.
Un classico report source-to-sink può descrivere il flusso:
input non affidabile → controllo regex → decodifica URL → reindirizzamento
Ma la vera domanda non è se il controllo esiste. È se quel controllo continua a vincolare il valore dopo le trasformazioni successive.
Se la regex viene eseguita prima della decodifica, limita davvero l’ URL decodificato nel modo in cui lo interpreta il gestore di reindirizzamento?
Rispondere a questa domanda richiede di ragionare sull’intera catena di trasformazione: cosa consente la regex, come si comportano decodifica e normalizzazione, come il parsing degli URL gestisce i casi limite e come la logica di reindirizzamento risolve schemi e autorità.
Molte delle vulnerabilità rilevanti nella pratica si presentano così: errori nell’ordine delle operazioni, normalizzazione parziale, ambiguità di parsing e discrepanze tra convalida e interpretazione. Il flusso dei dati è visibile. Il punto debole sta nel modo in cui i vincoli si propagano, o non riescono a propagarsi, lungo la catena di trasformazione.
Non si tratta solo di un modello teorico. In CVE-2024-29041(si apre in una nuova finestra), Express è stato interessato da un problema di open redirect in cui URL malformati potevano aggirare le implementazioni comuni di allowlist a causa del modo in cui le destinazioni di redirect venivano codificate e poi interpretate. Il flusso di dati era semplice. La domanda più complessa, e quella decisiva per stabilire se il bug esistesse, era se la validazione restasse efficace dopo la catena di trasformazioni.
Codex Security si basa su un obiettivo semplice: ridurre il triage portando alla luce problemi supportati da evidenze più solide. Nel prodotto, questo significa usare il contesto specifico del repository (incluso un modello di minaccia) e convalidare i problemi ad alto segnale in un ambiente isolato prima di segnalarli.
Quando Codex Security incontra un punto che sembra una “convalida” o una “sanificazione”, non lo tratta come una semplice spunta. Cerca di capire che cosa il codice sta tentando di fornire, e poi prova a metterla in discussione.
Nella pratica, si presenta come una combinazione di:
- Analizzare il percorso di codice rilevante con il contesto completo del repository, come farebbe un ricercatore di sicurezza, e individuare discrepanze tra intento e implementazione. Questo include anche i commenti, ma il modello non li prende necessariamente per buoni: aggiungere //Halvar says: this is not a bug sopra il codice non lo inganna, se il bug esiste davvero.
- Ridurre il problema alla porzione testabile più piccola (ad esempio la pipeline di trasformazione attorno a un singolo input), così puoi analizzarlo senza che il resto del sistema interferisca. In questo senso, Codex Security estrae piccole porzioni di codice e scrive micro-fuzzer per testarle.
- Ragionare sui vincoli lungo le trasformazioni, invece di trattare ogni controllo in modo indipendente. Dove opportuno, questo può includere la formalizzazione come problema di soddisfacimento dei vincoli. In altre parole, diamo al modello accesso a un ambiente Python con z3-solver, che sa usare quando serve, proprio come farebbe una persona per risolvere un problema complesso di vincoli sugli input. Questo è particolarmente utile per individuare overflow di interi o bug simili su architetture non standard.
- Eseguire le ipotesi in un ambiente di convalida isolato (sandbox), quando possibile, per distinguere “questo potrebbe essere un problema” da “questo è un problema”. Non c’è prova migliore di una PoC completa end-to-end con il codice compilato in modalità debug.
Questo è il cambiamento chiave: invece di fermarsi a “esiste un controllo”, il sistema punta a stabilire se “l’invariante è soddisfatta (o no)” e a fornire le prove. E il modello sceglie lo strumento migliore per farlo.
Una reazione ragionevole è: perché non fare entrambe le cose? Inizia con un report SAST, poi usa l'agente per ragionare più a fondo.
Ci sono casi in cui i risultati precomputati sono utili, soprattutto per classi di bug note e ristrette. Ma per un agente progettato per individuare e convalidare vulnerabilità in contesto, partire da un report SAST introduce tre modalità di errore prevedibili.
Innanzitutto, può favorire una restrizione prematura. Un elenco di risultati è una mappa delle aree già analizzate da uno strumento. Se lo usi come punto di partenza, il sistema può finire per concentrarsi troppo sulle stesse aree, usare sempre le stesse astrazioni e ignorare classi di problemi che non rientrano nella visione dello strumento.
In secondo luogo, può introdurre giudizi impliciti difficili da rimuovere. Molti risultati SAST incorporano presupposti su sanificazione, convalida o confini di fiducia. Se questi presupposti sono errati, o anche solo incompleti, inserirli nel ciclo di ragionamento può spingere l’agente a passare da “indagare” a “confermare o scartare”, che non è ciò che vogliamo.
In terzo luogo, può rendere più difficile valutare il sistema di ragionamento. Se la pipeline parte dall’output SAST, diventa difficile distinguere ciò che l’agente ha scoperto autonomamente da ciò che ha ereditato da altri strumenti. Questa distinzione è importante per misurare con precisione le capacità del sistema e permetterne il miglioramento nel tempo.
Per questo abbiamo creato Codex Security partendo da dove inizia la ricerca sulla sicurezza: il codice e l’intento del sistema, usando la convalida per aumentare il livello di affidabilità prima di coinvolgere una persona.
Possono essere molto efficaci nel loro ambito: far rispettare standard di codifica sicura, intercettare problemi diretti da source a sink e rilevare su larga scala pattern noti, con compromessi prevedibili. Possono essere una componente importante di una difesa a più livelli.
Questo articolo è più circoscritto: spiega perché un agente progettato per ragionare sul comportamento e convalidare i risultati non dovrebbe iniziare il proprio lavoro da un elenco statico di risultati.
Vale anche la pena evidenziare un limite del ragionamento puramente da source a sink: non tutte le vulnerabilità sono problemi di flusso di dati. Molti guasti reali sono problemi di stato e di invarianti: aggiramenti del flusso di lavoro, lacune nelle autorizzazioni e bug in cui il sistema si trova nello stato sbagliato. Per questi tipi di bug, un valore contaminato non raggiunge un unico “sink pericoloso”. Il rischio sta nelle ipotesi che il programma considera sempre vere.
Ci aspettiamo che l’ecosistema degli strumenti di sicurezza continui a migliorare: analisi statica, fuzzing, protezioni runtime e flussi di lavoro basati su agenti avranno tutti un ruolo.
Vogliamo che Codex Security eccella nella parte più onerosa per i team di sicurezza: trasformare “questo sembra sospetto” in “questo è reale, ecco come fallisce ed ecco una correzione coerente con l’intento del sistema”.
Per saperne di più su come Codex Security analizza i repository, convalida i risultati e propone correzioni, consulta la nostra documentazione(si apre in una nuova finestra).


