Come si popola il dependency graph
Il dependency graph è la base di tutta la Supply Chain Security di GitHub: senza di esso non esistono né gli alert di Dependabot, né la dependency review, né l’SBOM. Si popola in due modi. Il primo è l’analisi statica: GitHub legge i manifest file e i lock file presenti nel repository e ne ricava l’elenco dei pacchetti con versione, licenza, file che li ha introdotti e stato di vulnerabilità. Il secondo è la dependency submission API, con cui si inviano dipendenze che l’analisi statica non riesce a vedere.
La distinzione che conta è fra dipendenze dirette, dichiarate esplicitamente in un manifest o in un lock file, e dipendenze indirette o transitive, cioè le dipendenze delle dirette. Il grafo riconosce le transitive solo se compaiono in un file di manifest o di lock: quelle risolte al momento della build restano fuori. E c’è un secondo livello che sfugge quasi sempre: le dipendenze indirette dedotte da un manifest, e non da un lock file, sono escluse dal controllo delle vulnerabilità. Tradotto in pratica, un progetto che pubblica solo il manifest senza versioni bloccate produce un grafo più povero e meno alert, il che non significa affatto più sicuro. Per questo la documentazione, ecosistema per ecosistema, indica una colonna di file consigliati: sono i formati che fissano le versioni di tutte le dipendenze, dirette e indirette, e che permettono a Dependabot di trovare versioni vulnerabili anche in profondità.
Nel repository il grafo si consulta da Insights, poi Dependency graph, con la scheda Dependencies per ciò che il progetto usa e la scheda Dependents per i repository che dipendono dal progetto, visibile sui repository pubblici. Il menu accanto a una voce offre Show paths, che ricostruisce la catena attraverso cui una transitiva è entrata; la barra di ricerca filtra con ecosystem: e con relationship:, quest’ultimo con i valori direct, transitive e inconclusive.
Quando l’analisi statica non basta
Gli ecosistemi in cui l’albero delle dipendenze si risolve solo compilando sono il caso limite del grafo statico. La dependency submission API serve esattamente a questo: accetta uno snapshot dell’albero risolto e lo aggiunge al grafo come se fosse stato letto da un file. Di norma non la si chiama a mano, si usa una action pubblicata per il proprio ecosistema dentro un job che compila davvero il progetto, e le si concede il permesso di scrittura sui contenuti.
jobs:
submit-dependencies:
runs-on: ubuntu-latest
permissions:
contents: write # necessario per inviare lo snapshot al grafo
steps:
- uses: actions/checkout@v4
# action di dependency submission dell'ecosistema in uso
Un dettaglio di copertura da ricordare: le dipendenze inviate per API compaiono nella dependency review, ma non nelle dependency insights dell’organizzazione.
Reviewed, unreviewed e malware nel GitHub Advisory Database
Il grafo dice che cosa hai; il GitHub Advisory Database dice che cosa c’è di noto su quel che hai. Contiene tre tipi di record, e la differenza fra loro è operativa, non accademica.
Gli advisory reviewed sono verificati dal personale di GitHub, con descrizione completa e mappatura a un ecosistema e a un pacchetto specifici: sono gli unici advisory di vulnerabilità che fanno scattare un alert di Dependabot. Gli advisory unreviewed arrivano dal National Vulnerability Database senza passare per quella verifica: non essendo controllati per validità e completezza né legati a un pacchetto di un ecosistema supportato, non generano alcun alert. Restano consultabili e ricercabili nel database, ma non raggiungono il repository. Gli advisory malware riguardano pacchetti segnalati come malevoli, oggi sull’ecosistema npm, e un alert lo generano: la differenza rispetto a una vulnerabilità è che non esiste una versione sana verso cui aggiornare, quindi l’unica risposta è smettere di usare il pacchetto.
Sulla priorità agiscono due scale che vanno tenute separate. CVSS misura la gravità tecnica e produce le etichette Low, Moderate, High e Critical. EPSS stima la probabilità che quella vulnerabilità venga sfruttata nel mondo reale, con un punteggio compreso fra 0 e 1. Un CVSS critico con EPSS bassissimo e un CVSS medio con EPSS alto raccontano due storie diverse: l’esame usa spesso proprio questa coppia per verificare che si capisca la differenza fra quanto fa male e quanto è probabile che accada.
Esportare l’SBOM
Dalla scheda Dependencies il pulsante Export SBOM produce un SBOM, cioè un inventario formale e leggibile da una macchina delle dipendenze del progetto, in formato SPDX versione 2.3, scaricabile dal browser; esiste anche l’endpoint REST corrispondente per automatizzare l’estrazione. Il file riporta versioni, identificatori di pacchetto, licenze, percorsi transitivi e informazioni di copyright, mentre non include i dependents. Se serve un formato diverso, per esempio CycloneDX, o un SBOM prodotto durante la build, si ricorre ad apposite action che lo allegano come artefatto del workflow.
Nella catena di fornitura l’SBOM è il documento che si consegna a chi lo chiede: audit, compliance, clausole contrattuali, o la domanda del cliente su quale versione di una libreria appena finita sui giornali sia presente nei tuoi prodotti. La confusione tipica da evitare, e che l’esame sfrutta, è scambiarlo per un rapporto di vulnerabilità: l’SBOM è un inventario, le vulnerabilità stanno negli alert.
Un ultimo punto sulle licenze, perché è terreno d’esame: dependency graph, Dependabot alerts e security updates ed export dell’SBOM sono disponibili su tutti i piani, anche su repository privati. È la dependency review a essere gratuita sui repository pubblici e a richiedere GitHub Code Security su privati e internal.