Script greppabile: cercare le 7 falle in un repo in un comando
Un file di pattern e un solo comando (PowerShell, ripgrep o grep) per trovare in tutto il progetto le 7 falle tipiche del codice scritto dall’IA.
La guida alle 7 falle di sicurezza nel codice scritto dall’IA ti dà, falla per falla, le stringhe da cercare con Ctrl+F. Funziona su un file; su un progetto con decine di file diventa un lavoro lungo e facile da sbagliare. Qui trasformi quella lista in un file di pattern e lo passi a un solo comando che legge tutta la cartella. Il comando non dice se il codice è sicuro: ti porta dritto alle righe da leggere con attenzione.
La risposta breve: salvi i pattern in falle.txt nella cartella del progetto, poi lanci Select-String su Windows (PowerShell), rg (ripgrep) su qualsiasi sistema oppure grep su macOS e Linux. Sul progetto di prova, con file vulnerabili scritti apposta, i tre comandi hanno restituito le stesse 8 righe. Due casi restano fuori: except e pass scritti su due righe (serve un secondo comando) e i pacchetti inventati, che si verificano su PyPI e non nel codice.
ℹ️ Nota / Prerequisito operativo: ti serve un terminale aperto nella cartella principale del progetto. Su Windows usa PowerShell (il Prompt dei comandi ha
findstr, che non gestisce questi pattern); su macOS e Linux il Terminale.grepè già presente su macOS e Linux; ripgrep va installato a parte ed è facoltativo.
Il file dei pattern
Crea nella cartella del progetto un file di testo chiamato falle.txt con queste righe, una per pattern. Ogni riga corrisponde a una falla della guida madre.
(password|passwd|api_?key|secret|token)[A-Za-z0-9_]*\s*=\s*["'][^"']{4,}
shell\s*=\s*True
os\.system\(
verify\s*=\s*False
check_hostname\s*=\s*False
except[^:]*:\s*pass
0\.0\.0\.0
(^|[^A-Za-z0-9_.])(eval|exec)\(
pickle\.loads?\(
Cosa cerca ogni riga:
- Riga 1, credenziali in chiaro (falla 1): un nome come
password,api_key,secretotoken(ancheAPI_KEY_PROD), poi=e un testo tra virgolette di almeno 4 caratteri. Non segnalaDB_PASSWORD = os.environ.get(...), che è il modo corretto. - Righe 2 e 3, comandi di sistema (falla 2):
shell=True, anche scritto con gli spazi, eos.system(. - Righe 4 e 5, rete senza verifica (falla 3):
verify=Falseecheck_hostname=False. - Riga 6, errori silenziati (falla 4):
exceptseguito dapasssulla stessa riga. - Riga 7, app aperta a tutta la rete (falla 5):
0.0.0.0. - Righe 8 e 9, codice eseguito dai dati (falla 6):
eval(edexec(non preceduti da lettere o da un punto, cosìast.literal_eval(non compare; poipickle.load(epickle.loads(.
I pattern usano solo costrutti che PowerShell, ripgrep, GNU grep e il grep di macOS interpretano allo stesso modo. Per questo la riga 1 scrive [A-Za-z0-9_] invece della scorciatoia \w.
💡 Consiglio pratico — Il file vive nel progetto: tieni
falle.txtnella cartella principale e aggiungilo al controllo di versione. Quando scopri un nuovo schema pericoloso, aggiungi una riga e il controllo successivo lo cerca già.
Il comando, sistema per sistema
Windows (PowerShell):
Get-ChildItem -Recurse -File -Include *.py |
Where-Object { $_.FullName -notmatch '[\\/](\.venv|venv|node_modules|\.git)[\\/]' } |
Select-String -Pattern (Get-Content .\falle.txt)
Get-ChildItem -Recurse -File -Include *.py: elenca tutti i file.pydella cartella e delle sottocartelle.Where-Object { ... -notmatch ... }: scarta i file dentro.venv,venv,node_modulese.git, cioè librerie di terzi e storico di Git.Select-String -Pattern (Get-Content .\falle.txt): legge i pattern dal file e cerca ognuno in ogni file.Select-Stringignora già maiuscole e minuscole.
Qualsiasi sistema, con ripgrep:
rg -n -i -f falle.txt -g '*.py' .
-n: mostra il numero di riga.-i: ignora maiuscole e minuscole, così trova ancheAPI_KEYeShell=True.-f falle.txt: legge i pattern dal file, uno per riga.-g '*.py': limita la ricerca ai file Python..: parte dalla cartella corrente.
ripgrep salta da solo le cartelle nascoste come .venv e .git e quelle elencate in .gitignore. Su Windows lo stesso comando funziona in PowerShell. Per installarlo: winget install BurntSushi.ripgrep.MSVC su Windows, brew install ripgrep su macOS, sudo apt install ripgrep su Debian e Ubuntu (comandi dalla documentazione ufficiale di ripgrep).
macOS / Linux, con grep:
grep -rniE -f falle.txt --include='*.py' --exclude-dir={.venv,venv,node_modules,.git} .
-r: cerca in tutte le sottocartelle.-ne-i: numero di riga e nessuna differenza tra maiuscole e minuscole.-E: usa le espressioni regolari estese, necessarie per|,?e{4,}.-f falle.txt: legge i pattern dal file.--include='*.py': solo file Python.--exclude-dir={...}: salta le cartelle elencate. Le parentesi graffe le espande la shell (bash o zsh) in quattro opzioni separate.
Le combinazioni utili: aggiungi -c (sia a grep sia a rg) per avere solo il conteggio per file, oppure togli --include e -g per controllare anche file .js o .ts (i pattern 1, 7 e 8 valgono anche lì).
Il risultato sul progetto di prova
Il progetto di prova ha due file in app/ con le falle scritte apposta, una copia di eval dentro .venv/ (da non segnalare), un test che usa ast.literal_eval (da non segnalare) e una password letta correttamente dalle variabili d’ambiente. Output di grep su Debian 13 (GNU grep 3.11):
./app/utils.py:3:token = 'abc123-finto'
./app/utils.py:7: os.system("rm -rf " + cartella)
./app/server.py:9:API_KEY = "sk-ESEMPIO-NON-REALE-123"
./app/server.py:14: subprocess.run(f"ping -c 1 {host}", shell=True)
./app/server.py:18: return requests.get(url, verify=False)
./app/server.py:23: return pickle.load(f)
./app/server.py:34: return eval(espressione)
./app/server.py:38: app.run(host="0.0.0.0", port=5000, debug=True)
ripgrep 14.1.1 ha dato le stesse 8 righe nello stesso formato; PowerShell 7.5.4 le stesse 8, con i percorsi senza ./ iniziale e i file in ordine alfabetico. Nessuna riga da .venv/, nessun literal_eval, nessun DB_PASSWORD. Su macOS 15.7.7 il grep di sistema (BSD grep 2.6.0) ha riconosciuto gli stessi pattern sulle stesse righe di prova. La chiave dell’esempio è inventata.
Quando non trova nulla, grep e rg restituiscono il codice di uscita 1 e non stampano niente. Nessun output significa solo che nessun pattern compare, non che il codice è sicuro.
Il secondo comando: except e pass su due righe
La riga 6 di falle.txt trova except Exception: pass scritto su una riga sola. Nel codice generato dall’IA lo trovi spesso così:
except Exception:
pass
Un comando che legge riga per riga non vede le due righe insieme. La soluzione: cercare gli except che finiscono con i due punti e mostrare anche la riga dopo.
Windows (PowerShell):
Get-ChildItem -Recurse -File -Include *.py |
Where-Object { $_.FullName -notmatch '[\\/](\.venv|venv|node_modules|\.git)[\\/]' } |
Select-String -Pattern 'except[^:]*:\s*$' -Context 0,1
macOS / Linux:
grep -rnE -A1 'except[^:]*:\s*$' --include='*.py' --exclude-dir={.venv,venv,node_modules,.git} .
Con ripgrep puoi cercare direttamente le due righe con -U (ricerca su più righe):
rg -n -U -i 'except[^:\n]*:\s*\n\s*pass\b' -g '*.py' .
-Context 0,1 e -A1 aggiungono la riga successiva a ogni risultato: tocca a te guardare se è pass. Sul progetto di prova tutti e tre hanno trovato i due casi, app/server.py righe 29-30 e app/utils.py righe 13-14. Output di grep:
./app/utils.py:13: except:
./app/utils.py-14- pass
--
./app/server.py:29: except Exception:
./app/server.py-30- pass
La falla 7 non si cerca nel codice
I pacchetti inventati dall’IA non lasciano tracce nel codice Python: stanno nei comandi di installazione. Puoi elencare tutte le righe pip install di README, file di testo e script, poi controllare a mano ogni nome su pypi.org:
rg -n -i 'pip3? install' -g '*.md' -g '*.txt' -g '*.sh' .
Sul progetto di prova:
./README.md:3:pip install flask requests super-fast-pdf-cleaner
Per ogni nome guarda anche requirements.txt. Se un pacchetto non esiste su PyPI, o esiste da pochi giorni con un solo autore e nessun progetto collegato, non installarlo. I controlli passo per passo, anche per npm, sono nella guida su come verificare un pacchetto inventato dall’IA. Con grep su macOS e Linux: grep -rniE 'pip3? install' --include='*.md' --include='*.txt' --include='*.sh' .
Cosa il comando non trova
Il comando trova testo, non capisce il codice. Questo ha due conseguenze.
I falsi negativi: nella prova non sono stati segnalati una chiave costruita in due pezzi (chiave = "sk-" + resto), un dizionario {"shell": True} passato come opzioni, e from pickle import load seguito da load(f). Un risultato vuoto non è un certificato.
I falsi positivi: la stessa riga di testo può comparire in un commento, in un messaggio a schermo o in un test. Lanciando il comando grep sugli script scaricabili di questo sito, per esempio, è comparso anche print(...) di uno scanner che nomina eval per spiegarlo: era testo, non una chiamata. Ogni risultato va aperto e letto nel suo contesto.
⚠️ Attenzione — Non cancellare righe a colpo d’occhio: il comando ti dice dove guardare, non cosa cambiare. Per correggere
shell=Trueedevalsenza rompere il programma, segui le guide dedicate prima di toccare il codice.
Per un controllo che legge la struttura del codice servono strumenti di analisi statica come Bandit; questo comando resta il primo passaggio, rapido e senza installazioni. Se preferisci lavorare dall’editor, le stesse verifiche come ricerche da incollare in VS Code o Cursor sono in Find in Files: stringhe spia per revisionare codice generato.
Errori tipici
- Su Windows il comando non trova nulla anche se le falle ci sono → lo hai lanciato nel Prompt dei comandi o hai usato
findstr→ apri PowerShell nella cartella del progetto e usa il bloccoGet-ChildItem ... | Select-String. - Errore che dice che
falle.txtnon esiste (congrep:grep: falle.txt: No such file or directory) → il terminale non è nella cartella dove hai salvatofalle.txt→ spostati nella cartella principale del progetto concdprima di lanciare il comando. - Centinaia di risultati da librerie che non hai scritto tu → l’ambiente virtuale ha un nome diverso da
.venvovenv→ aggiungi quel nome all’elenco di--exclude-dir(grep) o alla parte tra parentesi di-notmatch(PowerShell); ripgrep salta già le cartelle elencate in.gitignore. grep: falle.txt:3: Unmatched ( or \(e nessun risultato → manca-E, quindigreplegge i pattern con le regole di base e non capisce parentesi e|→ ricopia il comando con-rniE.rg: command not foundorg non è riconosciuto→ ripgrep non è installato o il terminale è stato aperto prima dell’installazione → installalo conwinget,brewoapte apri un terminale nuovo.
Domande Frequenti (FAQ)
Funziona anche su progetti JavaScript o TypeScript?
In parte. Le righe per le credenziali, per 0.0.0.0 e per eval( valgono anche in JavaScript: basta cambiare *.py in *.js o *.ts. Le altre righe sono specifiche di Python; per JavaScript andrebbero aggiunti schemi come child_process.exec( o rejectUnauthorized: false.
Perché non usare findstr sul Prompt dei comandi?
findstr supporta solo un sottoinsieme ridotto delle espressioni regolari: non conosce | per le alternative né le ripetizioni come {4,}. I pattern di falle.txt andrebbero spezzati e semplificati, con più falsi positivi. PowerShell è già presente su Windows e legge lo stesso file senza modifiche.
Posso salvare il comando in uno script da lanciare con un doppio clic?
Sì: salva il blocco PowerShell in un file .ps1 o quello grep in un file .sh accanto a falle.txt. Su Windows i criteri di esecuzione di PowerShell possono bloccare gli script locali; in quel caso incolla il comando nel terminale.
Il comando trova anche chiavi API vere dentro i file .env?
Solo se aggiungi .env ai file da cercare: con ripgrep basta rg -n -i -f falle.txt -g '.env' ., che nella prova ha letto .env anche se era nascosto ed elencato in .gitignore. Le chiavi in .env sono al posto giusto; il problema nasce se .env finisce nel controllo di versione. Per quello controlla che .env sia elencato in .gitignore.
Quanto spesso conviene lanciarlo?
Ogni volta che l’IA ha generato o riscritto parti del progetto, e prima di consegnarlo o metterlo online.
Dopo il controllo
Apri ogni riga trovata, decidi se è un problema reale e correggila con la guida della falla corrispondente: per i comandi di sistema i pattern sicuri per shell=True e subprocess in Python, per eval e pickle le alternative sicure per dati e configurazione. Poi rilancia il comando: l’obiettivo è un elenco di sole righe che hai letto e deciso di tenere. Se preferisci che sia qualcun altro a passare il progetto riga per riga, è un lavoro che faccio anche su richiesta. Per il quadro completo e la tabella dell’audit in 10 minuti, torna alla guida madre sulle 7 falle di sicurezza nel codice scritto dall’IA.