Parliamone
// tecnologie.static-code-analysis

Static Code Analysis

Rilevamento automatico di bug, vulnerabilità e debito tecnico senza eseguire il codice: meccanismi, strumenti e integrazione nelle pipeline di sviluppo.

Software ArchitectureCybersecurity

Executive summary

Quando un team di sviluppo scrive codice, è inevitabile che vengano introdotti errori: alcuni sono bug che causeranno malfunzionamenti, altri sono falle di sicurezza che potrebbero essere sfruttate da un attaccante, altri ancora sono problemi di struttura che rendono il codice progressivamente più difficile da modificare e mantenere. L'analisi statica è la tecnica che consente di rilevare questi problemi in modo automatico, esaminando il codice sorgente senza eseguirlo, prima che il software raggiunga la produzione. Questo articolo confronta i principali strumenti disponibili, analizzandone i meccanismi interni di rilevamento, le differenze nella filosofia di progettazione, e il modo in cui si integrano nel processo di sviluppo per bloccare automaticamente il codice che non rispetta gli standard di qualità.


Background

L'analisi statica del codice sorgente consiste nell'esaminare un programma senza eseguirlo, derivando proprietà sul suo comportamento, sulla sua struttura e sulla sua conformità a regole definite [1]. Il termine copre uno spettro ampio di tecniche, dalla verifica sintattica (linting) all'analisi dei flussi di dati per la rilevazione di vulnerabilità di sicurezza (taint analysis), fino alla misurazione di metriche strutturali come la complessità ciclomatica [2].

La complessità ciclomatica, introdotta da Thomas McCabe nel 1976, è una delle metriche più longeve e utilizzate nell'ingegneria del software [2]. Definita come il numero di percorsi linearmente indipendenti attraverso il grafo di controllo di flusso di una funzione, si calcola come $v(G) = e - n + 2p$ dove $e$ è il numero di archi, $n$ il numero di nodi e $p$ il numero di componenti connesse del grafo. In pratica, la complessità ciclomatica cresce di un'unità per ogni branch condizionale (if, else, case, loop). Valori inferiori a 10 sono generalmente considerati accettabili; valori superiori a 15-20 segnalano funzioni difficili da testare e mantenere; valori superiori a 30-40, comuni nei codebase legacy non gestiti, indicano codice che dovrebbe essere oggetto di refactoring prioritario [2]. La metrica resta rilevante perché fornisce un indicatore oggettivo, calcolabile automaticamente, della complessità strutturale, una proprietà che si correla con il tasso di difettosità e con il costo di manutenzione.

L'evoluzione degli strumenti di analisi statica negli ultimi anni è stata guidata da due forze convergenti: l'integrazione nelle pipeline CI/CD (shift-left security) e l'applicazione di tecniche di intelligenza artificiale per la rilevazione di pattern vulnerabili complessi. L'OWASP (Open Web Application Security Project) classifica l'analisi statica come SAST (Static Application Security Testing) e la include tra le pratiche fondamentali per la sicurezza applicativa, raccomandandone l'uso sistematico durante lo sviluppo e non solo come verifica pre-rilascio [4].


Meccanismi di analisi

Gli strumenti di analisi statica operano attraverso meccanismi fondamentalmente diversi, ciascuno con capacità e limiti specifici.

Pattern matching sintattico e semantico

Il livello più semplice di analisi statica è il matching di pattern: lo strumento cerca nel codice sorgente strutture che corrispondono a regole predefinite. I linter tradizionali (ESLint per JavaScript, Pylint per Python, RuboCop per Ruby) operano prevalentemente a questo livello, rilevando violazioni di stile, costrutti potenzialmente erronei (variabili non utilizzate, import non necessari) e anti-pattern noti [5]. Il pattern matching semantico, adottato da Semgrep, eleva questo approccio: anziché operare sul testo del codice, Semgrep analizza l'AST (Abstract Syntax Tree) e comprende la struttura sintattica, consentendo di scrivere regole che catturano varianti strutturalmente equivalenti di uno stesso pattern, ad esempio, rilevare tutte le chiamate a yaml.load() senza parametro Loader indipendentemente da rinominazioni, import alias o variazioni sintattiche [6].

Data flow analysis e taint analysis

L'analisi del flusso di dati traccia il percorso dei valori attraverso il programma, da dove un dato viene generato (source) a dove viene consumato (sink). La taint analysis è una specializzazione che traccia i dati non fidati (user input) attraverso il codice per verificare se raggiungono punti critici (query SQL, output HTML, comandi di sistema) senza essere sanitizzati [7]. Questa tecnica è il meccanismo primario per la rilevazione di vulnerabilità di iniezione (SQL injection, XSS, command injection), le categorie di vulnerabilità più diffuse secondo la OWASP Top 10 [4].

SonarQube implementa taint analysis cross-file con tracciamento inter-procedurale: il flusso di un dato viene seguito attraverso chiamate di funzione, classi e moduli, consentendo la rilevazione di vulnerabilità che attraversano i confini delle singole funzioni [7]. Dalla versione Advanced SAST, il tracciamento si estende anche alle librerie di terze parti, identificando vulnerabilità che emergono dall'interazione tra codice proprietario e dipendenze open-source [7].

Semgrep ha introdotto l'analisi inter-file e il taint tracking nella versione Pro, estendendo le capacità del pattern matching semantico con la rilevazione di flussi di dati cross-function. L'approccio è più leggero di quello di SonarQube, ottimizzato per velocità di scansione anziché per profondità di analisi, con tempi di esecuzione significativamente inferiori rispetto agli strumenti basati su data flow analysis completa [8].

Metriche strutturali

Oltre alla rilevazione di difetti specifici, gli strumenti di analisi statica calcolano metriche aggregate sulla struttura del codice: complessità ciclomatica per funzione, percentuale di codice duplicato, coupling tra moduli (afferente ed efferente), coesione delle classi, profondità dell'ereditarietà. SonarQube integra queste metriche in un modello di debito tecnico, il SQALE (Software Quality Assessment based on Lifecycle Expectations), che stima il tempo di remediation necessario per risolvere tutte le violazioni rilevate, espresso in giorni-persona [7]. Il Technical Debt Ratio, rapporto tra il costo di remediation e il costo stimato per riscrivere l'intera applicazione, è l'indicatore sintetico più utilizzato per quantificare lo stato di salute strutturale di un codebase.


Strumenti: analisi comparativa

SonarQube

SonarQube, sviluppato da SonarSource, è la piattaforma di analisi statica più diffusa in ambito enterprise, con supporto per oltre 30 linguaggi di programmazione [7]. L'architettura prevede un server centrale (con database PostgreSQL, indice di ricerca Elasticsearch) che riceve i risultati delle analisi eseguite dai scanner, componenti CLI che analizzano il codice localmente o nel CI e trasmettono i risultati al server. Il server mantiene lo storico completo delle analisi, consentendo il tracciamento dell'evoluzione delle metriche nel tempo.

Il modello di regole di SonarQube opera su quattro dimensioni: Reliability (bug, codice che produce comportamento errato), Security (vulnerabilità e security hotspot, codice che può essere sfruttato), Maintainability (code smell, codice che funziona ma è strutturalmente problematico), e Coverage (percentuale di codice coperta da test automatici, rilevata tramite integrazione con i report dei test runner) [7].

Il meccanismo di quality gate è il punto di integrazione con la pipeline CI/CD: una serie di condizioni soglia (es. "nessuna nuova vulnerabilità critica", "copertura sui nuovi file ≥80%", "duplicazione sui nuovi file <3%") che determinano se il codice supera o meno il gate. Se il quality gate fallisce, la build viene bloccata e il merge non è consentito. Questo meccanismo, applicato al codice nuovo (new code period) anziché all'intero codebase, è la strategia più efficace per arrestare l'accumulo di debito tecnico senza richiedere la bonifica immediata del codice legacy [7].

SonarQube è disponibile in tre edizioni: Community (open-source, funzionalità base), Developer (analisi branch, PR decoration), e Enterprise/Data Center (taint analysis avanzata, portfolio management, alta disponibilità). Le funzionalità di sicurezza più avanzate (taint analysis inter-procedurale, rilevazione di vulnerabilità nelle dipendenze) richiedono le edizioni commerciali [7].

Semgrep

Semgrep (Semantic Grep), sviluppato da Semgrep Inc. (precedentemente r2c), adotta un approccio architetturalmente diverso: le regole sono definite in YAML con una sintassi che "assomiglia al codice che cercano", e l'analisi avviene tramite pattern matching sull'AST del programma [6]. Un paper pubblicato a EASE 2024 (International Conference on Evaluation and Assessment in Software Engineering) ha analizzato le performance di Semgrep rispetto ad altri strumenti SAST, evidenziando che l'approccio pattern-based raggiunge velocità di scansione significativamente superiori agli strumenti basati su data flow analysis, con trade-off sulla profondità di rilevazione [8].

Il punto di forza distintivo di Semgrep è la facilità di creazione di regole custom: un ingegnere può scrivere una regola di sicurezza specifica per il proprio codebase in minuti, senza competenze specialistiche in analisi statica. Il registry pubblico contiene migliaia di regole mantenute dalla community e da Semgrep Inc. [6]. Il modello di pricing prevede una versione open-source (Semgrep OSS, pattern matching locale, single-file) e una versione commerciale (Semgrep Pro, con taint analysis cross-file, AI-assisted detection e supply chain analysis).

Linter specializzati

Per contesti dove l'analisi di sicurezza non è il requisito primario, i linter specifici per linguaggio offrono un'integrazione più leggera e immediata. ESLint (JavaScript/TypeScript) e Pylint (Python) sono i più diffusi, con ecosistemi di plugin che estendono le regole base con controlli specifici per framework (React, Django, FastAPI) [5]. L'integrazione nell'IDE (VS Code, IntelliJ) fornisce feedback in tempo reale durante la scrittura, complementando l'analisi in CI che opera come gate di qualità.

Ruff, emerso nel 2023 come alternativa a Pylint per Python, ha guadagnato adozione rapida grazie a performance di ordini di grandezza superiori (implementazione in Rust anziché Python) e compatibilità con le regole di Pylint, Flake8 e isort [9]. La velocità di esecuzione è un fattore critico per l'adozione: un linter che aggiunge 30 secondi alla pipeline CI viene accettato; uno che aggiunge 5 minuti viene aggirato o disabilitato.


Quality gate e integrazione CI/CD

L'efficacia dell'analisi statica dipende dalla sua integrazione nel workflow di sviluppo più che dalla sofisticazione delle regole. Il pattern architetturale consolidato è il quality gate nella pipeline CI/CD: ad ogni pull request, la pipeline esegue l'analisi statica sul codice modificato e blocca il merge se le condizioni del gate non sono soddisfatte [7].

La configurazione del quality gate richiede un bilanciamento tra rigore e praticabilità. Un gate troppo rigido (zero violazioni di qualsiasi severity) produce attrito eccessivo e spinge gli sviluppatori a cercare workaround. Un gate troppo permissivo non arresta l'accumulo di debito. La best practice, formalizzata da SonarSource come "Clean as You Code", applica il quality gate esclusivamente al new code, il codice modificato o aggiunto nel periodo corrente, consentendo al team di mantenere standard elevati sul codice nuovo senza essere bloccato dalle violazioni accumulate nel legacy [7].

La PR decoration, l'annotazione automatica della pull request con i risultati dell'analisi, visibili direttamente nell'interfaccia di GitHub, GitLab o Bitbucket, è il meccanismo che chiude il feedback loop: lo sviluppatore vede le violazioni nel contesto del proprio codice, prima della review, con link alla documentazione della regola e suggerimenti di correzione. L'integrazione con l'IDE (SonarLint per SonarQube, estensione VS Code per Semgrep) estende il feedback al momento della scrittura, spostando la rilevazione ancora più a sinistra nel ciclo di sviluppo [7, 6].


Limiti e problemi aperti

Falsi positivi e falsi negativi. Ogni strumento di analisi statica opera in un trade-off tra precision (percentuale di segnalazioni che sono effettivamente problemi reali) e recall (percentuale di problemi reali effettivamente rilevati). Strumenti orientati alla sicurezza (Semgrep, Checkmarx) tendono a privilegiare il recall (segnalare tutto ciò che potrebbe essere un problema), producendo falsi positivi che richiedono triage manuale. Strumenti orientati alla qualità (SonarQube) tendono a privilegiare la precision, con il rischio di non rilevare vulnerabilità complesse. Nessuno strumento elimina la necessità della review manuale per le segnalazioni critiche.

Limiti intrinseci dell'analisi statica. Il teorema di Rice (1953) stabilisce che qualsiasi proprietà semantica non triviale di un programma è indecidibile nel caso generale [10]. Questo significa che nessuno strumento di analisi statica può garantire contemporaneamente zero falsi positivi e zero falsi negativi: il trade-off è strutturale, non risolvibile con strumenti migliori. L'analisi statica è una approssimazione, utile e necessaria, ma non sufficiente come unica linea di difesa.

Copertura linguistica e framework. Il supporto per linguaggi e framework varia significativamente tra strumenti. SonarQube supporta oltre 30 linguaggi ma con profondità di analisi disomogenea (Java e C# hanno l'analisi più matura; linguaggi meno diffusi hanno set di regole più limitati). Semgrep supporta oltre 30 linguaggi per il pattern matching ma il taint tracking è disponibile solo per un sottoinsieme. I linter specializzati coprono un singolo linguaggio con profondità superiore ma richiedono la gestione di strumenti multipli in un codebase poliglotta.

AI-assisted analysis. L'integrazione di modelli di linguaggio nell'analisi statica (Semgrep Assistant, SonarQube AI CodeFix) rappresenta la direzione emergente, con la promessa di rilevare pattern vulnerabili complessi non esprimibili come regole deterministiche e di suggerire correzioni automatiche. L'efficacia in produzione di questi approcci è ancora oggetto di valutazione, e il rischio di falsi positivi generati da modelli probabilistici richiede meccanismi di validazione aggiuntivi.


Riferimenti

[1] F. Nielson, H. R. Nielson, C. Hankin, Principles of Program Analysis, Springer, 1999.

[2] T. J. McCabe, "A Complexity Measure," in IEEE Transactions on Software Engineering, vol. SE-2, no. 4, 1976. https://doi.org/10.1109/TSE.1976.233837

[3] NIST, "Static Analysis Tool Exposition (SATE)," National Institute of Standards and Technology, 2023. https://www.nist.gov/itl/ssd/software-quality-group/source-code-security-analyzers/sate

[4] OWASP Foundation, "OWASP Top 10 — 2021," 2021. https://owasp.org/Top10/

[5] ESLint, "ESLint Documentation," 2026. https://eslint.org/docs/latest/

[6] Semgrep, "Semgrep Documentation," 2026. https://semgrep.dev/docs/

[7] SonarSource, "SonarQube Documentation," 2026. https://docs.sonarsource.com/sonarqube-server/

[8] M. Pashchenko et al., "Semgrep*: Improving the Limited Performance of Static Application Security Testing (SAST) Tools," in Proc. EASE 2024, ACM, 2024. https://dl.acm.org/doi/10.1145/3661167.3661262

[9] Astral, "Ruff — An Extremely Fast Python Linter," 2025. https://docs.astral.sh/ruff/

[10] H. G. Rice, "Classes of Recursively Enumerable Sets and Their Decision Problems," in Transactions of the American Mathematical Society, vol. 74, no. 2, 1953.

Static Code Analysis

Raccontaci la situazione. Rispondiamo entro 24 ore nei giorni lavorativi.

Tweaks

Light mode
Atmospheric (glass)
Client logos
Terminal hero