Torna a tutti gli articoli
Release

Il SAST per Rust è teatro. Abbiamo spedito quello vero.

Gran parte del SAST per Rust è pattern matching: tanti falsi positivi, bug reali mancati. Ecco perché il dataflow rafforza la sicurezza di Rust.

In questa pagina
  1. Perché Rust merita una vera scansione di sicurezza
  2. Perché gran parte del SAST per Rust è teatro di pattern matching
  3. Dataflow contro pattern matching, in un esempio
  4. Cosa intercetta un ruleset per Rust consapevole del flusso di dati
  5. Dentro il loop dell'agente, non solo nella dashboard
  6. Domande frequenti
  7. La memory safety di Rust basta a prevenire le vulnerabilità?
  8. La SQL injection può accadere in Rust con sqlx o diesel?
  9. Perché gli strumenti SAST per Rust producono così tanti falsi positivi?
  10. Cos'è l'analisi del flusso di dati (taint analysis)?
  11. La scansione del flusso di dati funziona dentro gli agenti di coding AI?

Tracciamento del taint tramite dataflow in Rust: l'input non attendibile scorre da una sorgente Query fino a un sink SQL di sqlx passando per format! e QueryBuilder, proprio dove gli scanner a pattern smettono di guardare.

Oggi Rust in produzione manda avanti internet, eppure gran parte degli strumenti di analisi statica continua a trattarlo come una casella da spuntare. Hanno aggiunto *.rs al file glob, pubblicato un elenco di nomi di funzioni pericolose e l'hanno chiamato supporto a Rust. Il lavoro vero, seguire l'input non attendibile attraverso funzioni async, connessioni a database generiche e helper derivati da macro fino a quando raggiunge una query SQL o una risposta HTML, è rimasto incompiuto. Ed è esattamente lì che vivono le vulnerabilità. Ecco perché il pattern matching fallisce su Rust e cosa intercetta invece l'analisi del flusso di dati.

Perché Rust merita una vera scansione di sicurezza

Rust ha smesso anni fa di essere una curiosità solo per i sistemi. Cloudflare serve una larga parte del web tramite Pingora. Discord ha riscritto in Rust i servizi critici per le prestazioni. AWS distribuisce Firecracker e Bottlerocket in Rust, Stripe lo usa nei percorsi di pagamento, e l'elenco (Linkerd, Polkadot, Solana, Tauri) cresce ogni mese. È codice che gestisce input non attendibile, denaro e credenziali su larga scala.

Il mercato dell'analisi statica ha risposto per lo più aggiungendo Rust a un glob e passando oltre. Il risultato sono strumenti che o inondano i team di falsi positivi su righe che sono ovviamente sicure, oppure tralasciano in silenzio le righe che non lo sono. In entrambi i casi il report smette di essere affidabile, lo scanner viene disattivato nella CI e il linguaggio che doveva essere più sicuro finisce con una copertura di sicurezza inferiore al servizio JavaScript che gli sta accanto. La memory safety non è sicurezza applicativa: il borrow checker non ti impedirà di concatenare input utente in una stringa SQL.

Perché gran parte del SAST per Rust è teatro di pattern matching

L'analisi statica su Rust è davvero difficile, per ragioni che non compaiono nel marketing dei vendor. Uno scanner che si limita a confrontare pattern testuali sbatte contro cinque muri tutti insieme.

Metti tutto questo insieme e Rust diventa un linguaggio in cui il pattern matching è rumoroso dove dovrebbe stare zitto e silenzioso dove dovrebbe gridare. La soluzione non sono altri pattern. È il dataflow.

Dataflow contro pattern matching, in un esempio

Ecco il tipo di bug che separa i due approcci. Usa sqlx, lo standard async moderno, e il suo QueryBuilder.

use sqlx::{PgPool, QueryBuilder, Postgres};

pub async fn search(pool: &PgPool, input: SearchInput) -> Result<Vec<Item>, sqlx::Error> {
    let mut q: QueryBuilder<Postgres> =
        QueryBuilder::new("SELECT * FROM items WHERE 1 = 1");

    if let Some(name) = input.name {
        // .push_bind() is safe (parameterized). .push() is concatenation.
        // This is direct SQL injection.
        q.push(" AND name LIKE '%").push(&name).push("%'");
    }

    q.build_query_as::<Item>().fetch_all(pool).await
}

Uno scanner basato su pattern che cerca format! accanto a sqlx::query qui non trova nulla. Non c'è format!, non c'è un operatore di concatenazione di stringhe, non c'è alcun sink evidente. Il taint scorre attraverso QueryBuilder::push, una funzione perfettamente sicura con una stringa costante e pericolosa con input utente. Lo scanner non riesce a cogliere la differenza perché non segue il valore, quindi consegna un report pulito su un file con una SQL injection reale.

Un motore di dataflow traccia il taint da input.name (deserializzato dalla richiesta da serde e axum) attraverso la chiamata .push(&name), riconosce QueryBuilder::push come sink di testo SQL e lo segnala con il percorso completo. La correzione è un solo metodo:

// .push_bind() keeps the value out of the SQL text.
q.push(" AND name LIKE ").push_bind(format!("%{}%", name));

Stessa forma, proprietà di sicurezza completamente diversa. Solo un'analisi che segue i dati riesce a distinguerli.

Cosa intercetta un ruleset per Rust consapevole del flusso di dati

Il ruleset per Rust di CybeDefend è costruito su questo modello, con modelli di sink specifici per libreria, così il taint non si perde quando attraversa un confine di API. Ogni segnalazione arriva con il percorso completo: posizione della sorgente, ogni passaggio intermedio e il sink.

4

driver SQL tracciati: sqlx, diesel, rusqlite, tokio-postgres

3

framework web per l'XSS: actix-web, axum, rocket

4

client HTTP async per l'SSRF: reqwest, ureq, hyper, surf

  • SQL injection attraverso i quattro driver principali, seguendo l'input utente attraverso funzioni async, tipi di connessione generici e helper di query derivati da macro, non solo i casi evidenti con format!.
  • Cross-site scripting (XSS) sui tre framework di riferimento, inclusi gli HTML prodotti tramite i template engine askama e tera, normalizzati in un unico modello di sink di risposta.
  • Configurazione errata del CORS, come un Access-Control-Allow-Origin con wildcard combinato a richieste con credenziali, oppure un'origine riflessa dalla richiesta senza un'allowlist, intercettata al momento della costruzione.
  • Server-side request forgery (SSRF) nei client HTTP async, con il taint seguito attraverso ogni helper costruttore di URL e fermato quando un'allowlist di host o un filtro IP è presente sul percorso.

Il senso di tracciare i sanitizzatori con la stessa cura dei sink è che una segnalazione scatta solo quando il percorso pericoloso è davvero raggiungibile senza alcuna protezione in mezzo. È questo che mantiene il report abbastanza affidabile da poterlo lasciare attivo nella CI.

Dentro il loop dell'agente, non solo nella dashboard

Le regole per Rust non sono solo un report di CI. Vengono inserite nel tuo agente di coding AI tramite VibeDefend, il layer che gira dentro Claude Code, Cursor, OpenAI Codex, Windsurf e VS Code Copilot. Quando l'agente genera Rust, il ruleset è già caricato come contesto, così un percorso contaminato verso uno dei sink qui sopra viene riscritto ancora prima che la riga ti venga suggerita.

npx -y @cybedefend/vibedefend@latest installRilevati: Claude Code, Cursor, CodexRuleset per Rust caricato come contestoQuery parametrizzata al primo tentativo
Il ruleset per Rust, caricato nell'agente al momento del prompt.

Questa è la differenza tra intercettare un bug in fase di revisione della pull request, dopo che l'agente ha consegnato 2.000 righe che nessuno ha letto dall'inizio alla fine, e non scriverlo affatto. Non fermiamo il diff alla revisione. Lo fermiamo al prompt.

Domande frequenti

La memory safety di Rust basta a prevenire le vulnerabilità?

No. Il borrow checker di Rust previene i bug di corruzione della memoria (use-after-free, buffer overflow, data race), che è una classe ampia e di grande valore. Non fa nulla per le vulnerabilità a livello applicativo. SQL injection, XSS, configurazione errata del CORS, SSRF, broken access control e deserializzazione insicura accadono tutti in Rust sicuro, perché riguardano il modo in cui i dati non attendibili scorrono attraverso la tua logica, non la memoria. Un linguaggio memory-safe ha comunque bisogno di una scansione di sicurezza applicativa.

La SQL injection può accadere in Rust con sqlx o diesel?

Sì. Entrambe le librerie offrono API sicure e parametrizzate (le macro verificate a compile-time di sqlx e .bind(), il DSL tipizzato di Diesel), ma entrambe espongono anche delle scappatoie: sqlx::query(&str) e QueryBuilder::push accettano stringhe a runtime, e diesel::sql_query esegue SQL grezzo. Nel momento in cui l'input utente viene concatenato in una di queste, hai un'injection, per quanto moderno sia il driver. Lo stesso vale per rusqlite e tokio-postgres.

Perché gli strumenti SAST per Rust producono così tanti falsi positivi?

Perché la maggior parte di essi fa pattern matching su nomi di funzioni e forme di stringhe invece di seguire i dati. I lifetime, le catene async, i generics, le macro derivate e i trait object di Rust nascondono il percorso reale dei dati, quindi uno scanner basato su pattern segnala ogni chiamata che assomiglia a un sink (sommergendo il team di rumore) oppure salta del tutto la regola (mancando bug reali). L'analisi del flusso di dati riduce il rumore segnalando solo i percorsi effettivamente raggiungibili da una sorgente non attendibile senza alcun sanitizzatore.

Cos'è l'analisi del flusso di dati (taint analysis)?

È un'analisi che modella un programma come un grafo di valori e si chiede, per ogni operazione pericolosa, se l'input non attendibile possa raggiungerla senza essere sanitizzato. I punti di ingresso sono le "sorgenti", le operazioni pericolose sono i "sink", e un percorso pulito tra loro senza protezione è una segnalazione reale e sfruttabile. A differenza del pattern matching, capisce che la stessa funzione può essere sicura o non sicura a seconda di quali dati la raggiungono.

La scansione del flusso di dati funziona dentro gli agenti di coding AI?

Sì. Tramite VibeDefend, il ruleset per Rust viene caricato nel contesto dell'agente al momento del prompt, così Claude Code, Cursor, Codex e gli altri lo applicano mentre scrivono anziché dopo. Se una modifica generata introducesse un percorso contaminato verso un sink SQL, HTML o HTTP, l'agente produce la versione sicura, parametrizzata o validata al primo tentativo, prima che il diff esista. Per il quadro più ampio su come mettere in sicurezza ciascun agente, consulta le nostre guide alla sicurezza di Claude Code, Cursor, GitHub Copilot e OpenAI Codex.

Live · appena rilasciato

Installa VibeDefend in 5 secondi.

Un solo comando collega ogni agente di coding sulla tua macchina a CybeDefend: le tue regole di business, i tuoi framework di compliance e guards che bloccano le chiamate distruttive prima che partano.

Installa in 5 secondiNode 18.17+
npx -y @cybedefend/vibedefend@latest install
Auto-rileva
  • Claude CodeClaude Code
  • CursorCursor
  • OpenAI CodexOpenAI Codex
  • WindsurfWindsurf
  • GitHub CopilotVS Code Copilot
Leggi il README su npm