Analizzatore statico di codice sorgente, senza dipendenze esterne. Passa la cartella di un progetto e ti dice quali file non servono più, quale codice è duplicato, quali dipendenze sono inutilizzate o obsolete e quali errori comuni ci sono dentro.
Supporta Python (analisi via AST, alta precisione) e JavaScript / TypeScript /
Vue / Svelte (parser interno, nessun Node richiesto). Legge package.json,
requirements.txt, pyproject.toml, Pipfile, setup.py e setup.cfg.
python -m codeanalyzer . # report a terminale
python -m codeanalyzer . --serve # web app localeDemo pubblica: https://codeanalyzer-demo.fly.dev — provala senza installare niente, scegliendo un progetto di esempio o caricando uno .zip del tuo codice.
Serve solo Python 3.11+. Nessuna dipendenza a runtime.
git clone <questo-repo> && cd code_analyze
python -m codeanalyzer --helpInstallazione come comando globale (opzionale):
pip install -e .Dopo l'installazione il comando codeanalyzer è disponibile nella shell.
Costruisce il grafo delle importazioni del progetto, individua i punti di
ingresso (main.py, __main__.py, index.js, main e bin di
package.json, script di CI, Dockerfile, file di test, routing per convenzione
tipo pages/ e routes/) e segnala tutto ciò che non è raggiungibile.
| Regola | Cosa significa |
|---|---|
unused-file |
Nessuno importa né referenzia questo file |
unreachable-module |
Importato solo da altri file a loro volta morti |
orphan-asset |
Immagine / font / media mai citato da nessuna parte |
broken-relative-import |
Import relativo che non risolve su nessun file |
empty-file, no-code-file |
File vuoto o solo commenti |
Gli import dinamici (importlib, __import__, require(variabile)) abbassano
automaticamente la confidenza del risultato invece di produrre falsi positivi
silenziosi.
Ogni funzione viene ridotta a un flusso di token con le variabili locali rinominate posizionalmente: due copie che differiscono solo nei nomi delle variabili collidono comunque.
| Regola | Cosa significa |
|---|---|
duplicate-function |
Stessa identica logica in più punti |
similar-function |
Quasi identiche (soglia Jaccard configurabile) |
identical-file |
Due file con contenuto identico |
duplicate-block |
Sequenza di righe ripetuta |
repeated-literal |
Stessa stringa scritta a mano decine di volte |
I quasi-duplicati usano bottom-k shingle sketches con indice invertito: il costo resta quasi lineare anche su progetti grandi, e solo le coppie candidate pagano il confronto Jaccard completo.
| Regola | Cosa significa |
|---|---|
unused-dependency |
Dichiarata nel manifest ma mai importata |
undeclared-dependency |
Importata ma assente dal manifest (funziona solo per fortuna) |
deprecated-dependency |
Pacchetto obsoleto, abbandonato o compromesso |
removed-stdlib-module |
Modulo tolto dalla stdlib (distutils, imp, cgi, telnetlib…) |
conflicting-dependency |
Versioni diverse in manifest diversi |
dev-dependency-in-production |
devDependency importata da codice non di test |
unpinned-dependency |
Nessun vincolo di versione |
Conosce la mappa nome-pacchetto → nome-modulo (Pillow→PIL,
PyYAML→yaml, beautifulsoup4→bs4, …) e non segnala i tool di build
(pytest, black, eslint, webpack…) come inutilizzati.
Python — argomenti default mutabili, except: nudo, except: pass,
chiave duplicata in un dizionario, funzione/metodo definito due volte nello
stesso scope, codice irraggiungibile, closure sulla variabile di ciclo, metodo
senza self, attributo di classe mutabile, == None, is con letterale,
type(x) == T, confronto di una variabile con sé stessa, import/variabili/
funzioni private inutilizzate, import *, f-string senza placeholder,
concatenazione di stringhe in ciclo, open() fuori da with, return
incoerenti, funzioni troppo lunghe / troppi parametri / complessità ciclomatica
/ annidamento eccessivo.
JavaScript / TypeScript — == invece di ===, var, console.log
dimenticati, debugger, catch {} vuoto, eval, assegnazione dentro un if,
auto-assegnazione, parseInt senza radix, promise senza .catch(), await
dentro un ciclo, import inutilizzati, : any espliciti, @ts-ignore.
Trasversali — marcatori di conflitto di merge non risolti, indentazione mista tab/spazi, file sovradimensionati, righe troppo lunghe, inventario di TODO / FIXME / HACK.
Credenziali in chiaro (chiavi AWS, token GitHub/Slack/Stripe, chiavi private,
JWT, DSN con password, segreti generici verificati per entropia), API
pericolose (shell=True, eval, pickle.loads, yaml.load, SQL costruito con
f-string, verify=False, debug=True, innerHTML, new Function,
Math.random per generare token) e file .env non coperti da .gitignore.
I valori sospetti vengono oscurati nel report: il tool non stampa mai il segreto per intero.
Assenza di test, suite di test sottile, README/LICENSE/.gitignore mancanti, e
una classifica di hotspot (dimensione × complessità) che indica dove i bug
tendono ad accumularsi.
python -m codeanalyzer . # analizza il progetto corrente
python -m codeanalyzer ../app -s medium # solo da severità media in su
python -m codeanalyzer . --only duplicates,security # solo due analizzatori
python -m codeanalyzer . -f html -o report.html # report navigabile da condividere
python -m codeanalyzer . -f json | jq '.findings[0]'
python -m codeanalyzer . --fail-on high # exit code 1 per la CI
python -m codeanalyzer --list-rules # elenco completo delle regole| Opzione | Effetto |
|---|---|
-f, --format |
text, json, markdown, html, csv, sarif (anche più di uno, separati da virgola) |
-o, --output |
Scrive su file; con più formati diventa il nome base |
-s, --min-severity |
info | low | medium | high | critical |
--only / --skip |
Attiva o disattiva categorie |
--ignore-rule |
Silenzia una regola (ripetibile) |
--exclude / --include |
Glob di percorsi (ripetibili) |
--no-gitignore |
Ignora i file .gitignore |
--include-hidden |
Analizza anche file e cartelle nascosti |
--similarity |
Soglia per i quasi-duplicati (default 0.80) |
--max-function-lines, --max-complexity, --max-file-lines |
Soglie di manutenibilità |
--max-per-rule |
Quante occorrenze mostrare per regola (0 = tutte) |
--fail-on |
Exit code 1 se emerge qualcosa a quella severità |
-v, --verbose |
Mostra ogni occorrenza e ogni suggerimento |
| Codice | Significato |
|---|---|
0 |
Nessun risultato a livello --fail-on |
1 |
Trovato qualcosa alla soglia indicata |
2 |
Errore di invocazione |
130 |
Interrotto |
Il tool legge, in ordine, .codeanalyzer.toml, .codeanalyzer.json, oppure la
sezione [tool.codeanalyzer] di pyproject.toml.
[tool.codeanalyzer]
exclude-globs = ["demo", "demo/*", "*.generated.ts"]
ignore-rules = ["repeated-literal", "long-lines"]
min-severity = "low"
max-function-lines = 90
max-complexity = 18
similarity-threshold = 0.85Le esclusioni si sommano ai default (node_modules, .venv, dist,
lock file, bundle minificati…): un file di configurazione non può riattivarle
per sbaglio.
- name: Analisi statica
run: |
python -m codeanalyzer . --fail-on high -f sarif -o results.sarif.json
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif.jsonL'output SARIF 2.1.0 è accettato da GitHub code scanning e dalla maggior parte delle dashboard di qualità.
codeanalyzer/
├── cli.py interfaccia a riga di comando
├── engine.py orchestrazione: scan → parse → analizzatori → report
├── config.py default, tabelle dei linguaggi, file di configurazione
├── scanner.py attraversamento del progetto, .gitignore, conteggio righe
├── manifests.py lettura di package.json / requirements / pyproject / Pipfile
├── models.py Finding, Severity, SourceFile, Report
├── parsers/
│ ├── python_parser.py AST: import, funzioni, impronte, complessità
│ └── js_parser.py parser JS/TS senza dipendenze (blanking + brace matching)
├── analyzers/
│ ├── unused_files.py grafo delle importazioni e raggiungibilità
│ ├── duplicates.py cloni esatti, quasi-cloni, blocchi ripetuti
│ ├── dependencies.py igiene delle dipendenze
│ ├── python_smells.py errori comuni Python (su AST)
│ ├── js_smells.py errori comuni JS/TS
│ ├── generic_smells.py controlli indipendenti dal linguaggio
│ ├── security.py credenziali e API pericolose
│ └── metrics.py salute del progetto e hotspot
├── reporters/
│ ├── terminal.py output a terminale (ANSI, fallback ASCII)
│ ├── structured.py JSON, Markdown, CSV, SARIF
│ └── html_reporter.py report HTML interattivo autoconsistente
└── web/
├── server.py server HTTP (stdlib), API JSON, token di sessione
├── jobs.py analisi in thread separati, cronologia, opzioni
├── demo.py restrizioni per l'esposizione pubblica
└── static/ index.html, app.js, style.css (nessun build step)
Ogni finding porta con sé una confidenza: le regole euristiche (asset orfani, segreti generici, dipendenze usate solo da plugin) dichiarano esplicitamente quanto sono sicure invece di fingere certezza.
Il parser JavaScript lavora su una copia del sorgente in cui commenti,
contenuto delle stringhe e letterali regex sono sostituiti da spazi
mantenendo tutti gli offset: nessun pattern può scattare dentro una stringa. Lo
stesso vale per le regole Python sulle API pericolose, che girano su una copia
prodotta con tokenize.
demo/messy_project/ contiene un progetto minuscolo con problemi voluti
(credenziali in chiaro, SQL injection, funzioni duplicate, file morti,
dipendenze obsolete) che serve a vedere il tool in azione:
python -m codeanalyzer demo/messy_projectLo stesso motore, dietro un'interfaccia locale:
python -m codeanalyzer . --serveSi apre il browser su http://127.0.0.1:8770. Nessuna dipendenza aggiuntiva:
il server è http.server della libreria standard, il frontend è HTML/CSS/JS
senza build step e senza CDN.
Cosa ci puoi fare:
- Scegliere il progetto scrivendo il percorso o navigando le cartelle; il
campo segnala se ha riconosciuto un manifest (
pyproject.toml,package.json…). - Trascinare uno .zip per analizzare codice che non sta su questa macchina.
- Regolare le opzioni — quali analizzatori attivare, severità minima, soglie di complessità, glob da escludere — senza ricordare i flag da riga di comando.
- Filtrare i risultati per severità, per testo, o mostrando solo i risultati ad alta confidenza.
- Aprire il codice cliccando un risultato: si apre il file alla riga esatta, con la riga incriminata evidenziata e la sintassi colorata.
- Esportare in HTML, JSON, Markdown, CSV, SARIF o testo.
- Rileggere le analisi precedenti dalla cronologia laterale.
| Opzione | Effetto |
|---|---|
--serve |
Avvia la web app invece di stampare un report |
--port N |
Porta (default 8770) |
--host ADDR |
Indirizzo di bind (default 127.0.0.1, solo loopback) |
--no-browser |
Non aprire automaticamente il browser |
Il percorso passato come argomento posizionale diventa la cartella di partenza:
codeanalyzer ~/progetti --serve.
La web app può leggere i file del progetto che analizzi, quindi:
- si lega solo a
127.0.0.1salvo--hostesplicito (che stampa un avviso); - ogni chiamata
/api/richiede un token di sessione generato all'avvio e consegnato solo dentro l'HTML dell'app: una pagina di un altro sito non può leggerlo e quindi non può pilotare il server; - il visualizzatore di codice risolve i percorsi dentro la radice analizzata e rifiuta qualsiasi tentativo di uscirne;
- gli zip caricati vengono estratti rifiutando percorsi assoluti e traversal (zip-slip), con un tetto di 80 MB;
- non viene mai eseguito nulla del codice analizzato.
La demo gira su fly.io: https://codeanalyzer-demo.fly.dev
La web app locale è uno strumento da sviluppatore: naviga il filesystem e
analizza qualunque cartella le indichi. Esposta su un URL pubblico, quello stesso
comportamento è lettura remota di file arbitrari per chiunque passi di lì.
Per questo la demo gira con --demo, che toglie proprio quelle capacità:
| Locale | --demo |
|
|---|---|---|
Navigazione del filesystem (/api/browse) |
sì | disabilitata (403) |
| Analisi di un percorso arbitrario | sì | rifiutata (403) |
| Progetti analizzabili | qualsiasi cartella | solo gli esempi inclusi e gli .zip caricati |
| Upload massimo | 80 MB | 8 MB, max 2.000 file, protezione zip-bomb |
| File per analisi | illimitati | 1.500 (max_files) |
| Limite di frequenza | nessuno | 12 analisi / 5 min per IP, 3 concorrenti |
| Ritenzione degli upload | fino a chiusura | cancellati dopo 30 minuti |
Il visualizzatore di codice resta confinato alla radice analizzata in entrambe le modalità, e in nessuna delle due viene mai eseguito il codice analizzato.
python -m codeanalyzer . --serve --demo --samples demo --host 0.0.0.0 --port 8080Ogni sottocartella di --samples diventa un progetto selezionabile.
docker build -t codeanalyzer-demo .
docker run --rm -p 8080:8080 codeanalyzer-demoL'immagine è python:3.13-slim più il pacchetto: nessuna dipendenza da
installare, processo non-root, healthcheck HTTP.
flyctl apps create codeanalyzer-demo
flyctl deploy --remote-onlyfly.toml tiene la macchina a min_machines_running = 0 con auto-stop: la demo
dorme quando nessuno la usa e si sveglia alla prima richiesta. Il deploy si
ripete a ogni push su main tramite .github/workflows/deploy.yml (richiede il
secret FLY_API_TOKEN, generabile con flyctl tokens create deploy).
Misurate su Windows, Python 3.14, senza parallelismo:
| Progetto | File | Righe | Tempo |
|---|---|---|---|
| questo repository | 37 | 8.800 | ~2 s |
site-packages completo |
3.360 | 1.006.000 | ~4 min 40 s |
Il costo cresce linearmente con le righe (~3.600 righe/s). Le due parti che potevano degenerare in comportamento quadratico — "questo nome è citato altrove?" e la ricerca di coppie candidate per i quasi-duplicati — usano rispettivamente un indice invertito costruito una sola volta e uno sketch bottom-k con tetto sulle coppie tracciate.
Disattivare un analizzatore ne evita davvero il costo: con --only security
il fingerprinting dei cloni non viene nemmeno calcolato.
pip install pytest
python -m pytest tests/ -q148 test coprono scanner, parser, ogni analizzatore, i reporter e la CLI — inclusi i casi negativi (codice pulito che non deve produrre segnalazioni) il livello web (autenticazione, sandbox dei percorsi, zip-slip) e ogni restrizione della modalità demo.
- L'analisi è statica: reflection, import dinamici, plugin caricati per nome e bundler con alias personalizzati possono far apparire "inutilizzato" del codice che invece viene usato. Per questo la confidenza è sempre esplicita.
- Il parser JS non è un vero parser: copre import, funzioni e i pattern d'errore comuni, non la semantica completa del linguaggio.
- Il controllo dipendenze legge i manifest, non il registro dei pacchetti: sa
che
requestè deprecato perché lo ha in tabella, non perché interroghi npm. - Le regole di sicurezza sono un linter, non un audit: segnalano schemi che meritano uno sguardo umano.