Le query suite di CodeQL, e il prezzo di allargarle

GitHub Code Security — il prodotto che analizza il codice con CodeQL, quello che storicamente si chiamava code scanning — non esegue tutto quello che CodeQL sa fare. Esegue una query suite. La suite default contiene query molto precise, pensate per restituire pochissimi falsi positivi: è la scelta che regge senza presidio. La suite security-extended comprende tutte le query della default più altre query con precisione e severità leggermente inferiori, e proprio per questo può produrre un numero maggiore di risultati falsi positivi. Allargare quindi non è mai gratis: si paga in tempo di analisi e, soprattutto, in ore di triage di un team che deve dimostrare che un alert non è reale.

Oltre alle suite predefinite si può scendere di un livello. Una custom query è un file .ql con metadati obbligatori come @id e @kind, utile per pattern specifici del proprio codice o per far rispettare standard interni; le query si distribuiscono come CodeQL pack pubblicati. I model pack servono invece a un problema diverso: estendere la copertura dell’analisi a framework e librerie che CodeQL non modella già.

# .github/codeql/codeql-config.yml
disable-default-queries: false
queries:
  - uses: security-extended
packs:
  - my-org/my-queries@1.2.3
paths-ignore:
  - '**/*.test.js'

Su scala di organizzazione non si tocca ogni repository a mano: in Settings > Advanced Security > Global settings si può raccomandare la suite estesa, abilitare Copilot Autofix e i model pack per tutti; e con la repository property github-codeql-config-file si applica un file di configurazione senza mantenere un workflow per repo. Confusione tipica che l’esame sfrutta: default setup è il percorso rapido, personalizzabile centralmente, mentre l’advanced setup è il workflow che si controlla riga per riga. Se la domanda chiede dove si iniettano query proprie, la risposta guarda a queste due strade, non alla schermata del singolo alert.

Adattare il rilevamento al profilo di rischio

Il secondo asse è che cosa cercare, e qui vanno tenute separate due cose che l’esame ama scambiare. Una security configuration è una collezione di interruttori di abilitazione che si applica ai repository: dice dove una funzionalità è accesa. I global settings dell’organizzazione dicono invece come le funzionalità analizzano, e sono ereditati da tutti i repository. Dentro i global settings vivono i custom pattern di GitHub Secret Protection e le auto-triage rule di Dependabot. Nota di governance: una configurazione non applicata in modalità enforced può essere sovrascritta dagli amministratori di repository.

I custom pattern sono espressioni regolari definibili a livello di repository, organizzazione o enterprise, e su ciascuno si può attivare la push protection. Le auto-triage rule chiudono da sole il rumore: fra i preset di GitHub ci sono la chiusura degli alert a basso impatto sulle dipendenze di sviluppo, attivo per impostazione predefinita sui repository pubblici, e la chiusura degli alert malware di pacchetto, disattivato per impostazione predefinita. Le regole personalizzate lavorano su metadati come severità, nome del pacchetto e CWE. Un alert chiuso automaticamente resta consultabile e può riaprirsi se le condizioni cambiano, per esempio se una dipendenza passa da sviluppo a produzione.

Per prioritizzare non basta la severità. CVSS esprime la gravità intrinseca della vulnerabilità; EPSS esprime la probabilità che venga sfruttata nei trenta giorni successivi, in percentuale, ed è filtrabile con epss_percentage. Una criticità mai sfruttata può aspettare più di una media già attivamente usata. Sul GitHub Advisory Database vale poi una distinzione netta: gli advisory reviewed generano alert, quelli malware generano alert quando la funzionalità è configurata, gli unreviewed non generano alert Dependabot. Infine il perimetro economico: sui repository pubblici la scansione dei segreti e la dependency review action sono gratuite, mentre su repository privati e internal servono le licenze di GitHub Secret Protection e GitHub Code Security.

Ruoli, eccezioni delegate e proprietà degli alert

Le due eccezioni delegate stanno agli estremi opposti del ciclo di vita. Il delegated bypass riguarda l’ingresso: chi non è nella bypass list — team, ruoli predefiniti o ruoli personalizzati — non può pubblicare un push bloccato, ma deve chiedere l’approvazione a chi ha il permesso granulare di revisionare e gestire le richieste di bypass. Esiste anche la possibilità di concedere esenzioni ad attori specifici, che saltano del tutto la push protection. La delegated alert dismissal riguarda invece l’uscita: chi può chiudere un alert. Si abilita a livello di organizzazione, copre alert di code scanning, secret scanning e Dependabot, e le richieste sono approvate o negate dai proprietari dell’organizzazione e dai security manager, oppure da ruoli personalizzati a cui il permesso è stato assegnato. Se la richiesta è negata, l’alert resta aperto.

La proprietà si esprime anche più semplicemente: gli alert si assegnano e si filtrano per assignee. Sopra c’è la security campaign, che raggruppa alert affini, nomina un campaign manager come punto di contatto e attiva Copilot Autofix sugli alert di code scanning: è lo strumento con cui la sicurezza e gli sviluppatori condividono la responsabilità invece di scambiarsi ticket.

Spostare i controlli il più a sinistra possibile

Il filo che tiene insieme il dominio è economico: correggere dopo costa più che impedire prima. La push protection blocca il segreto prima che entri nella storia del repository, da riga di comando, dall’interfaccia web, dal caricamento file e dalle API REST; le motivazioni di bypass non sono equivalenti, perché usato nei test e falso positivo producono un alert già chiuso, mentre lo sistemo dopo lascia un alert aperto da presidiare. La dependency review mostra il diff delle dipendenze nella pull request e, con la relativa action, blocca il merge secondo fail-on-severity, liste di licenze consentite o negate e fail-on-scopes. Il livello superiore sono i ruleset: una regola di merge protection impedisce il merge quando uno strumento richiesto trova alert della severità definita, quando l’analisi non è completa o quando lo strumento non è configurato sul repository. Attenzione, non è un controllo di stato e non si applica ai gruppi di merge queue né alle pull request di Dependabot analizzate dal default setup.