# Supply chain PHP: scansione di 6.000+ advisory ogni notte

> Audit online di composer.json/lock per PHP: CVE con CVSS, framework EOL, pacchetti abbandonati. Dataset 6.000+ ricompilato ogni notte. Niente upload.

Strumento gratuito nel browser (nessun upload): https://www.mauriziofonte.it/strumenti/audit-composer

Incolla un **composer.json** o un **composer.lock** e ottieni in trenta secondi un audit security-first del manifest: **CVE attive** nelle versioni installate (con CVSS vector e CWE ID), **pacchetti abbandonati** con suggerimento del sostituto, **framework end-of-life** con data di fine security support, audit del **vincolo PHP**, rilevamento di constraint wildcard, dipendenze su dev branch, repository non Packagist, plugin Composer abilitati. Dataset di 6.000+ advisory ricompilato ogni notte da Packagist Security API, OSV.dev e endoflife.date. L'analisi gira interamente nel browser: il tuo manifest non lascia mai la pagina.

## Come usare l'audit

1. **Recupera composer.json o composer.lock** - Entrambi i file vivono nella radice del progetto PHP, accanto a `vendor/`. **composer.json** dichiara le dipendenze e i vincoli di versione (es. `"laravel/framework": "^11.0"`): è committato in git e visibile sul repository (GitHub, GitLab, Bitbucket). **composer.lock** è generato da `composer install/update` e contiene le versioni esatte risolte (es. `11.5.0`) più gli hash di integrità: anch'esso è committato in git per garantire build deterministiche fra dev e production. Per un audit accurato preferisci il `composer.lock`: il manifest dice cosa è ammesso, il lock dice cosa è stato installato davvero.
2. **Incolla nel tool** - Il tool fa auto-detect del tipo: la presenza di un array top-level `packages` identifica il lock, la presenza di un oggetto `require` con vincoli stringa identifica il json. Il parsing JSON è strict: errori di sintassi falliscono con messaggio diagnostico esplicito. L'input non lascia il browser: nessuna richiesta di rete porta con sé il tuo manifest, l'analisi gira interamente in JavaScript locale contro un dataset statico precaricato dal sito.
3. **Leggi il report** - Output a due livelli. **Statistiche**: numero pacchetti totali, breakdown require / require-dev, vincolo PHP, estensioni richieste. **Findings ordinati per severity** (critical, high, medium, low, info): per ogni rilievo trovi pacchetto coinvolto, motivazione, riferimento al CVE pubblico (cve.org) o al GitHub Security Advisory (GHSA), CVSS vector quando presente, lista CWE associate, constraint vulnerabili e versione installata, suggerimento di remediation. In alto: punteggio aggregato 0-100 (verde sopra 80, giallo 50-80, rosso sotto 50).
4. **Esegui poi l'audit reale (CLI)** - Il triage statico nel browser ti dà la fotografia oggi. Per integrare in pipeline e bloccare regressioni future: `composer audit` da terminale (comando ufficiale dal Composer 2.4, settembre 2022) confronta i pacchetti installati con il database CVE FriendsOfPHP. `composer require --dev roave/security-advisories:dev-latest` aggiunge un meta-pacchetto che blocca l'installazione di pacchetti con CVE noti (zero overhead a runtime, solo conflict declarations). Best practice: `composer audit --abandoned=report` dentro il job di test della tua CI/CD, exit code != 0 se ci sono CVE attive.

## Vulnerabilità note, EOL, abbandonati: i tre rischi reali della supply chain PHP

**Le applicazioni PHP non muoiono di vecchiaia, muoiono di manutenzione mancata.** In ogni progetto Composer non curato accadono tre cose in silenzio: nuove CVE vengono pubblicate sulle versioni effettivamente installate, autori smettono di mantenere librerie e marcano i pacchetti come `abandoned`, branch maggiori di framework escono dal security support. Dopo diciotto-ventiquattro mesi di immobilità, l'applicazione è formalmente funzionante ma esposta a vulnerabilità note e patchate da tempo nelle versioni nuove. Questo tool fa emergere quello stato in trenta secondi a partire dal solo manifest.

**Cosa controlla l'audit.** Tre fonti distinte alimentano il triage. (1) **Vulnerabilità note (CVE)**: l'API Packagist Security Advisory aggrega FriendsOfPHP/security-advisories e GitHub Advisory DB; OSV.dev (Open Source Vulnerabilities, gestito da Google) arricchisce ogni advisory con CVSS v3 vector e CWE ID. Coverage: 6.000+ advisory su 1.000+ pacchetti Composer. (2) **End-of-life (EOL)**: endoflife.date pubblica i cicli release dei framework principali con data di fine security support. Pacchetti monitorati: PHP, Laravel (e `illuminate/*`), Symfony (e ogni `symfony/*`), Composer stesso, Guzzle, Twig, CakePHP, Drupal, GrumPHP. (3) **Abandoned**: il flag `abandoned` di Packagist viene letto per l'unione di tutti i pacchetti citati nelle advisory e dei top 2.000 pacchetti popolari, con cache TTL di 7 giorni. Risultato: 210+ pacchetti effettivamente abbandonati nel dataset.

**Differenza fra audit del lock e audit del json.** Il `composer.lock` contiene le versioni esatte risolte: il matcher fa lookup esatto contro l'intervallo affected dell'advisory (es. `>=9.0.0,<9.5.6`). Il `composer.json` contiene solo i constraint dichiarati (es. `^9.0`): il matcher esegue intersezione fra il constraint utente e l'intervallo affected, segnalando se esiste almeno una versione installabile vulnerabile. Per audit rigorosi usa il lock. Il json è utile per stabilire *se il vincolo permetterebbe* di scaricare versioni vulnerabili anche con un nuovo `composer install` domani: lo flagga il triage, ma la verità è in produzione.

**Il matcher di constraint è preciso.** Non si limita al confronto major: implementa il caret `^X.Y.Z` con la regola eccezione per major-zero (`^0.5.0` = `>=0.5.0 <0.6.0`, `^0.0.5` = `>=0.0.5 <0.0.6`), il tilde `~X.Y`, il wildcard `X.Y.*`, l'hyphen range `1.0 - 2.0`, gli operatori `>=` `<=` `>` `<` `=`, l'OR `||` e l'AND comma-or-whitespace, le stability suffix `-dev` `-alpha` `-beta` `-RC` con priorità lessicografica. La normalizzazione è in DNF (disjunctive normal form) di intervalli e l'intersezione fra constraint è pairwise overlap: stessa semantica di `composer/semver` PHP, in JS vanilla, senza dipendenze.

**Privacy operativa.** Il `composer.lock` rivela la struttura completa di un progetto, le sue dipendenze transitive e gli eventuali pacchetti privati con riferimenti a repository VCS interni. Caricarlo su un servizio terzo per analisi preliminari non è una buona idea, anche con TLS: ogni servizio terzo è un vettore di leak per la mappa delle tue dipendenze. Qui non parte nessuna richiesta con i dati del manifest. Il dataset (16 shard advisory + 1 file EOL + 1 file abandoned) viene scaricato dal browser una sola volta al primo utilizzo e cachato in `localStorage` per le sessioni successive (invalidazione automatica via SHA-256 sui file pubblici). Audit, parsing e matching: tutto in locale.

**Quando il triage statico è il livello giusto.** Casi tipici. (a) Review preliminare di un progetto legacy senza accesso al filesystem: il cliente ti incolla il manifest in mail e vuole una valutazione in trenta secondi. (b) Pre-engagement audit per stimare lo sforzo di un eventuale incarico di adeguamento prima di firmare il preventivo. (c) Screen iniziale prima di un assessment cyber serio: definisci la lista dei high/critical da approfondire con esercizio di exploitability assessment, mappatura dei vettori applicabili, verifica della reachability dei codepath. (d) Self-check periodico per il proprietario dell'applicazione che vuole sapere se chi mantiene il codice sta facendo il suo lavoro.

**Limite di scope.** Il tool segnala CVE pubblicate, EOL noti, abbandoni dichiarati. Non sostituisce un Software Composition Analysis enterprise (Snyk, Mend, Sonatype): non controlla licenze, non genera SBOM CycloneDX, non analizza vulnerabilità di codice non-Composer (binari di sistema, script Node, container base image). Non sostituisce `composer audit` CLI integrato in pipeline. Non sostituisce un assessment manuale che valuti sfruttabilità reale dei vettori, presenza dei codepath nelle versioni, mitigazioni applicate a livello di hardening. È un triage e si propone come tale.

## Glossario

- **composer.json**: File manifest del progetto PHP. Dichiara nome, autore, dipendenze (`require`, `require-dev`), constraint di versione (es. `^11.0`), autoload PSR-4. Committato in git, è il documento autorevole su cosa il progetto dichiara di voler installare.
- **composer.lock**: File auto-generato da `composer install/update`. Contiene le versioni esatte risolte di ogni pacchetto + content-hash + timestamp. Garantisce build deterministica fra dev e production. Committato in git (best practice). Per audit di sicurezza precisi è la fonte autoritativa: il manifest dice cosa è ammesso, il lock dice cosa è installato davvero.
- **Constraint di versione Composer**: Sintassi per dichiarare quali versioni di una dipendenza sono accettabili. `^1.2` = SemVer compatible (`>=1.2 <2.0`, eccezione per major 0). `~1.2.3` = patch-level (`>=1.2.3 <1.3.0`). `1.2.*` = wildcard. `>=1.2,<1.5` = AND di operatori. `^1.0 || ^2.0` = OR di range. `1.0 - 2.0` = hyphen range. Best practice: `^X.Y` per dipendenze stabili, `~X.Y.Z` per dipendenze conservative dove preferisci solo patch.
- **CVE (Common Vulnerabilities and Exposures)**: Identificatore univoco di una vulnerabilità pubblica nel formato `CVE-YYYY-NNNN`. Assegnato dalla CVE Numbering Authority (CNA) competente. Database autoritativo: [cve.org](https://www.cve.org). Ogni CVE rilevante per ecosistema Composer ha una corrispondenza in Packagist Security e/o GitHub Advisory.
- **CVSS (Common Vulnerability Scoring System)**: Sistema standard per scoring di severity di una CVE. Versione corrente: 3.1 (4.0 in adozione progressiva). Output: **vector string** (es. `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H`) e **base score** 0.0-10.0. Severity bands: 0.1-3.9 low, 4.0-6.9 medium, 7.0-8.9 high, 9.0-10.0 critical.
- **CWE (Common Weakness Enumeration)**: Tassonomia delle classi di vulnerabilità software. Identificatori nel formato `CWE-NNN` (es. `CWE-79` Cross-Site Scripting, `CWE-89` SQL Injection, `CWE-502` Deserialization of Untrusted Data, `CWE-200` Information Disclosure). Una CVE può avere più CWE associate. Utile per pattern matching durante review e per training del team.
- **GHSA (GitHub Security Advisory)**: Identificatore di security advisory nativo GitHub, formato `GHSA-xxxx-xxxx-xxxx`. GitHub mantiene un database parallelo a CVE con triage spesso più rapido per ecosistemi open source. Ogni GHSA può corrispondere a un CVE pubblicato (mappatura esposta nel campo `cve` dell'advisory).
- **PSA (Packagist Security Advisory)**: Identificatore Packagist nel formato `PKSA-xxxx-xxxx-xxxx`. Packagist aggrega FriendsOfPHP/security-advisories e GHSA pertinenti all'ecosistema Composer in un endpoint unificato consultabile via API.
- **EOL (End Of Life)**: Data oltre la quale un pacchetto o framework non riceve più security update. Mantenere componenti EOL in production è un rischio: vulnerabilità nuove non vengono patchate sulla branch e l'unica strada è l'upgrade. In scope NIS2 (art. 21 c.2 lett. g) è esplicitamente segnalato come 'igiene cyber inadeguata'.
- **LTS (Long Term Support)**: Branch di un framework o linguaggio che riceve security update per un periodo esteso (tipicamente 2-5 anni) anche dopo il rilascio della versione successiva. Esempi: Laravel LTS storico (5.5, 6.x), Symfony LTS (4.4, 5.4, 6.4), PHP non ha LTS formale ma ogni minor riceve security update per circa 2 anni.
- **Abandoned**: Stato dichiarato dal maintainer in `composer.json` con il campo `abandoned` (boolean `true` oppure stringa con il nome del replacement suggerito). Packagist espone questo flag nel JSON pubblico del pacchetto. Da non confondere con deprecated: un pacchetto deprecated è ancora maintained ma sostituito da una versione successiva, un pacchetto abandoned è dichiarato fuori manutenzione dal suo autore.
- **FriendsOfPHP/security-advisories**: Database open source collaborativo di security advisory per pacchetti Composer. Aggiornato giornalmente, formato YAML, repository su GitHub. È la base storica del meta-pacchetto `roave/security-advisories` e dell'API Packagist Security Advisory.
- **OSV.dev**: Open Source Vulnerabilities, database mantenuto da Google. Schema OSV unificato, dump bulk per ecosistema (Packagist, npm, PyPI, Go, Maven, ecc.). Per ogni vulnerabilità: aliasing fra CVE/GHSA, severity CVSS v3, CWE ID, range affected per ogni package version. Sorgente di arricchimento per il dataset di questo tool.
- **endoflife.date**: Database open source dei cicli release di linguaggi, framework, runtime e tool. API JSON pubblica via `/api/{product}.json`. Per ogni ciclo: data di rilascio, fine supporto attivo, fine security support, flag LTS, ultima patch release. Sorgente del dataset EOL di questo tool.
- **roave/security-advisories**: Meta-pacchetto Composer (no codice, solo conflict declarations) che blocca l'install di pacchetti con CVE noti. Pattern d'uso: `composer require --dev roave/security-advisories:dev-latest`. Nessun overhead a runtime e nessun impatto sulle dimensioni della build: i conflict si valutano solo al solve time del dependency manager.
- **composer audit**: Comando ufficiale Composer disponibile dalla 2.4 (settembre 2022). Confronta i pacchetti installati con il database CVE di Packagist Security e produce output testuale o JSON. Exit code != 0 se sono presenti CVE attive. Da integrare nei job di test in CI/CD per bloccare merge su PR che introducono regressioni di security.
- **Renovate / Dependabot**: Bot automatici (GitHub Dependabot nativo, Renovate Bot multi-platform) che monitorano dependencies e aprono PR di aggiornamento. Per PHP-Composer: supporto nativo, riconoscono security advisories e prioritizzano le PR di security separandole dagli update minor/patch ordinari.
- **Supply chain security**: Disciplina della sicurezza ICT focalizzata sulle dipendenze di terze parti: librerie open source, package registry, mirror, build runner, deploy artifact. Scope NIS2 (art. 21 c.2 lett. d) include esplicitamente la supply chain ICT come materia di gestione del rischio. Strumenti operativi: SBOM (Software Bill of Materials), signing dei deploy, monitoring CVE in continuo, policy di update con SLA.

## Domande frequenti sul Composer audit

### Sono il proprietario o il project manager di un'applicazione PHP, non sono uno sviluppatore. Mi serve davvero questo tool?

Sì, ed è anzi uno dei target. La logica del tool è scritta per essere leggibile senza conoscenza approfondita di PHP: per ogni finding ricevi severity (critica, alta, media, bassa, informativa), nome del pacchetto coinvolto, motivazione esplicita e link al CVE pubblico o all'advisory GitHub. Se il report restituisce più di tre o quattro finding di severity high o critical sulle dipendenze di produzione, hai un argomento solido da portare al team di sviluppo per chiedere come e quando l'applicazione verrà aggiornata.

### Dove trovo materialmente il composer.json o il composer.lock della mia applicazione?

Entrambi vivono nella radice del progetto, accanto alla cartella `vendor/`. Tre vie tipiche di reperimento. (1) Repository git: chiedi accesso al repository (GitHub, GitLab, Bitbucket) e li trovi in radice, sono file di testo committati. (2) Server di produzione: se hai accesso SSH alla macchina, sono in `/var/www/<nome-progetto>/composer.json` o simili. (3) Hosting condiviso: spesso accessibili via FTP/SFTP o pannello di controllo. In nessun caso questi file contengono dati sensibili dell'applicazione (utenti, password, transazioni): contengono solo la lista delle librerie e le loro versioni.

### Il report mi segnala un pacchetto come EOL e il mio sviluppatore mi dice 'non è urgente'. Come rispondere?

EOL significa che il maintainer del pacchetto NON rilascia più security update sulla branch in uso. Le vulnerabilità nuove non vengono patchate per quella versione. La data è pubblica, nessuna stima soggettiva. La risposta corretta da uno sviluppatore competente è 'pianifico l'upgrade entro la data X' oppure 'il vettore non è applicabile al nostro scenario perché abbiamo Y, e lo documento per iscritto', non 'non è urgente'. In ambito NIS2 (art. 21 c.2 lett. g, recepita in Italia con il D.Lgs. 138/2024) tenere componenti EOL in produzione viene esplicitamente trattato come segnale di igiene cyber inadeguata.

### Differenza fra un audit del lock e un audit del json?

Il `composer.lock` contiene le versioni esatte risolte: il tool sa che hai installato esattamente `9.5.10` e può dirti se quella versione specifica è affected da una CVE. Il `composer.json` contiene solo i constraint dichiarati (`^9.0`): il tool non conosce la versione effettiva, ma può dirti se il vincolo permetterebbe di installare versioni vulnerabili. Per audit precisi e di valore operativo immediato usa il lock; il json è utile per capire se un nuovo `composer install` domani potrebbe scaricare versioni affected o no.

### Il tool segnala specifiche CVE per ogni pacchetto?

Sì. Il dataset alimentato da Packagist Security API + OSV.dev contiene 6.000+ advisory pubblicate con identificatore CVE quando assegnato, GitHub Security Advisory (GHSA), severity, CVSS vector v3 quando arricchito da OSV, lista CWE associata, constraint affected esatto. Per ogni match: link diretto al CVE Record o alla pagina GitHub Advisory. Il dataset è ricompilato ogni notte per restare allineato con le nuove pubblicazioni.

### Il matching delle versioni è preciso o solo approssimativo?

Preciso, lato lock. Il modulo `ComposerSemver` implementa la stessa semantica di `composer/semver` PHP: caret, tilde, wildcard, hyphen range, OR/AND, stability suffix, eccezione major-zero. Sul lock fa lookup esatto (versione concreta vs intervallo affected). Sul json fa intersezione di intervalli normalizzati in DNF: la finding viene emessa quando esiste almeno una versione installabile coperta sia dal vincolo dichiarato sia dall'intervallo vulnerabile. Il modulo è validato da una suite di 44 fixture (eseguibile manualmente da console: `ComposerSemver.runFixtures()`).

### Quanti pacchetti riesco a controllare in un audit? Performance?

Performance OK fino a 1.000 pacchetti in un singolo audit. Il tempo dominante è il fetch dei shard advisory (fino a 16 file da ~100 KB ciascuno, gzippati a ~25 KB). Con cache calda l'audit è istantaneo (parsing + matching su qualche centinaio di pacchetti = millisecondi). Per progetti enterprise con 1.000+ deps: `composer audit` CLI è più veloce e accurato (legge direttamente `vendor/composer/installed.json`). Il nostro tool è ottimizzato per progetti tipici fra 30 e 300 deps, l'80% dei progetti di consulenza per aziende.

### Cosa succede se carico un pacchetto sconosciuto al dataset?

Viene contato nelle statistiche generali ma non emette finding. Sconosciuto al dataset non significa pericoloso: la maggior parte dei pacchetti Composer è di nicchia o privata e non è mai stata oggetto di advisory pubblica, EOL pubblicato o flag abandoned. Per coverage più ampia su pacchetti di nicchia: `composer audit` CLI legge il database CVE FriendsOfPHP completo, e Snyk/Dependabot integrati nel repository monitorano in continuo.

### Il vincolo PHP del progetto è `>=7.4`. Cosa segnala il tool?

Lo flagga come `php_eol`: il vincolo include PHP 7.4 (security support terminato 2022-11), 8.0 (terminato 2023-11), 8.1 (terminato 2025-12). Significa che `composer install` formalmente accetterebbe l'installazione su una macchina con PHP 7.4 anche se la realtà di production è 8.x. Best practice: `>=8.2` minimo nel constraint, indipendentemente dalla versione di production attuale. Comunica intent + previene installazioni accidentali su VPS legacy + è un segnale di igiene cyber per audit esterni.

### Symfony ha tanti componenti standalone (`symfony/console`, `symfony/yaml`, eccetera). Sono tutti coperti?

Sì. Il tool considera ogni pacchetto `symfony/*` con la stessa policy LTS della release principale (Symfony 5.4 LTS = `symfony/console:5.4` LTS, eccetera). Il mapping è via prefisso `symfony/` nel `product_prefixes` dell'eol.json. Stesso pattern per `illuminate/*` mappati al ciclo Laravel. Eccezioni rare (componenti con cicli leggermente diversi dalla release principale) non sono modellate per non introdurre falsi positivi su corner case marginali.

### Il tool integra una libreria PHP/JS esterna a runtime?

No. JSON parse è nativo, il matcher di constraint è il modulo `composer-semver.js` scritto da zero, il fetch del dataset usa l'API `fetch` nativa, il sharding usa `crypto.subtle.digest('SHA-1')` nativo. Zero dipendenze, totale ~13 KB di JavaScript (3 KB minified gzip per il semver, 10 KB gzip per il GUI). Il dataset stesso è statico, ricompilato dal cron PHP server-side: il browser scarica solo i shard necessari per i pacchetti del manifest, non l'intero dataset.

### Differenza fra abandoned, deprecated e EOL?

**Abandoned**: il maintainer ha esplicitamente marcato il pacchetto come abbandonato in `composer.json` (campo `abandoned` letto da Packagist). **Deprecated**: il pacchetto è ancora maintained ma sostituito da una versione successiva (es. `league/flysystem 1.x` deprecato in favor di `2.x`). **EOL**: il maintainer non rilascia più security update sulla branch (es. Symfony 5.4 dopo novembre 2025). Tutti e tre richiedono pianificazione di migration; abandoned e EOL su rami di produzione sono i più urgenti.

### Posso usare il tool sul composer.json di una libreria, non di un'applicazione?

Sì, funziona uguale. Le librerie tipicamente hanno meno pacchetti (10-30) e sono più conservative sulla retrocompatibilità. Findings tipici su libreria: vincolo PHP troppo permissivo (`>=7.0` per supportare downstream legacy), version pinning di test framework ormai EOL, dipendenze su vecchi `doctrine/cache` abandoned. Audit utile prima di pubblicare una nuova major release o di chiudere lo scope di una versione long-term.

### Come integro l'audit reale in CI/CD?

GitLab CI: `composer audit --abandoned=report --format=json` dentro il job di test, exit code != 0 fa fallire il merge. GitHub Actions: stesso comando, opzionalmente con action `tj-actions/composer-audit`. Pre-commit hook: `composer audit` dentro `.husky/pre-commit` oppure `.git/hooks/pre-commit`. Best practice: eseguire l'audit anche sui tag di release, non solo sulle PR, per catturare CVE pubblicate fra il merge e il deploy. Per blocco install di pacchetti vulnerabili: `composer require --dev roave/security-advisories:dev-latest`.

### Il dataset è aggiornato in tempo reale?

Quasi: il cron lo ricompila ogni notte alle 03:00 UTC. Latency tipica fra pubblicazione di una nuova CVE su Packagist e visibilità nel tool: meno di 24 ore. Per latency zero serve `composer audit` CLI integrato in pipeline, che interroga l'API Packagist al momento dell'esecuzione. La scelta del rebuild giornaliero è un compromesso fra freschezza e costo di rete (il job fa ~2.800 chiamate Packagist a regime).

### Privacy: il manifest che incollo viene salvato o tracciato?

No. Il tool gira interamente nel browser. L'unica richiesta di rete che parte è il GET dei file dataset statici, identici per tutti gli utenti, completamente anonimi. Il `composer.json` o `composer.lock` incollato non viene mai inviato a nessuna richiesta di rete, non viene salvato in `localStorage`, non viene tracciato in analytics. Si può verificare aprendo il pannello Network di DevTools durante l'audit: zero richieste outgoing con i dati del manifest.

---
Uno strumento di Maurizio Fonte, senior backend engineer. Tutti gli strumenti: https://www.mauriziofonte.it/strumenti/
