Automatizzare lo scanning di sicurezza non significa “accendere tutti i tool disponibili”. Su AZ-400 la domanda ha sempre la stessa forma: dato questo requisito e questo vincolo operativo, quale tipo di scan, in quale punto della pipeline, con quale livello di enforcement. Diamo per acquisiti i meccanismi di base e concentriamoci sulle decisioni di design e sui trade-off.

Quattro classi di segnale, quattro punti di enforcement diversi

Una strategia coerente distingue quattro famiglie di findings, perché hanno tempi di rilevamento e costi di remediation diversi.

  • Secret: un secret esposto va bloccato prima che entri nella history, non segnalato dopo. È l’unico caso in cui l’enforcement sincrono e bloccante sul push è la scelta corretta.
  • Codice applicativo (SAST): l’analisi costa tempo di build, quindi va collocata dove il feedback è ancora utile ma non blocca ogni push: pull request e schedule ricorrenti.
  • Dipendenze open source (SCA): il segnale è asincrono per natura. Una libreria oggi pulita diventa vulnerabile domani quando esce l’advisory, senza che il tuo codice sia cambiato: serve un meccanismo che rivaluti l’inventario in continuo, non solo alla build.
  • Licenze: non è sicurezza ma compliance legale, e la valutazione utile è differenziale. Interessa la licenza che stai introducendo con questa pull request, non l’intero albero già accettato.

Il trade-off dominante è shift-left contro attrito: rendere tutto bloccante produce pipeline lente e team che imparano a bypassare i gate. La regola di design è blocca in modo sincrono solo dove la remediation post-hoc è sproporzionatamente costosa (i secret), usa gate di merge dove il fix è ancora economico (PR), lascia asincrono ciò che dipende da eventi esterni al codice (advisory sulle dipendenze).

GitHub Advanced Security e CodeQL: stessa suite, due piattaforme

Advanced Security oggi non è più un blocco monolitico: è confezionato come GitHub Secret Protection e GitHub Code Security, acquistabili separatamente. Secret Protection copre tra l’altro secret scanning, push protection, pattern personalizzati e security overview; Code Security copre tra l’altro code scanning, Copilot Autofix, dependency review e le regole di auto-triage per Dependabot. La decisione che ne consegue è concreta: se il requisito è “impedire la fuga di credenziali su tutti i repo, incluso il legacy che non compiliamo più”, Secret Protection da solo è la risposta corretta e la più economica.

Lo stesso split esiste su Azure DevOps con GitHub Advanced Security for Azure DevOps. Due vincoli pesano sul design:

Solo Azure DevOps Services e solo repository Git. Se lo scenario cita Azure DevOps Server on-premises o repository TFVC, GHAzDO non è la risposta: la strada è integrare tool di terze parti e pubblicarne i risultati.

Enablement e billing a livelli. Puoi abilitare a livello di organizzazione, progetto o singolo repository, con conteggio basato sugli active committer. Su un’organizzazione grande la decisione non è “on/off” ma dove. Il compromesso tipico è l’attivazione a livello di progetto più l’opzione di abilitazione automatica sui nuovi repository, così la copertura non degrada nel tempo.

Sul secret scanning esiste una asimmetria che vale la pena memorizzare: la push protection valuta solo i push successivi all’attivazione, mentre lo scanning del repository rileva anche i secret già presenti nella history. Se il requisito è “trovare le credenziali committate l’anno scorso”, la push protection non serve a nulla.

Per l’enforcement sul merge, GHAzDO espone status check da usare come branch policy: AdvancedSecurity/AllHighAndCritical impone che tutti gli alert critici e high del repository siano risolti, AdvancedSecurity/NewHighAndCritical blocca solo le vulnerabilità nuove. Su codebase esistenti la scelta è quasi sempre la seconda: la prima trasforma l’adozione in un big-bang di remediation.

CodeQL: default setup contro advanced setup. Su Azure DevOps il code scanning offre due modalità. Il default setup rileva automaticamente i linguaggi, si riconfigura da solo quando cambiano e gira su schedule tramite Azure Pipelines: costo di adozione minimo, controllo minimo. L’advanced setup aggiunge le task CodeQL direttamente nelle pipeline e serve quando ti servono agent pool dedicati, step di build custom per linguaggi compilati o scansione su più branch. Le due modalità convivono nella stessa organizzazione, ed è il pattern giusto: default setup come baseline diffusa, advanced setup sui repository critici.

Attenzione a un limite di copertura: il default setup supporta tra gli altri C#, C/C++, Java, JavaScript/TypeScript, Python, Ruby e Rust, ma Go e Swift richiedono l’advanced setup. Un requisito che nomina un servizio scritto in Go decide la risposta da solo.

Il caso container è il classico distrattore d’esame. Sui linguaggi compilati CodeQL deve osservare la compilazione: se la build avviene dentro un container e CodeQL gira fuori, l’analisi fallisce con l’errore che nessun sorgente è stato visto durante la build. La soluzione è eseguire tutte le action nello stesso container in cui il codice viene compilato, con un’immagine che contenga le dipendenze necessarie a partire da Git. Un vincolo va ricordato perché contraddice l’istinto di usare immagini minimali: la CLI di CodeQL non è compatibile con distribuzioni Linux non-glibc come Alpine, basata su musl.

Dipendenze e licenze: due problemi diversi, due strumenti diversi

I Dependabot alert nascono dal GitHub Advisory Database e si attivano sia quando cambia il tuo dependency graph sia quando viene pubblicato un nuovo advisory su una libreria che già usavi: è il meccanismo asincrono di cui parlavamo. Richiedono il dependency graph abilitato, considerano solo gli advisory revisionati da GitHub e non coprono i repository archiviati.

Gli alert però non rimediano. I Dependabot security update aprono automaticamente pull request che portano la dipendenza alla versione minima che include il fix, per ogni alert aperto con patch disponibile. Il trade-off è il volume: su repository con molte dipendenze il rumore diventa insostenibile, ed è lì che le auto-triage rule servono a filtrare invece di disattivare tutto. Un limite da conoscere: solo alcuni ecosistemi, npm fra questi, sanno aggiornare una dipendenza indiretta; altrove, se il fix richiede di aggiornare anche il parent, Dependabot non procede.

Per le licenze lo strumento è la dependency review, che al momento della pull request confronta le dipendenze aggiunte, rimosse o aggiornate e permette di configurare liste allow o deny di licenze tramite la relativa action. È il controllo differenziale che serve alla compliance: non una revisione retroattiva dell’intero albero, ma un gate contro l’ingresso di nuove licenze non conformi. Confronta solo il diff rispetto al branch di destinazione, e sui repository privati richiede Code Security o Advanced Security.

Defender for Cloud DevOps Security: da findings sparsi a posture unica

Tutto quanto sopra produce alert dentro la piattaforma di sviluppo. Quando il requisito parla di security team, visibilità centralizzata o ambienti multi-piattaforma, la risposta cambia livello: Microsoft Defender for Cloud DevOps Security connette ambienti Azure DevOps, GitHub e GitLab e aggrega in una console unica i findings di codice, secret, dipendenze e infrastructure as code, più le raccomandazioni di DevOps posture management sulla configurazione degli ambienti stessi.

Il valore architetturale non è duplicare lo scanning ma la correlazione code-to-cloud: legare una vulnerabilità nel repository alla risorsa cloud che ne deriva, per prioritizzare in base all’esposizione reale, e riportare il segnale allo sviluppatore tramite pull request annotation. Chiave di lettura per l’esame: se lo scenario chiede rilevamento, la risposta è GHAS o Dependabot; se chiede visibilità unificata e prioritizzazione per il team di sicurezza, la risposta è Defender for Cloud.

Trappole tipiche d’esame

  • “Impedire che nuove credenziali finiscano nel repository, con effetto immediato”push protection del secret scanning; non lo scanning del repository, che rileva ma non blocca.
  • “Trovare le credenziali già committate mesi fa”secret scanning del repository, che analizza anche i commit storici; la push protection valuta solo i push successivi all’attivazione.
  • “Il progetto è in Go e serve analisi statica su Azure DevOps” → CodeQL in advanced setup; il default setup non copre Go.
  • “La build gira in un container e CodeQL non vede sorgenti” → eseguire tutte le action nello stesso container della build, evitando immagini non-glibc come Alpine.
  • “Bloccare il merge solo sulle vulnerabilità introdotte dalla PR, senza fermare i team sul debito esistente” → status check AdvancedSecurity/NewHighAndCritical come branch policy, non AllHighAndCritical.
  • “Vietare l’ingresso di dipendenze con licenze copyleft non ammesse”dependency review con deny list di licenze; non Dependabot, che si occupa di vulnerabilità.
  • “Aggiornare automaticamente le librerie vulnerabili non appena esce la patch”Dependabot security update, con auto-triage rule per contenere il volume delle PR.
  • “Il security team deve vedere in un unico punto i findings di repository su GitHub e su Azure DevOps, e capire quali toccano risorse esposte”Defender for Cloud DevOps Security con i relativi connettori; non una dashboard per piattaforma.