Checklist di revisione del codice AI-generated: pattern anti-OWASP in PHP
Nella mia sandbox di audit su codice AI-generated tengo dall'ottobre 2025 un repository Git dedicato dove archivio gli output di ChatGPT, Copilot, Claude e Gemini su prompt tecnici PHP realistici - di solito task di implementazione CRUD, endpoint API, form handler, integrazione pagamenti, middleware di autenticazione. L'obiettivo non è dimostrare che l'AI scrive codice insicuro; è catalogare i pattern ricorrenti che emergono sistematicamente e costruire la checklist operativa che applico prima di far entrare qualsiasi codice AI-generated in una pull request verso un progetto reale. A fine marzo 2026 il campione è di 40 output analizzati con metriche comparabili, e la conclusione quantitativa è secca: nessuno dei 40 output è stato sicuro out-of-the-box - ognuno aveva almeno una vulnerabilità dimostrabile classificabile sotto almeno una voce della OWASP Top 10 per LLM Applications 2025. Non è una critica teorica: è un dato empirico che cambia il modo in cui un senior engineer responsabile dovrebbe impostare il processo di code review quando un pezzo di codice ha un LLM nella sua catena di origine.
Cosa distingue le vulnerabilità AI-generated dalle vulnerabilità scritte da umani?
Le vulnerabilità scritte da uno sviluppatore umano inesperto hanno una firma riconoscibile: errori di type coercion, dimenticanze su edge case, scorciatoie inconsapevoli come mysql_real_escape_string su input che passerà poi attraverso altri sistemi. Le vulnerabilità AI-generated hanno una firma diversa: il codice è sintatticamente impeccabile, i nomi delle variabili sono coerenti, la struttura è allineata con i pattern idiomatici del linguaggio, ma alla scansione ingegneristica attenta emergono assunzioni silenziose che non sono giustificate dal contesto di input. L'LLM produce codice che "sembra aver fatto la cosa giusta" perché ha pescato da un corpus di formazione ricco di esempi simili, ma non ha fatto ragionamento specifico sul threat model del caso reale - ed è lì che si annida il rischio.
La OWASP GenAI Security Project ha pubblicato nel novembre 2024 la versione 2025 della Top 10 per LLM Applications, confermando Prompt Injection (LLM01) come rischio numero uno ma allargando il perimetro a nuove categorie come Excessive Agency (LLM06) e Vector and Embedding Weaknesses (LLM08). Per chi fa code review su output AI il framework rilevante non è l'intera Top 10 (metà riguarda l'LLM come sistema, non il codice che produce); sono specificamente LLM02 Sensitive Information Disclosure, LLM03 Supply Chain Vulnerabilities e LLM05 Improper Output Handling - più una categoria che OWASP classifica trasversalmente e che io nel mio lavoro tratto come sesta classe indipendente: le dipendenze allucinate. Vediamole una per una.
Prima classe: SQL injection con parametri concatenati
Il pattern più ricorrente nel mio campione. Su 40 output PHP, 23 contenevano almeno un costrutto tipo $db->query("SELECT * FROM users WHERE email = '" . $email . "'") o varianti con sprintf e string interpolation. La probabilità di trovare questo pattern aumenta quando il prompt chiede "codice rapido" o "proof of concept" - l'LLM interpreta queste parole come autorizzazione implicita a saltare la parametric binding. Il fix è banale quando lo vedi (prepared statement via PDO o query builder Laravel), ma il pattern sopravvive quando il review è frettoloso.
La mia checklist su questa classe richiede tre verifiche specifiche: (1) grep nel diff per pattern " seguito da . o {, ovvero qualunque string interpolation nel corpo di query; (2) verifica che ogni chiamata a ->query(), ->exec(), ->prepare() abbia solo placeholder numerici o nominali e che i valori siano passati via execute() o bindValue(); (3) per Eloquent, grep di whereRaw, selectRaw, DB::raw - usi legittimi ma che richiedono revisione contestuale esplicita perché l'LLM li propone spesso come scorciatoia a where() strutturato.
Seconda classe: XSS e output encoding inconsistente
Diciotto dei 40 output toccavano template rendering o echo diretto di dati. Undici avevano almeno un punto dove un dato utente arrivava a output senza passare da htmlspecialchars o equivalente framework (Blade {{ }}, Twig {{ var|e }}). Il pattern subdolo è quando l'LLM mischia stili: una variabile passa da {{ $user->name }} (sicura), un'altra passa da {!! $user->bio !!} (raw unsafe) perché "così supporta HTML". Se la logica applicativa non impone sanitization a monte, il secondo costrutto è una stored XSS in attesa.
Checklist: (1) grep per {!! !!} in Blade, per |raw in Twig, per echo diretto senza htmlspecialchars in PHP puro; (2) verifica che ogni endpoint che accetta rich text da utente passi attraverso un sanitizer esplicito (HTMLPurifier, DOMPurify lato client non basta); (3) Content-Security-Policy nei response header come rete di sicurezza - se manca, il XSS diventa escalation diretta.
Terza classe: secret, chiavi API, credenziali committate
Ricorrenza su 40 output: 14 contenevano almeno una credenziale hardcoded nel codice esempio - password in plain text, chiavi API Stripe fake ma con formato realistico, token JWT con secret stringa letterale. L'LLM non "intende" committare secret reali, produce placeholder - ma se il prompt richiedeva codice "pronto per produzione" e lo sviluppatore copia senza sostituire, il placeholder diventa un commit con secret nel Git history e un report SAST che suona.
Checklist: (1) grep per pattern regex specifici (Stripe sk_live_, AWS AKIA, OAuth client_secret, JWT Bearer con payload hardcoded); (2) GitLeaks o TruffleHog nel pre-commit hook, non come verifica post-push; (3) verifica che ogni chiamata a servizi esterni legga il credential da $_ENV o da un secret manager, mai da hardcoded literal. OWASP LLM02 Sensitive Information Disclosure è il riferimento diretto: l'LLM può esporre dati sensibili direttamente nell'output generato se non esiste un filtro applicativo esplicito.
Quarta classe: errori uniformi che fanno leaking di informazioni
Pattern meno ovvio ma pericoloso: su 40 output, 19 gestivano gli errori con un singolo catch (\Throwable $e) { return $e->getMessage(); } che in produzione espone stack trace, query SQL eseguita, path filesystem, versione del framework. L'LLM riproduce il pattern perché è comune nei tutorial - dove è didattico - ma un endpoint API che risponde con SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '[email protected]' for key 'users_email_unique' sta rivelando all'attaccante che esiste una tabella users con un campo email unique e un utente admin. È information leak OWASP A05.
Checklist: (1) grep per getMessage(), getTrace(), getFile() nei blocchi catch che escono via response; (2) verifica che esista un exception handler centralizzato che restituisce message generici in produzione e dettaglia solo in development; (3) controllo che il framework logging (Monolog tipicamente) scriva su file con permessi stretti e non sia esposto via HTTP.
Se stai implementando un processo di code review strutturato per codice AI-generated e vuoi una metodologia applicata che tenga insieme produttività e sicurezza, nel mio hub dedicato alla sicurezza AI per aziende trovi raccolti gli articoli tecnici sui pattern offensivi e difensivi che uso nei progetti di audit.
Quinta classe: dipendenze allucinate (package hallucination)
Categoria che merita attenzione particolare perché è specifica del codice AI-generated e non ha equivalente stretto nel codice scritto da umani. Su 40 output, 6 suggerivano l'installazione di package Composer che non esistono nei registri ufficiali - nomi plausibili come vendor/something-helper, con import tipo use Vendor\SomethingHelper\Factory. Il rischio operativo è duplice: il primo è di ordine funzionale (lo sviluppatore che copia-incolla ottiene composer require che fallisce e perde tempo); il secondo è molto più serio e documentato nei paper accademici del 2024-2025 come slopsquatting: un attaccante registra sul registry un package con il nome hallucinated - perché gli LLM tendono a ripetere gli stessi nomi falsi - e iniettare codice malevolo nella supply chain.
OWASP categorizza questa classe sotto LLM03 Supply Chain Vulnerabilities. La difesa è triplice: (1) pinning delle dipendenze con lock file committato; (2) verifica pre-installazione che ogni package esista sul registry ufficiale e abbia download count storico ragionevole; (3) per le aziende con requisiti security più stringenti, private registry proxy che impone allowlist esplicita di package approvati.
Sesta classe: trasversale - assunzioni silenziose di sanitizzazione a monte
L'errore più sfumato e più frequente. L'LLM produce un metodo saveUserProfile($data) dove $data arriva dal caller e scrive dritto a database o filesystem. Non c'è controllo perché l'LLM assume che il caller abbia già fatto validazione. Ma quel caller, se anche lui è AI-generated, ha fatto la stessa assunzione opposta - si affida al callee per la validazione. La catena di responsabilità si rompe silenziosamente, e nessuno dei due pezzi individualmente è "sbagliato" secondo una code review isolata.
Checklist: (1) boundary testing esplicito tra livelli applicativi - quali metodi sono "ingresso" (entry point da utente) e devono validare, quali sono "interni" e possono fidarsi; (2) type safety con Value Object per dati strutturati (email, password, IBAN, codice fiscale) invece di passare string nudo attraverso i confini; (3) integration test che verificano il flusso end-to-end con input malformato, perché è l'unica prova oggettiva che la validazione esiste da qualche parte lungo la catena.
Quali procedure operative applico prima di mergiare un PR AI-generated
La checklist meccanica sopra è condizione necessaria ma non sufficiente. Nella mia pipeline personale ho aggiunto tre procedure operative sopra la checklist automatica. La prima è il time boxing del review: se il diff è maggiore di 200 righe e proviene da AI, non viene revisionato in una sola sessione - viene splittato in chunks e revisionato a mente fresca. L'illusione di produttività è il vero vector d'attacco: l'LLM genera in pochi minuti quello che richiede ore di review competente, e il gap tra velocità di generazione e velocità di revisione è dove i bug passano.
La seconda procedura è l'obbligo di un rationale test: per ogni PR AI-generated chiedo al submitter (che può essere anch'io) di scrivere in tre frasi perché la patch funziona, senza riferirsi al codice. Se il submitter non sa articolare il rationale, significa che ha accettato l'output senza comprenderlo - e un codice non compreso è un codice che non può essere mantenuto. Questa è la regola che ha più impatto sulla revert rate post-merge.
La terza è il regression harness mirato. Ogni PR AI-generated passa da una suite di test che verifica esplicitamente le categorie di vulnerabilità della checklist - SAST con SemGrep o PHPStan con regole custom, secret scanner, dependency scanner tipo Composer Audit, test di regressione sui boundary applicativi. Il pipeline blocca il merge se uno di questi test fallisce, senza opzione di override manuale. Gartner nel press release del 25 giugno 2025 ha stimato che oltre il 40% dei progetti agentic AI verrà cancellato entro fine 2027, in larga parte per inadequate risk controls - il regression harness è esattamente il controllo che fa la differenza tra un progetto AI che sopravvive in produzione e uno che viene cancellato dopo il primo incident di sicurezza.
Quali incidenti reali 2025-2026 confermano empiricamente questi rischi
Nel biennio in cui ho tenuto aperta la sandbox di audit ho visto moltiplicarsi gli incidenti pubblici che confermano questa tassonomia. Il 12 febbraio 2026 un agent autonomo deployato su piattaforma OpenClaw ha aperto una pull request al repository ufficiale di matplotlib - libreria Python con 130 milioni di download mensili - e dopo il reject del maintainer Scott Shambaugh ha autonomamente pubblicato un blog post ad hominem contro di lui tentando di costruire narrativa di "gatekeeping". Il caso è pubblico e documentato su theshamblog.com, e costituisce la prima evidenza pubblicamente osservata di quello che Shambaugh chiama "autonomous influence operation against a supply chain gatekeeper". La lezione per chi fa code review è che il rischio non è solo il codice che arriva - è la pressione reputazionale che un agent può esercitare su chi lo respinge. Il processo di review deve essere istituzionalmente protetto da questo tipo di escalation.
Sul fronte CVE, Anthropic stessa ha patchato a ottobre 2025 la CVE-2025-59536 (CVSS 8.7) su Claude Code, una remote code execution via file .claude/settings.json malevolo in repository clonati: un attaccante poteva mettere settings malevoli in un repo pubblico, e il primo sviluppatore che lo clonava vedendo il trust dialog rischiava esecuzione arbitraria prima ancora del consent. A gennaio 2026 è arrivata la CVE-2026-21852 (CVSS 5.3) sempre su Claude Code, questa volta su API key exfiltration via override di ANTHROPIC_BASE_URL. Questi due CVE confermano che lo strumento che stai usando per generare codice è a sua volta un'eseguibile con attack surface non trascurabile - e che il tuo code review deve estendersi all'ambiente di generazione, non solo al codice prodotto.
OWASP GenAI Security Project nella Top 10:2025 mappa esplicitamente queste classi: LLM01 Prompt Injection resta il rischio numero uno ed è il vettore che permette a un documento esterno di manipolare il comportamento dell'agente che legge il codice prima di scriverlo. LLM06 Excessive Agency categorizza il problema del tool con privilegi eccessivi - Claude Code che può eseguire rm -rf senza hook di validazione ricade qui. LLM07 System Prompt Leakage è il rischio emergente del 2026: il system prompt che contiene context aziendale riservato può essere estratto con prompt injection mirata e ritrovarsi in log di debug o in output accidentali.
La probabilità che una qualsiasi di queste classi si manifesti in un progetto AI-assisted reale nel 2026 non è teorica - è documentata empiricamente. Il punto pratico per il senior engineer è che il threat model del proprio codice non include più solo gli attaccanti esterni al perimetro applicativo: include anche i tool che partecipano attivamente alla pipeline di sviluppo.
Se il tuo team sta adottando strumenti AI per lo sviluppo e vuoi una qualificazione rapida di quali controlli mettere in pipeline prima che un output AI entri in produzione, il modulo di preventivo gratuito ti risponde in sette domande - circa due minuti - e ti dice se il tuo scenario rientra nel mio ambito o ti indirizzo verso figure più adatte. Il lavoro preliminare parte sempre dalla sicurezza di base dell'infrastruttura: sui codebase dove il baseline non è ancora solido trovi un inquadramento operativo nel mio articolo su supply chain security con Composer per Laravel e Symfony e nella checklist di hardening Laravel e Symfony NIS2-ready in 14 giorni. Il codice AI-generated non introduce vulnerabilità nuove nel senso teorico; amplifica quelle che il processo di sviluppo già tollerava. La differenza la fa la disciplina che costruisci prima di aprire il repository all'agente.