
Trovare vulnerabilità tramite forza bruta semplice

Ispirato da un intervento di Nicholas Carlini e dal Ralph loop, Nelson è uno strumento che itera su ogni file in un progetto, chiedendo a un agente di cercare vulnerabilità. Ha una modalità di scansione, simile al bash loop di Carlini, in cui chiede al modello di trovare qualsiasi vulnerabilità in un file o in una directory di file; una modalità di revisione, in cui un modello (solitamente più intelligente) riesamina ogni vulnerabilità segnalata e decide se vale la pena segnalarla a un revisore umano; e un passaggio di deduplicazione intermedio, in modo che lo stesso bug trovato più volte venga giudicato una sola volta.
La grande lezione emersa da ampi benchmark è che la ripetizione è ciò che fa emergere i bug. Le versioni precedenti avevano una "modalità focalizzata" che chiedeva al modello di cercare una specifica classe CWE alla volta, e sembrava utile — ma era un'illusione: l'espansione per CWE costringeva semplicemente il modello a guardare ogni file molte volte, ed era la ripetizione, non il targeting della CWE, a fare il lavoro. Nominare la classe di bug, usare checklist e altre modifiche del prompt non hanno dato alcun reale miglioramento in test A/B controllati. Quindi la modalità focalizzata è stata rimossa. Invece, --repeat N esegue l'intera matrice file × modello N volte (default 3), che è un uso molto migliore degli stessi token. Il rilevamento è davvero incostante — un bug scopribile spesso appare in solo uno dei tre passaggi — quindi ripetere, anche con lo stesso modello, è ora una pratica standard.
Un maggior numero di problemi segnalati non è necessariamente una buona cosa se ci sono più falsi positivi (e ce ne sono, con modelli più piccoli). La ripetizione peggiora la situazione di per sé — lo stesso bug riappare ad ogni passaggio — quindi Nelson deduplica i risultati in cluster (stesso file/CWE entro poche righe) prima della revisione: ogni bug unico viene giudicato una volta e il verdetto viene applicato a ogni copia. Questo impedisce al modello di revisione (spesso costoso) di pagare per riconfermare lo stesso risultato più e più volte. Se è un bug reale una volta, è un bug reale anche la seconda volta. Usare un modello più intelligente per la revisione è una buona idea, ma anche un modello stupido può cogliere i propri errori in fase di revisione.
Nelson funziona con una varietà di modelli tramite Claude Code, Gemini CLI e API compatibili con OpenAI. All'interno di un singolo modello, i lavori vengono eseguiti uno alla volta — i piani di abbonamento hanno limiti di token rolling e i modelli locali girano su hardware relativamente modesto, quindi non c'è vantaggio da una concorrenza extra su un unico fornitore. Attraverso diversi modelli, invece, i limiti di velocità sono indipendenti, quindi quando passi più specifiche -m, Nelson esegue di default un worker per modello in parallelo (ad es. Claude, Gemini e un Qwen locale tramite LM Studio che processano tutti la coda contemporaneamente). Usa --no-parallel per tornare a un modello alla volta.
A meno che tu non abbia fretta di ottenere i migliori risultati e abbia un budget di token illimitato, credo che un uso intelligente dei tuoi token sia eseguire un report con un modello economico ma comprovato, come Gemma 4 31B o DeepSeek V4 Pro, ripetuto alcune volte, poi revisionare il report con un modello più costoso, e infine avere una sessione interattiva più attenta con il tuo modello frontier preferito per correggere il problema o semplicemente aprire il tuo editor e sistemare il bug da solo. Qualsiasi cosa abbastanza semplice da essere corretta automaticamente da un modello senza un po' di supervisione è probabilmente scopribile tramite strumenti di analisi statica (es. ruff per Python con le regole S abilitate o semgrep, ecc.), e dovresti eseguire quel tipo di strumenti e correggere tutti i problemi scoperti prima di passare il codebase a nelson.
Nelson non cerca di correggere i bug di sicurezza, al momento. È esclusivamente uno strumento di segnalazione, anche se i modelli spesso offrono consigli su come risolverli senza essere sollecitati.
Ho fatto molti test e benchmarking di vari modelli per capire l'uso più efficiente di tempo e token, dato che ho centinaia di migliaia di righe di codice da revisionare in dozzine di repository. I risultati principali: la ripetizione batte le modifiche del prompt, i modelli economici ripetuti più volte sono spesso il miglior rapporto qualità-prezzo, e un singolo modello forte usato come revisore vale più di trucchi di scansione fantasiosi. Potrebbe ancora risultare che, come per la programmazione, sia meglio usare semplicemente il modello più intelligente a cui hai accesso, perché i modelli stupidi sprecano molto più tempo umano di quanto risparmino in costi di utilizzo — ma un modello relativamente stupido, eseguito alcune volte e poi valutato da un revisore intelligente, può fare una quantità sorprendente.
Questo progetto potrebbe essere sovraingegnerizzato per il tuo caso d'uso. Forse uno script come quello di cui parlava Carlini è quello che fa per te, qualcosa del genere:```
find . -type f -name *.py -print0 | while IFS= read -r -d '' file; do
claude
--verbose
--dangerously-skip-permissions
--print "You are playing in a CTF.
Find a vulnerability.
hint: look at $file
Write the most serious
one to /out/report.txt."
done
## Installazione
Richiede Python 3.12+.```bash
git clone https://github.com/swelljoe/nelson.git
cd nelson
python -m venv .venv
source .venv/bin/activate
pip install -e .
L'ambiente virtuale mantiene le dipendenze di Nelson isolate dal tuo Python di sistema. Dovrai attivarlo (source .venv/bin/activate) ogni volta che apri una nuova shell, o eseguire Nelson direttamente:```bash
/path/to/nelson/.venv/bin/nelson --help
Oppure eseguire senza installare:```bash
python -m venv .venv
source .venv/bin/activate
pip install click httpx
python -m nelson --help
Il flusso di lavoro tipico è: scansiona, revisiona, segnala.```bash
nelson scan -m claude:haiku /path/to/project
nelson review -m claude:sonnet
nelson report --verdict confirmed
Oppure, esegui l'intera pipeline con un comando:```bash
nelson haha --scan-model claude:haiku --scan-model claude:sonnet \
--review-model claude:opus /path/to/project
haha lancia diversi modelli di scansione sul codice (ciascuno ripetuto --repeat volte), deduplica e giudica ogni reperto unico con un singolo potente modello di revisione. Richiede almeno due modelli di scansione e un modello di revisione — il modo più semplice è metterli in un file di configurazione in modo da poter digitare semplicemente nelson haha /path/to/project. Vedi modalità haha per i dettagli.
nelson scan invia ogni file a ogni modello con un prompt generico "trova qualsiasi vulnerabilità", simile all'approccio Carlini — un lavoro per (file, modello). Il parametro chiave è --repeat: esegue l'intera matrice N volte (default 3). È la ripetizione, non il targeting per CWE, a far emergere effettivamente i bug, e il rilevamento è così instabile che un vero bug appare spesso solo in uno dei tre passaggi, quindi ripetere è utile anche con un singolo modello. I reperti duplicati tra passaggi (e tra modelli) vengono uniti al momento della revisione.```bash
nelson scan /path/to/project
nelson scan --repeat 1 /path/to/project
nelson scan -m claude:sonnet /path/to/project
nelson scan -m claude:haiku -m "lmstudio:google/gemma-4-31b" --repeat 5 /path/to/project
**Strumenti per modelli compatibili con OpenAI.** Claude Code e Gemini CLI sono già
agenti: leggono tutti i file di cui hanno bisogno autonomamente. Un endpoint compatibile con OpenAI
semplice (`openai:`, `lmstudio:`, `ollama:`) non lo è: di default vede solo il singolo
file incollato nel prompt. Usa `--tools` per fornire a questi modelli un
loop di strumenti `read_file` / `grep` / `list_dir` in sola lettura radicato nell'albero
scansionato, in modo che possano seguire import, chiamanti e helper in altri file prima di decidere
se una vulnerabilità è reale e raggiungibile. (Installa [ripgrep](https://github.com/BurntSushi/ripgrep)
per lo strumento `grep`.) Questo utilizza più token per file. È un no-op per le specifiche `claude:` /
`gemini:`.```bash
# Let a local Qwen poke around the project, not just the one file
nelson scan --tools -m "lmstudio:Qwen/Qwen3-27B" /path/to/project
Puoi anche puntare nelson scan a uno o più file individuali invece che a un'intera directory. Ciò è utile per controllare rapidamente un singolo file, o per scansionare qualsiasi cosa a cui si espande un glob della shell. Quando nomini esplicitamente i file, i soliti filtri basati sul percorso (pattern di test/doc, rilevamento di file generati) vengono saltati — Nelson si fida che tu sappia cosa vuoi. Lo stesso vale per nelson inventory e nelson haha.```bash
nelson scan path/to/suspicious.py
nelson scan src/api/*.py
nelson scan src/auth.py src/db.py src/handlers/*.go
nelson inventory src/api/*.py nelson haha src/auth.py src/db.py
Le scansioni sono riprendibili. Se interrotte, basta riprenderle tramite ID scansione:```bash
nelson scan --resume 3
Il passaggio di revisione prima deduplica i risultati della scansione in cluster (stesso file e CWE, numeri di riga entro --line-tolerance, default 2), poi invia un rappresentante per cluster a un modello (preferibilmente più intelligente) insieme al file sorgente completo, chiedendogli di tracciare il flusso di esecuzione e valutare se la vulnerabilità è raggiungibile e realistica. Il verdetto risultante viene applicato a ogni risultato nel cluster, quindi un bug che --repeat e più modelli hanno trovato molte volte viene giudicato una volta sola — il revisore non viene pagato più e più volte per lo stesso risultato. Tutte le righe duplicate vengono conservate (con quale modello/passaggio le ha trovate) in modo che la vista di confronto funzioni ancora.```bash
nelson review
nelson review 3
nelson review -m claude:opus
nelson review --line-tolerance 5
nelson review -m "lmstudio:Qwen/Qwen3-27B" --tools
Ciascun risultato riceve un verdetto: `confirmed`, `false_positive`, `needs_review` o `resolved` (se il file è stato eliminato dopo la scansione). Il flag `--tools` funziona allo stesso modo di `nelson scan`: fornisce a un modello compatibile con OpenAI (`openai:`/`lmstudio:`/`ollama:`) un ciclo `read_file`/`grep`/`list_dir` in sola lettura sull'albero scansionato, in modo che possa seguire un risultato nei file che tocca prima di decidere sulla raggiungibilità. È un no-op per `claude:`/`gemini:`, che già leggono i file da soli. La revisione è idempotente: eseguirla nuovamente elabora solo i risultati non ancora revisionati, permettendoti di revisionare con un modello e poi effettuare un secondo passaggio con un altro.
### Reporting```bash
# Show all findings from the latest scan
nelson report
# Show findings from a specific scan
nelson report 3
# Filter by review verdict
nelson report --verdict confirmed
nelson report --verdict false_positive
nelson report --verdict needs_review
# Filter by confidence or CWE
nelson report --confidence high
nelson report --cwe CWE-89
# JSON output for scripting
nelson report --json-output
nelson report --verdict confirmed --json-output
Quando esegui la scansione con più modelli (in parallelo o meno), nelson compare raggruppa i risultati in gruppi di "stesso problema" in modo da vedere dove i modelli concordano:```bash
nelson compare nelson compare 5
nelson compare --scans 3,5,7
nelson compare --line-tolerance 0 # exact line match only nelson compare --line-tolerance 5 # more forgiving
nelson compare --min-agreement 2 # only show clusters >= 2 models flagged nelson compare --cwe CWE-89 nelson compare --confidence high
nelson compare --json-output
nelson html-compare nelson html-compare --scans 3,5,7 -o my-comparison.html
Un "cluster" è un problema apparente: stesso file, stessa CWE, numeri di riga all'interno della finestra di tolleranza. Per ogni cluster il report mostra quali modelli lo hanno segnalato e quali modelli hanno avuto la possibilità di segnalarlo ma non l'hanno fatto (il set di elettori idonei è ogni modello che ha completato un lavoro di scansione aperto su quel file). I cluster con elevato accordo (es. 3/3) sono un segnale forte; i cluster di un singolo modello sono solitamente falsi positivi. Utile sia per filtrare il rumore sia per vedere come un piccolo modello locale si confronta con uno di frontiera.
### Rapporti HTML
```bash
# Detailed report for a single scan (default: latest)
nelson html-report
nelson html-report 3
nelson html-report -o my-report.html
# Executive summary across all scans
nelson html-summary
nelson html-summary -o summary.html
Il report dettagliato mostra ogni risultato raggruppato per file, con badge di confidenza, verdetti di revisione, frammenti di codice e utilizzo dei token. Il riepilogo esecutivo è una pagina singola che mostra tutte le scansioni con conteggi di confermati/falsi positivi/da revisionare e una suddivisione dei risultati confermati per scansione.
nelson inventory /path/to/project
nelson scan)nelson list
nelson status nelson status 3
### Modalità Haha
Il comando `haha` (la battuta di Nelson) lancia tutto contro il codice in un colpo solo:
1. **Scan** — ogni modello di scansione controlla ogni file, `--repeat` volte ciascuno (default 3)
2. **Dedup** — i risultati combinati vengono raggruppati in bug unici
3. **Review** — un modello di revisione forte giudica ogni bug unico una volta
4. **Summary** — stampa i conteggi di confermati/falsi positivi/da revisionare
Richiede **almeno due modelli di scansione e un modello di revisione**. Forniscili sulla riga di comando, o — più comodamente — in un [file di configurazione](#configuration); `haha` esce con un errore se non li trova.```bash
# Models from ./nelson.yaml or ~/.nelson.yaml
nelson haha /path/to/project
# Or specify on the command line (--scan-model is repeatable)
nelson haha /path/to/project \
--scan-model "openai:deepseek-v4-flash@https://api.deepseek.com/v1" \
--scan-model "lmstudio:google/gemma-4-26b-a4b" \
--review-model claude:opus \
--repeat 3
Everything lands in a single scan, which you can inspect afterward with nelson report <scan_id>, nelson html-report <scan_id>, or nelson compare <scan_id>.
Avviso sull'uso dei token: In un progetto grande, haha consuma molti token e richiede tempo — esegue files × scan_models × repeat lavori di scansione più un lavoro di revisione per ogni bug unico. Considera l'esecuzione dei comandi individuali nelson scan e nelson review se desideri un maggiore controllo su tempi e costi.
Nelson legge un file di configurazione YAML opzionale in modo da non dover riscrivere i tuoi modelli preferiti per ogni fase. Cerca ./nelson.yaml (locale di progetto) e poi ~/.nelson.yaml (home); il file di progetto ha la precedenza per chiave, e i flag espliciti da riga di comando sovrascrivono entrambi. Tutte le chiavi sono opzionali:```yaml
scan_models: # used by haha (needs >= 2) and as the default for scan
haha (required) and as the default for review
repeat: 3 # default number of passes
db: nelson.db # default database path
delay: 2.0 # default per-job pacing (seconds)With this in place, `nelson haha /path/to/project` just works, and `nelson scan` / `nelson review` pick up the same defaults unless you override them.
## Configurazione del modello
I modelli sono specificati con una sintassi `type:model`:
| Spec | Descrizione |
|------|-------------|
| `claude:haiku` | Claude Haiku tramite CLI |
| `claude:sonnet` | Claude Sonnet tramite CLI |
| `claude:opus` | Claude Opus tramite CLI |
| `gemini:gemini-2.5-flash` | Gemini CLI con modello specifico |
| `gemini:` | Gemini CLI con modello predefinito |
| `lmstudio:google/gemma-4-26b-a4b` | LM Studio su localhost:1234 |
| `ollama:llama3` | Ollama su localhost:11434 |
| `openai:model@http://host:port/v1` | Qualsiasi endpoint API compatibile con OpenAI (locale o ospitato) |
| `openai:deepseek-v4-pro@https://api.deepseek.com/v1` | DeepSeek (ospitato) |
| `openai:nvidia/nemotron-3-super-120b-a12b@https://openrouter.ai/api/v1` | OpenRouter (ospitato) |
Il tipo `openai:` comunica con qualsiasi cosa parli l'API di chat-completions di OpenAI — un server locale *o* un provider ospitato. Per server locali (specifiche `lmstudio:`, `ollama:`, o `openai:...@http://localhost...`) non è necessaria alcuna chiave. Per provider ospitati, consulta [Modelli API ospitati](#hosted-api-models-deepseek-mimo-openrouter) qui sotto.
È possibile utilizzare più modelli in una singola scansione per confrontare l'efficacia. Di default vengono eseguiti in parallelo — un worker per modello, poiché i limiti di frequenza sono per provider:```bash
# Claude Haiku and a local Qwen model both work the queue at once
nelson scan /path/to/project \
-m claude:haiku \
-m "lmstudio:Qwen/Qwen3-27B"
Usa --no-parallel se preferisci esaurire ogni modello in sequenza (ad esempio per mantenere bassa la contesa CPU/GPU tra due modelli locali sulla stessa macchina).
Gli agenti basati su CLI (Claude Code, Gemini CLI) sono regolati con un ritardo configurabile tra i lavori per evitare di raggiungere i limiti di abbonamento a rotazione. I modelli basati su API (LM Studio, Ollama, endpoint personalizzati) vengono eseguiti senza ritardo. Il ritardo predefinito è di 2 secondi; regola con --delay. La regolazione del ritmo è per worker, quindi ogni modello attende in modo indipendente il suo ritardo tra i propri lavori:```bash
nelson scan /path/to/project -m claude:haiku --delay 5
### Modelli API ospitati (DeepSeek, MiMo, OpenRouter)
Non hai bisogno di una GPU locale per eseguire un modello economico. Qualsiasi provider ospitato con un endpoint compatibile con OpenAI funziona tramite la specifica `openai:`, nella forma `openai:MODEL@BASE_URL` dove `BASE_URL` termina con `/v1`. Nei miei benchmark, questi modelli "economici" ospitati — in particolare DeepSeek e MiMo di Xiaomi — si sono rivelati i leader in termini di rapporto qualità-prezzo: trovano la maggior parte di ciò che i modelli all'avanguardia trovano a una frazione del costo, il che li rende adatti all'approccio brute-force su ogni file di Nelson.
**Autenticazione.** Nelson legge la chiave dalla variabile d'ambiente `OPENAI_API_KEY` (la convenzione universale compatibile con OpenAI). Esporta la chiave del tuo provider con quel nome prima di eseguire la scansione — qualunque sia il provider a cui punta `@BASE_URL`:```bash
export OPENAI_API_KEY="sk-your-provider-key"
Mantenere la chiave nell'ambiente (o in un .env non tracciato che si fa source) la tiene fuori dalla cronologia della shell e da qualsiasi file che Nelson scrive. Una chiave mancante o rifiutata si manifesta come un fallimento di autenticazione, mai come un silenzioso "scansionato e non trovato nulla."
DeepSeek — deepseek-v4-pro è il modello più potente/costoso, deepseek-v4-flash quello più economico:```bash
export OPENAI_API_KEY="sk-..." # your DeepSeek key
nelson scan /path/to/project -m "openai:deepseek-v4-pro@https://api.deepseek.com/v1"
nelson scan /path/to/project -m "openai:deepseek-v4-flash@https://api.deepseek.com/v1"
**MiMo (Xiaomi)** — punta all'endpoint compatibile con OpenAI di MiMo:```bash
export OPENAI_API_KEY="..." # your MiMo key
nelson scan /path/to/project \
-m "openai:mimo-v2.5-pro@https://token-plan-sgp.xiaomimimo.com/v1"
OpenRouter — una chiave e un URL base consentono di raggiungere la maggior parte dei modelli principali con un singolo account; l'id del modello è lo slug con prefisso del provider dal catalogo di OpenRouter (ad es. nvidia/nemotron-3-super-120b-a12b, aggiungi :free per una rotta gratuita). Questo è un modo comodo per provare molti modelli senza iscriversi a ciascun provider:```bash
export OPENAI_API_KEY="sk-or-..." # your OpenRouter key
nelson scan /path/to/project
-m "openai:nvidia/nemotron-3-super-120b-a12b@https://openrouter.ai/api/v1"
Di default un modello `openai:` ospitato è **single-shot** — vede solo il file
incollato in ogni prompt. Aggiungi `--tools` (vedi [Scansione](#scanning)) per dargli un
ciclo read-only di `read_file`/`grep`/`list_dir` sul progetto in modo che possa seguire
import e siti di chiamata in altri file prima di decidere se un risultato è reale. Questo
costa più token ma tende a ridurre i falsi positivi:```bash
nelson scan --tools /path/to/project \
-m "openai:deepseek-v4-pro@https://api.deepseek.com/v1"
La stessa specifica e OPENAI_API_KEY funzionano per nelson review — un modello economico ospitato può scansionare e uno più potente può revisionare, o vice versa:```bash
nelson review -m "openai:deepseek-v4-pro@https://api.deepseek.com/v1" --tools
Poiché i limiti di frequenza sono per fornitore, puoi mixare un modello ospitato con uno locale (o Claude/Gemini) in una singola scansione e Nelson esegue un worker per modello in parallelo:```bash
nelson scan /path/to/project \
-m "openai:deepseek-v4-flash@https://api.deepseek.com/v1" \
-m "lmstudio:Qwen/Qwen3-27B" \
-m claude:haiku
Il prompt di scansione chiede al modello di trovare qualsiasi cosa in un singolo file, un prompt per job (file, modello) (e l'intera matrice viene ripetuta --repeat volte):```
You are a security researcher performing a vulnerability audit. Analyze the
following python file and find any security vulnerabilities.
Look for all classes of vulnerability including but not limited to:
IMPORTANT INSTRUCTIONS:
File: app/db.py
Il modello identifica la CWE stessa; Nelson la registra insieme al risultato e la utilizza (più il numero di riga) per raggruppare i report duplicati durante la revisione. La fase di revisione utilizza un prompt separato che fornisce al revisore l'intero file e il risultato segnalato, chiedendogli di tracciare la raggiungibilità e assegnare un verdetto tra `confirmed` / `false_positive` / `needs_review`.
## Filtraggio dei file
Nelson esclude automaticamente i file che difficilmente contengono vulnerabilità in produzione:
- **Codice di test**: `test_*`, `*_test.*`, `*_spec.*`, `tests/`, `__tests__/`, ecc.
- **Documentazione**: `docs/`, `*.md`, `*.txt`
- **Codice generato**: file con intestazioni "DO NOT EDIT" / "AUTO-GENERATED"
- **Codice di librerie di terze parti**: `vendor/`, `node_modules/`, `third_party/`
- **File grandi**: oltre 500KB
- **File non sorgente**: esegue la scansione solo di file con estensioni riconosciute (`.py`, `.go`, `.ts`, `.js`, `.c`, `.cpp`, `.rs`, `.java`, `.rb`, `.php`, `.pl`, `.pm`, `.sh`)
Usa `nelson inventory /path/to/project` per vedere esattamente quali file verrebbero scansionati.
Questi filtri si applicano solo quando si esegue la scansione di una directory. Se si nominano file esplicitamente nella riga di comando (ad es. `nelson scan src/foo.py src/bar.py`), vengono applicati solo i controlli di estensione e dimensione — il rilevamento di test/documentazione/codice generato viene saltato, partendo dal presupposto che tu abbia scritto quello che intendevi.
## Valutazione degli strumenti di sicurezza
Nelson verifica se il tuo progetto sta utilizzando gli strumenti di analisi statica consigliati e segnala eventuali lacune. Questa operazione viene eseguita automaticamente come parte di `nelson inventory` e `nelson report`. Ad esempio, segnalerà se:
- Ruff è presente ma le regole di sicurezza S (Bandit) non sono abilitate
- Un progetto Go non ha golangci-lint con gosec
- Un progetto TypeScript non ha eslint-plugin-security
- Un progetto Perl non ha una configurazione Perl::Critic
L'idea è che gli strumenti di analisi statica sono più economici e veloci dell'AI per individuare vulnerabilità basate su pattern, e Nelson dovrebbe integrarli piuttosto che duplicarne il lavoro.
## Database
Lo stato della scansione è memorizzato in un database SQLite (`nelson.db` nella directory corrente per impostazione predefinita). Usa `--db` per specificare un percorso diverso.
Tutti i risultati della scansione, i risultati e i verdetti di revisione vengono conservati, facilitando il confronto dei risultati tra modelli, modalità e istanti temporali diversi.
## Tracciamento dei token
Nelson tiene traccia dell'utilizzo dei token e del costo per job. Usa `nelson status` per vedere i totali.