MS Change Intelligence è una dashboard web leggera per raccogliere, normalizzare, consultare e filtrare gli aggiornamenti ufficiali di Microsoft 365 Roadmap e Azure Updates.
Il progetto privilegia un'architettura static-first, un footprint operativo ridotto e la continuità di servizio: il browser consulta un dataset JSON pre-generato, mentre un collector Python aggiorna i dati in modo atomico e conserva l'ultima versione valida se una fonte upstream non è disponibile o restituisce dati incompleti.
La versione attualmente distribuita su Azure App Service è raggiungibile all'indirizzo:
- https://app-mschangeintelligence.azurewebsites.net/microsoft/
- https://app-mschangeintelligence.azurewebsites.net/microsoft/cves/ (Microsoft CVEs & KBs da MSRC CVRF)
Il progetto nasce per offrire una vista operativa unica sugli aggiornamenti Microsoft senza introdurre un database, un framework frontend o dipendenze client di terze parti.
Gli obiettivi principali sono:
- aggregare fonti Microsoft ufficiali;
- mantenere una finestra mobile di tre anni basata sul campo Last modified;
- offrire una vista predefinita sugli ultimi 30 giorni;
- evidenziare rilasci, ritiri, modifiche e aggiornamenti che richiedono un'azione;
- consentire ricerca e filtraggio interamente nel browser;
- non inviare query, indirizzi IP o dati di navigazione a servizi di analisi esterni;
- continuare a servire l'ultimo dataset valido durante errori o rallentamenti upstream;
- funzionare come applicazione WSGI gestita su Azure App Service.
- Microsoft 365 Roadmap tramite RSS ufficiale.
- Azure Updates tramite endpoint OData v4 ufficiale.
- Paginazione completa dell'endpoint Azure con controllo di
@odata.count. - Convalida restrittiva degli URL di continuazione OData.
- Limite alle dimensioni delle risposte upstream.
- Deduplicazione per identificatore della fonte.
- Normalizzazione di entità HTML, markup, spaziatura e date.
- Riduzione controllata delle descrizioni troppo lunghe.
- Finestra mobile di conservazione di tre anni.
- Pubblicazione atomica di
data.jsonedata.json.gz. - Protezione dell'ultimo dataset valido in caso di raccolta parziale o fallita.
- MSRC CVRF v3.0 come fonte ufficiale. Le keyword
Azure,Dynamics,GitHub,Fabric,Microsoft,M365,Office,PowerShell,Remote Desktop,Surface,System Center,Teams,Visual StudioeWindowssono applicate come filtro case-insensitive*keyword*durante il parsing; i tag prodotto originali CVRF vengono mantenuti nel dataset. Azure Linuxè escluso esplicitamente dal filtroAzure.- Finestra mobile di 12 mesi basata su
InitialReleaseDate, individuata tramite il filtro dell'indice/updates. - Un documento CVRF mensile scaricato per ogni mese della finestra.
- Deduplicazione per CVE e normalizzazione di severity, KB, CWE, exploit e disclosure; i nomi prodotto non vengono rinominati.
- La vista CVE applica di default il filtro temporale degli ultimi 30 giorni; la retention backend resta di 12 mesi.
- Pubblicazione atomica separata di
cve-data.jsonecve-data.json.gz. - Limite di download CVRF separato dal limite RSS/OData, necessario per i documenti mensili più grandi.
- Ultimo dataset valido mantenuto in caso di errore upstream.
- Ricerca per titolo, descrizione, prodotto e feature ID.
- Filtri per fonte, prodotto, stato, azione e periodo.
- Vista predefinita sugli ultimi 30 giorni.
- Ordinamento per data o prodotto.
- Filtri serializzati nella query string, quindi condivisibili tramite URL.
- Caricamento progressivo delle schede, 60 elementi alla volta.
- Conteggi riepilogativi per elementi visibili, ritiri, azioni richieste e aggiornamenti recenti.
- Interfaccia responsive, accessibile da tastiera e compatibile con tema chiaro/scuro.
- Scorciatoia
/per portare il focus sul campo di ricerca. - Rendering sicuro mediante
textContent; il markup proveniente dalle fonti non viene inserito nel DOM.
Nella distribuzione WSGI il client controlla l'età del dataset. Se generatedAt ha almeno 24 ore:
- il browser invia
POST /api/refresh-if-stale; - il server acquisisce un lock non bloccante;
- un solo processo esegue il collector;
- gli altri client ricevono
202 runninge attendono; - il dataset viene ricaricato solo dopo la pubblicazione di una generazione più recente.
Dopo un errore upstream viene applicato un backoff persistente di 15 minuti, 1 ora e infine 6 ore. Durante tutto il processo il dataset precedente rimane disponibile.
La vista CVE usa lo stesso protocollo e la stessa soglia di 24 ore, ma con endpoint, lock e stato indipendenti:
- vista principale:
POST /api/refresh-if-stale→collector.py→data.json; - vista CVE:
POST /api/refresh-cves-if-stale→cve_collector.py→cve-data.json.
Microsoft 365 Roadmap RSS ─┐
├─> collector.py ─> data.json + data.json.gz ─┐
Azure Updates OData v4 ────┘ │
MSRC CVRF /updates ────────> cve_collector.py ─> cve-data.json + .gz ────┤
v
Browser <─ HTML/CSS/JavaScript <─ server.py / Gunicorn
├──── POST refresh-if-stale ───────┘
└──── POST refresh-cves-if-stale ──┘
Il repository non richiede un database e non contiene un processo di raccolta residente. Il collector è un comando one-shot avviato dall'applicazione durante una procedura di aggiornamento controllata.
.
├── collector.py Collector RSS/OData e pubblicazione atomica
├── server.py Applicazione WSGI e refresh stale-only
├── startup.sh Avvio Gunicorn per Azure App Service
├── requirements.txt Dipendenza runtime Gunicorn
├── index.html Dashboard principale
├── styles.css Stili responsive
├── app.js Filtri, ricerca, ordinamento e rendering
├── app-utils.js Utility frontend condivise e testabili
├── refresh-client.js Protocollo client di aggiornamento
├── demo.html / demo.js Demo deterministica degli stati di refresh
├── cve_collector.py Collector MSRC CVRF separato
├── cves.html / cves.css / cves.js Vista isolata CVEs & KBs
├── mschangeintelligence.png Logo dell'applicazione
├── favicon-16x16.png / favicon-32x32.png
├── favicon-96x96.png / apple-touch-icon.png Icone favicon dell'applicazione
├── data.json / data.json.gz Dataset last-known-good principale
├── cve-data.json / cve-data.json.gz Dataset last-known-good CVE, 12 mesi
├── samples/ Fixture RSS per test e sviluppo offline
├── test_collector.py Test del collector principale
├── test_cve_collector.py Test del parser CVRF
├── test_server.py Test del server WSGI
├── test_app.js Test del frontend principale
├── test_cves.js Test del contratto frontend CVE
├── test_refresh_client.js Test del protocollo di refresh
└── test_demo.js Test della demo
Le directory build/, __pycache__/, gli ambienti virtuali e gli stati runtime non fanno parte della codebase versionata.
- Linux o un ambiente compatibile con
fcntl; - Python 3.11 o successivo;
- accesso HTTPS alle fonti Microsoft;
- Gunicorn 23.0.0 per l'esecuzione WSGI.
- Node.js recente;
- nessun pacchetto npm richiesto.
Il collector utilizza esclusivamente la libreria standard Python. Gunicorn è l'unica dipendenza Python esterna del runtime web.
| Variabile | Predefinito | Descrizione |
|---|---|---|
PORT |
8000 |
Porta utilizzata da startup.sh. |
DASHBOARD_ROOT |
directory di server.py |
Directory degli asset applicativi. |
DASHBOARD_DATA_DIR |
DASHBOARD_ROOT in server.py; /home/data/microsoft-release-dashboard in startup.sh |
Directory persistente e scrivibile per dataset, lock e stato di backoff. |
DASHBOARD_ORIGIN |
origine derivata dalla richiesta | Origine autorizzata per il POST same-origin di refresh su Azure App Service. |
Non sono richieste chiavi API per le fonti Microsoft attualmente utilizzate.
Il documento generato contiene quattro sezioni principali:
{
"schemaVersion": 1,
"generatedAt": "2026-07-30T16:12:45Z",
"sources": {},
"summary": {},
"retention": {},
"items": []
}Ogni elemento normalizzato contiene, tra gli altri:
id: identificatore globale composto da fonte e ID upstream;sourceesourceKey;featureId;titleedescriptionnormalizzati;urllimitato a host Microsoft approvati;updatedin UTC ISO 8601;status,phase,actioneupdateType;product,productsecategories.
La classificazione action per Microsoft 365 è euristica e basata su categorie e parole chiave. Non sostituisce la valutazione del testo ufficiale collegato nella scheda.
La suite non richiede pytest né dipendenze npm.
python3 -m unittest -q test_server.py test_collector.py test_cve_collector.py
node test_app.js
node test_cves.js
node test_refresh_client.js
node test_demo.jsControlli sintattici aggiuntivi:
python3 -m py_compile collector.py cve_collector.py server.py test_collector.py test_server.py test_cve_collector.py
node --check app.js
node --check cves.js
node --check app-utils.js
node --check refresh-client.js
node --check demo.jsI test coprono, tra l'altro:
- parsing e normalizzazione RSS/OData;
- cutoff mobile e gestione del 29 febbraio;
- paginazione, loop e host OData autorizzati;
- conteggi completi e deduplicazione;
- scrittura atomica e coerenza gzip;
- routing WSGI, cache validation ed header di sicurezza;
- soglia esatta delle 24 ore;
- lock concorrente, backoff persistente e recovery;
- protocollo client
fresh,running,updated,backoffefailed; - filtri iniziali e isolamento della vista CVE;
- parsing CVRF, deduplicazione CVE e coerenza gzip del dataset CVE;
- endpoint CVE stale-only con lock e stato indipendenti.
startup.sh è predisposto per un App Service Linux con runtime Python:
/home/site/wwwroot/startup.sh
La configurazione usa:
- un worker Gunicorn;
- worker class
gthreadcon due thread; - timeout di 210 secondi;
DASHBOARD_DATA_DIR=/home/data/microsoft-release-dashboardcome storage persistente;- porta fornita da Azure tramite
PORT.
L'applicazione è pubblicata sotto /microsoft/. Gli endpoint di refresh sono /api/refresh-if-stale per la dashboard principale e /api/refresh-cves-if-stale per la vista CVE.
Impostazioni raccomandate per App Service:
- HTTPS obbligatorio;
- TLS minimo 1.2 o superiore;
- FTPS disabilitato se non necessario;
- HTTP/2 abilitato;
- health probe sulla dashboard;
- persistenza della directory
/home/data.
Il workflow .github/workflows/deploy-azure.yml esegue test, controlli sintattici, validazione del dataset, creazione di un pacchetto runtime pulito, autenticazione passwordless tramite GitHub OIDC, deploy e verifica HTTP dell'applicazione.
Le GitHub Actions di terze parti sono referenziate tramite commit SHA immutabili. Il pacchetto contiene esclusivamente i file runtime approvati e un deployment-metadata.json generato durante la build con repository, commit e identificativo della run.
Il repository usa le seguenti GitHub Actions variables, senza publish profile o client secret:
AZURE_CLIENT_ID;AZURE_TENANT_ID;AZURE_SUBSCRIPTION_ID;AZURE_RESOURCE_GROUP;AZURE_WEBAPP_NAME;AZURE_DEPLOY_ENABLED.
Il job di test viene eseguito a ogni push su main. Il job di produzione è attivo e viene eseguito dopo il completamento positivo dei controlli. L'identità Entra federata usa il ruolo minimo Website Contributor, limitato alla singola Web App, e la federazione è vincolata al GitHub environment production.
Ogni aggiornamento di main avvia automaticamente il deploy su Azure App Service e la successiva verifica HTTP dell'applicazione pubblicata.
- sono accettate soltanto fonti HTTPS definite nel collector;
- gli URL OData di continuazione sono limitati a host e path Microsoft previsti;
- i link mostrati agli utenti sono limitati a host Microsoft approvati;
- il markup upstream viene rimosso e non è renderizzato con
innerHTML; - le descrizioni sono limitate a 2.000 caratteri.
Il server applica:
- Content Security Policy restrittiva;
X-Frame-Options: DENY;X-Content-Type-Options: nosniff;Referrer-Policy: no-referrer;- richiesta same-origin per il refresh;
- limite di 1 KiB al corpo della richiesta;
- assenza di CORS permissivo;
ETag,Last-Modifiede supporto304;Vary: Accept-Encodingper il dataset.
- una raccolta incompleta non sostituisce il dataset valido;
- JSON e gzip vengono generati prima della sostituzione atomica;
- il refresh usa un lock Linux non bloccante;
- errori upstream attivano un backoff persistente;
- il browser continua a visualizzare i dati precedenti.
La dashboard non integra analytics, tracker, CDN JavaScript, font remoti o chiamate client verso terze parti. Le richieste alle fonti Microsoft vengono effettuate dal collector sul server, non dal browser del visitatore.
I file data.json, cve-data.json e samples/ contengono esclusivamente contenuti pubblicati dalle fonti Microsoft ufficiali; non devono essere usati per memorizzare dati di tenant o informazioni personali interne.
- Microsoft 365 e Azure possono modificare endpoint o schema senza preavviso.
- Il feed RSS Microsoft 365 può rappresentare una finestra upstream anziché l'intero storico; il collector espone il conteggio upstream osservato.
- La classificazione delle azioni è euristica.
- Il refresh su navigazione dipende dall'arrivo di una richiesta dopo la soglia di 24 ore.
- Il server usa
fcntle quindi è destinato a Linux/Unix, non a Windows nativo. - Una singola istanza con filesystem condiviso è il modello operativo previsto per lock e stato persistente.
Controllare i log dell'App Service, la disponibilità dello storage persistente /home/data e la raggiungibilità delle fonti Microsoft. Se la raccolta fallisce, il dataset esistente non viene sostituito.
Verificare che DASHBOARD_ORIGIN corrisponda esattamente all'origine pubblica Azure.
Un altro worker o client sta già aggiornando il dataset. Il client riprova rispettando Retry-After.
Un tentativo precedente è fallito. Il server conserva il dataset corrente e restituisce l'orario del prossimo tentativo in nextAllowedAt.
La dashboard usa intenzionalmente il prefisso /microsoft/. Verificare che la configurazione Azure e il server applicativo conservino tale percorso.
Per modifiche funzionali:
- creare un branch dedicato;
- aggiungere o aggiornare prima i test del comportamento interessato;
- eseguire tutte le suite Python e JavaScript;
- verificare che
data.json.gzecve-data.json.gzsiano le decompressioni esatte dei rispettivi JSON quando i dataset vengono rigenerati; - evitare nuove dipendenze salvo reale necessità;
- non aggiungere segreti, file
.env, cache o dati di tenant al repository.
Creato da Alessandro Fabiani.
Il logo mschangeintelligence.png è un asset fornito dal titolare del progetto ed è incluso nel repository privato per l'uso con MS Change Intelligence. Non viene concessa separatamente una licenza di riutilizzo del marchio o dell'immagine.
Nessuna licenza open source è attualmente dichiarata. Il repository è privato e tutti i diritti restano riservati al titolare del progetto.