Strumenti di analisi statica del codice per sviluppatori Rust

La cassetta degli attrezzi dello sviluppatore Rust: i migliori strumenti per l'analisi statica del codice

Il compilatore di Rust è il sistema di revisione del codice più rigido con cui la maggior parte degli sviluppatori si troverà mai a lavorare. Il suo sistema di proprietà, il controllo dei prestiti e l'applicazione a vita eliminano intere categorie di bug che affliggono le codebase C e C++. Ma un compilatore che previene gli errori di memoria non è la stessa cosa di un sistema di qualità completo. Bug logici, catene di dipendenze non sicure, uso improprio di blocchi non sicuri, anti-pattern prestazionali e deriva stilistica sopravvivono tutti. rustcI team che rilasciano codice Rust affidabile su larga scala combinano il compilatore con un insieme stratificato di strumenti di analisi statica, ognuno dei quali copre una diversa area della superficie di controllo della qualità.

Scegliere la combinazione sbagliata spreca tempo di CI e genera rumore che gli sviluppatori imparano a ignorare. Scegliere quella giusta fa sì che la rigorosità del compilatore venga percepita come un limite minimo piuttosto che massimo. Gli strumenti in questa guida sono organizzati in base alla loro funzione effettiva, non in ordine alfabetico, in modo che i team possano costruire uno stack che copra tutti e tre i livelli di qualità: linting e idiomi, sicurezza delle dipendenze e verifica approfondita per codice critico o sensibile alla sicurezza.

SMART TS XL

Mantenere la qualità nello sviluppo moderno di Rust è una sfida, nonostante le solide garanzie di sicurezza del linguaggio. SMART TS XL È progettato per aiutare i team a creare software affidabile, manutenibile e sicuro, offrendo funzionalità di analisi statica approfondita, su misura per le caratteristiche uniche di Rust. Supporta i flussi di lavoro di ingegneria professionale individuando tempestivamente i problemi, garantendo la coerenza e riducendo lo sforzo di revisione manuale.

Analisi statica dei tre strati della ruggine

Capire quale strumento sia più adatto a ciascun contesto previene l'errore comune di utilizzare un verificatore formale laddove sarebbe sufficiente un linter, o di affidarsi a un linter laddove sarebbe necessario uno scanner di sicurezza. Ogni progetto Rust maturo dovrebbe coprire tutti e tre i livelli.

Livello 1: Il controllo logico e di stile individua violazioni idiomatiche, errori comuni e problemi di manutenibilità. Strumenti: Clippy, rustfmt, rust-analyzer.

Livello 2: Audit di sicurezza delle dipendenze e della catena di fornitura di crate di terze parti per individuare vulnerabilità note e violazioni delle policy. Strumenti: cargo-audit, cargo-deny, cargo-auditable.

Livello 3: La verifica approfondita individua problemi di sicurezza della memoria in codice non sicuro, comportamenti indefiniti e dimostra formalmente le proprietà delle funzioni critiche. Strumenti: Miri, Kani, MIRAI, Rudra, Creusot, Prusti.

La tabella seguente associa ciascuno strumento principale al relativo livello, costo, idoneità all'integrazione continua (CI) e caso d'uso principale:

ChiavettaStratoCostoCI AdattoCaso d'uso primario
ClippylintingLibero / OSSSiPelucchi idiomatici, stile, insetti comuni
rustfmtlintingLibero / OSSSiApplicazione delle norme di formattazione del codice
analizzatore di ruggineLinting / DXLibero / OSSParzialeDiagnostica IDE, analisi in linea
audit del caricoSicurezza delle dipendenzeLibero / OSSSiCVE noti nelle casse
negare il caricoSicurezza delle dipendenzeLibero / OSSSiLicenza, duplicati, avvisi
carico udibileSicurezza delle dipendenzeLibero / OSSSiIncorporare i dati di dipendenza nei file binari
MiriVerifica approfonditaLibero / OSSSelettivoComportamento indefinito in codice non sicuro
KaniVerifica approfonditaLibero / OSSSelettivoVerifica formale, controllo del modello
MIRAIVerifica approfonditaLibero / OSSSelettivoInterpretazione astratta, analisi della contaminazione
RudraVerifica approfonditaLibero / OSSRicercaBug di sicurezza della memoria in Rust non sicuro
SegrepTaglio trasversaleLivello gratuito + a pagamentoSiModelli di sicurezza personalizzati, multilingue
Sonar CloudTaglio trasversaleLivello gratuito + a pagamentoSiControllo qualità continuo, dashboard

Livello 1: Linting, stile ed esperienza dello sviluppatore

Clippy

Clippy è il punto di partenza definitivo per l'analisi statica in Rust. Incluso nella toolchain ufficiale di Rust, fornisce oltre 700 lint che coprono correttezza, prestazioni, stile, complessità e restrizioni. È lo strumento che la maggior parte dei team Rust utilizza quotidianamente, e per una buona ragione: individua bug reali, non solo opinioni di stile.

bash

# Run all lints including pedantic and nursery groups
cargo clippy -- -W clippy::all -W clippy::pedantic

# Fail CI on any warning
cargo clippy -- -D warnings

# Check specific lint group
cargo clippy -- -W clippy::correctness -W clippy::suspicious

È importante comprendere a fondo le categorie di pelucchi di Clippy:

CategoriaCosa cattura
correctnessCodice quasi certamente errato (rifiutare sempre)
suspiciousCodice probabilmente errato o molto sorprendente
styleModelli non idiomatici con un netto miglioramento
complexityCostrutti inutilmente complessi
performanceCodice che si compila ma viene eseguito più lentamente del previsto.
pedanticLinee guida rigide, basate su opinioni ben precise (abilitazione selettiva)
restrictionRegole che potresti voler applicare in contesti specifici
nurseryNuovi pelucchi, potrebbero produrre falsi positivi

controllo merci vs clippy: cargo check Verifica la correttezza dei tipi e la compilazione senza generare un file binario. È veloce e adatto alla verifica della build. cargo clippy corre cargo check più tutte le analisi lint. Per CI, esegui cargo check per un feedback di compilazione rapido e cargo clippy -- -D warnings come un controllo di qualità separato. Non sono intercambiabili: cargo check non coglierà i problemi idiomatici e cargo clippy è più lento.

Configurazione di Clippy via clippy.toml oppure gli attributi inline consentono ai team di controllare il rumore:

toml

# clippy.toml
avoid-breaking-exported-api = false
msrv = "1.70"

ruggine

// Suppress a lint for one function with documented reason
#[allow(clippy::too_many_arguments)]
fn complex_setup(...) { ... }

Ciò che Clippy non copre: analisi approfondita del flusso di dati tra i moduli, scansione delle vulnerabilità di sicurezza nelle dipendenze, prova formale di correttezza o regole personalizzate specifiche dell'organizzazione. Queste funzionalità richiedono strumenti presenti negli altri livelli.

rustfmt

rustfmt impone una formattazione coerente in tutto il codice sorgente utilizzando cargo fmt. rustfmt.toml Il file di configurazione definisce le regole di stile a livello di progetto. In CI, cargo fmt -- --check esce con un codice diverso da zero se un qualsiasi file dovesse essere riformattato, rendendolo un controllo di qualità pulito senza overhead di configurazione:

bash

# Check formatting without modifying files (CI mode)
cargo fmt -- --check

# Apply formatting (developer mode)
cargo fmt

Rust-analyzer e integrazione con l'IDE

rust-analyzer è l'implementazione del Language Server Protocol (LSP) per Rust, che fornisce diagnostica in tempo reale, completamento automatico, navigazione alla definizione e avvisi Clippy inline in qualsiasi editor compatibile con LSP. Ha sostituito il deprecato RLS (Rust Language Server) e dovrebbe essere l'unico language server utilizzato nei nuovi progetti.

Dal punto di vista dell'analisi statica, rust-analyzer visualizza i commenti di Clippy direttamente nel codice mentre gli sviluppatori digitano, fornendo un feedback immediato su problemi di stile e correttezza nel momento stesso in cui vengono introdotti, anziché richiedere l'esecuzione separata nel terminale.

json

// VS Code settings.json: enable Clippy via rust-analyzer
{
  "rust-analyzer.check.command": "clippy",
  "rust-analyzer.check.extraArgs": [
    "--", "-W", "clippy::all", "-W", "clippy::pedantic"
  ]
}

rust-analyzer fornisce anche suggerimenti integrati per i tipi inferiti, la durata dei prestiti e i nomi dei parametri, utili per esaminare codice sconosciuto senza dover consultare le definizioni.

Livello 2: Dipendenza e sicurezza della catena di approvvigionamento

audit del carico

controlli di audit del carico Cargo.lock Questo strumento confronta il file con il RustSec Advisory Database, identificando le CVE note nelle dipendenze dirette e transitive. È lo strumento minimo di sicurezza delle dipendenze che ogni progetto Rust dovrebbe utilizzare in un sistema di integrazione continua (CI).

bash

# Install
cargo install cargo-audit

# Run audit
cargo audit

# Ignore a specific advisory (document the reason in comments)
cargo audit --ignore RUSTSEC-2024-0001

# JSON output for pipeline integration
cargo audit --json | jq '.vulnerabilities'

cargo-audit rileva solo le vulnerabilità note e pubblicate. Non è in grado di individuare vulnerabilità nel proprio codice, problemi non ancora presenti nel database RustSec o violazioni delle policy, come ad esempio licenze indesiderate.

negare il carico

cargo-deny estende la gestione delle dipendenze oltre le CVE note, includendo la conformità delle licenze, il rilevamento di crate duplicati e le policy di blocco/autorizzazione personalizzate. È lo strumento più completo per le organizzazioni con requisiti di conformità:

toml

# deny.toml
[advisories]
vulnerability = "deny"
unmaintained = "warn"

[licenses]
allow = ["MIT", "Apache-2.0", "ISC", "BSD-2-Clause", "BSD-3-Clause"]
deny = ["GPL-2.0"]
copyleft = "warn"

[bans]
multiple-versions = "warn"

bash

cargo install cargo-deny
cargo deny check

carico udibile

cargo-auditable incorpora l'intero albero delle dipendenze nei binari Rust compilati come metadati strutturati in una sezione dedicata del linker. Ciò consente di verificare i binari distribuiti senza accedere al codice sorgente, un aspetto prezioso per la revisione della sicurezza post-distribuzione e la trasparenza della catena di fornitura.

bash

cargo install cargo-auditable
cargo auditable build --release
# The binary now contains auditable dependency metadata
# Audit a binary directly:
cargo audit bin ./target/release/my-service

Livello 3: Verifica approfondita

Gli strumenti di questo livello richiedono una configurazione più complessa, maggiori competenze e tempi di analisi più lunghi. Non sono adatti a ogni funzione in ogni codebase. Sono essenziali per il codice non sicuro, le implementazioni crittografiche, la logica di analisi sintattica che gestisce input non attendibili e qualsiasi codice in cui la correttezza deve essere dimostrata anziché testata.

Miri: Rilevamento di comportamenti indefiniti

Miri è un interprete per la Mid-Level Intermediate Representation (MIR) di Rust che esegue il codice in modo da rilevare comportamenti indefiniti che il compilatore consente ma che violano le garanzie di sicurezza di Rust: accesso alla memoria fuori dai limiti in blocchi non sicuri, utilizzo di memoria dopo la sua liberazione, dereferenziazioni di puntatori non allineate, race condition in codice non sicuro multi-thread e violazioni del modello di aliasing di Rust.

bash

# Install Miri (requires nightly)
rustup +nightly component add miri

# Run tests under Miri
cargo +nightly miri test

# Enable strict aliasing validation
MIRIFLAGS="-Zmiri-strict-provenance" cargo +nightly miri test

Ciò che Miri rileva: comportamento indefinito in unsafe blocchi, violazioni di accesso alla memoria, uso di memoria dopo la sua liberazione, accessi non allineati, overflow di interi nelle build di debug, race condition in codice concorrente non sicuro.

Ciò che Miri non copre : bug logici in codice sicuro che non implicano comportamenti indefiniti, vulnerabilità di dipendenza, stile di programmazione o problemi di prestazioni.

Integrazione CI: Miri è 10-50 volte più lento di cargo test. Eseguilo selettivamente sui moduli contenenti unsafe codice, oppure con una pianificazione notturna anziché ad ogni commit:

YAML

- name: Miri on unsafe modules
  run: |
    rustup +nightly component add miri
    cargo +nightly miri test --package my-unsafe-crate

Kani: Verifica dei modelli e verifica formale

Kani, sviluppato da Amazon Web Services, è un model checker che verifica formalmente le proprietà del codice Rust esplorando in modo esaustivo i percorsi di esecuzione fino a un limite definito. Mentre Miri esegue casi di test reali e trova bug concreti, Kani dimostra o confuta che le proprietà sono valide per tutti gli input all'interno del suo ambito di analisi.

Kani richiede la scrittura di framework di verifica: funzioni che definiscono lo spazio di input e le proprietà da verificare:

ruggine

#[cfg(kani)]
mod verification {
    use super::*;

    #[kani::proof]
    fn verify_add_no_overflow() {
        let a: u32 = kani::any();
        let b: u32 = kani::any();
        if a.checked_add(b).is_some() {
            let result = safe_add(a, b);
            assert!(result == a + b);
        }
    }
}

bash

cargo install --locked kani-verifier
cargo kani setup
cargo kani

Ideale per: funzioni critiche per la sicurezza, implementazioni di protocolli, calcoli aritmetici in codici finanziari o crittografici e qualsiasi funzione in cui sia necessario dimostrare, e non solo testare, una proprietà di correttezza.

MIRAI: Interpretazione astratta e analisi della contaminazione

MIRAI, sviluppato da Facebook Research, utilizza l'interpretazione astratta per analizzare il MIR di Rust. Esegue un'analisi interprocedurale che traccia il flusso dei valori attraverso le chiamate di funzione, rilevando potenziali panic, violazioni di contratto e propagazione di taint nel codice.

ruggine

use mirai_annotations::*;

fn divide(a: i32, b: i32) -> i32 {
    precondition!(b != 0);  // MIRAI verifies all callers satisfy this
    a / b
}

MIRAI richiede un compilatore Rust nightly e presenta una curva di configurazione ripida, il che lo rende più adatto a team con competenze dedicate di ingegneria della sicurezza o con requisiti di qualità orientati alla ricerca.

Rudra: Sicurezza della memoria nel codice di libreria non sicuro

Rudra esegue un'analisi completa del programma su diverse librerie per rilevare bug di sicurezza della memoria specifici del modello di proprietà di Rust: violazioni della sicurezza dei trait Send/Sync, problemi di durata di tipo superiore che consentono estensioni non corrette della durata e problemi di sicurezza in caso di panic in codice non sicuro. Originariamente sviluppato da ricercatori, funziona come plugin del compilatore e richiede una specifica versione nightly. Eseguilo a intervalli regolari per le librerie con componenti interni non sicuri, anziché a ogni commit.

Creusot e Prusti: Verifica deduttiva

Creusot e Prusti sono strumenti di verifica deduttiva che dimostrano le proprietà matematiche dei programmi Rust. Entrambi richiedono specifiche formali (precondizioni, postcondizioni, invarianti di ciclo) e utilizzano dimostratori automatici di teoremi per verificare le implementazioni rispetto a tali specifiche. Si rivolgono a un sottoinsieme ristretto di codice Rust sicuro e richiedono competenze in metodi formali. Sono adatti per settori regolamentati, sviluppo di librerie crittografiche o qualsiasi contesto in cui la dimostrazione matematica della correttezza sia un requisito contrattuale o normativo.

Strumenti aggiuntivi che vale la pena includere

Semgrep per modelli di sicurezza personalizzati

Semgrep offre un'analisi statica basata su pattern che funziona simultaneamente su più linguaggi di programmazione. Per Rust, consente ai team di scrivere regole personalizzate che applicano le policy di sicurezza aziendali e i pattern specifici del codice sorgente, cosa che Clippy non è in grado di fare.

YAML

# Custom rule: flag .unwrap() in production paths
rules:
  - id: no-unwrap-in-production
    pattern: $X.unwrap()
    message: "Use proper error handling instead of .unwrap()"
    languages: [rust]
    severity: WARNING

Semgrep offre anche un registro di regole gestito con regole di sicurezza Rust mantenute dalla community.

SonarCloud / SonarQube per il controllo continuo della qualità

SonarCloud offre un'analisi continua della qualità del codice con dashboard, monitoraggio delle tendenze e decorazione delle pull request. Il supporto per Rust copre i modelli di bug più comuni, i punti critici di sicurezza e i "code smell" (cattive pratiche di programmazione). Per i team aziendali che già utilizzano SonarCloud per altri linguaggi, l'aggiunta di Rust alla stessa pipeline offre una visibilità unificata della qualità su tutta la pila tecnologica.

Creazione di una pipeline CI/CD a livelli

Il seguente flusso di lavoro di GitHub Actions rappresenta un punto di partenza pratico per i team che desiderano una copertura di qualità completa di Rust senza ricorrere a soluzioni eccessivamente complesse:

YAML

# .github/workflows/quality.yml
name: Code Quality and Security

on: [push, pull_request]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy, rustfmt

      - name: Check formatting
        run: cargo fmt -- --check

      - name: Clippy (treat warnings as errors)
        run: cargo clippy -- -D warnings

  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable

      - name: Install security tools
        run: |
          cargo install cargo-audit
          cargo install cargo-deny

      - name: Dependency audit
        run: cargo audit --deny warnings

      - name: License and policy check
        run: cargo deny check

  verify:
    runs-on: ubuntu-latest
    # Scheduled or manually triggered, not on every push
    if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@nightly
        with:
          components: miri

      - name: Miri on unsafe modules
        run: cargo +nightly miri test --package my-unsafe-crate

Quale strumento dovresti usare? Guida alla decisione

Avvio di un nuovo progetto Rust : Clippy + rustfmt + cargo-audit. Configurabile in meno di cinque minuti, individua la maggior parte dei problemi di qualità e sicurezza riscontrati dai team.

Servizio di produzione con dipendenze esterne : aggiunta della funzionalità di negazione del carico per la conformità delle licenze e l'applicazione delle politiche della catena di approvvigionamento oltre le CVE.

Libreria con componenti interni non sicuri : aggiungi Miri alla tua suite di test per i moduli contenenti codice non sicuro. Esegui Rudra a intervalli programmati.

Codice critico per la sicurezza o crittografico : Scrivere framework di verifica Kani per le funzioni più critiche. Valutare Creusot o Prusti se il team possiede competenze in metodi formali.

Organizzazione focalizzata sulla sicurezza : aggiungi Semgrep con regole personalizzate per le politiche di sicurezza aziendali, oltre a cargo-auditable nelle build di rilascio.

Visibilità continua per un team più ampio : SonarCloud per il monitoraggio delle tendenze, la decorazione delle pull request e i controlli di qualità, in abbinamento agli strumenti open source.

Ambiente aziendale poliglotta Quando Rust interagisce con COBOL, Java o altri linguaggi legacy, gli strumenti Rust monolingue non sono in grado di vedere oltre i confini del linguaggio. SMART TS XL fornisce un'analisi interlinguistica che copre l'intero sistema.

Quando l'analisi statica di Rust incontra i sistemi aziendali

Rust viene sempre più utilizzato per nuovi servizi in organizzazioni che utilizzano anche COBOL, Java, PL/I o RPG su mainframe o piattaforme legacy. In questi ambienti, il quadro dell'analisi statica si estende oltre ciò che un qualsiasi strumento specifico per Rust è in grado di rilevare. Un microservizio Rust può richiamare un programma COBOL, scrivere su un database condiviso o elaborare eventi prodotti da un sistema batch legacy. L'analisi della qualità e delle dipendenze, rilevante per un architetto aziendale, si estende simultaneamente a tutti questi sistemi.

SMART TS XL colma questa lacuna fornendo un'analisi delle dipendenze tra linguaggi diversi sull'intero portfolio di applicazioni. Dove Clippy e cargo-audit vedono solo Rust, SMART TS XL mappa come un servizio Rust dipende da strutture dati condivise, programmi legacy e API aziendali e quale sarebbe l'impatto della modifica di una qualsiasi di queste dipendenze. Questo lo rende lo strumento di scelta per analisi d'impatto and pianificazione della modernizzazione del patrimonio in ambienti in cui Rust è uno dei tanti linguaggi di programmazione.

Per i team che adottano Rust in modo incrementale all'interno di una codebase aziendale più ampia, SMART TS XL'S analisi statica del codice fornisce la visibilità strutturale necessaria per capire come i componenti Rust si inseriscono nel sistema più ampio prima di apportare modifiche che potrebbero influenzare i componenti scritti in altri linguaggi. Come esaminato nel contesto di grafici di dipendenza e rischio applicativoMappare l'intera struttura delle dipendenze prima di estendere un sistema è ciò che distingue le modifiche che procedono senza intoppi da quelle che producono errori in componenti che nessuno ha pensato di testare.

L'approccio a strati è il punto

L'analisi statica in Rust non si basa sulla scelta di un singolo strumento. È una disciplina a più livelli che parte dagli strumenti forniti gratuitamente dal compilatore e si estende in base al profilo di rischio del codice. Clippy e rustfmt rappresentano il livello base. cargo-audit è imprescindibile per qualsiasi progetto con dipendenze esterne. Miri è indispensabile in qualsiasi progetto con codice non sicuro. Kani e gli strumenti di verifica formale sono indicati per le funzioni in cui commettere errori è inaccettabile.

I team che riescono a fare le cose per bene integrano l'analisi statica nel ciclo di sviluppo: Clippy direttamente nell'editor, cargo-audit in ogni esecuzione di CI e Miri in un job notturno che individua ciò che i soli test non riescono a rilevare. Il risultato non è solo un minor numero di bug. È quel tipo di fiducia nel codice sorgente che permette ai team di apportare modifiche rapidamente, sapendo che i livelli di analisi tra lo sviluppatore e la produzione funzionano correttamente.