- reports.html: RIPRISTINATO da HEAD (working tree corrotto: 89 tab 'Report Esecutivo' duplicati, ~146 righe spazzatura non committate; HEAD era sano). La corruzione era servita live, ora risolta.
- Nuovo tab 'Requisiti ACN': mostra gli 87/116 requisiti specifiche-base per funzione (GV/ID/PR/DE/RS/RC) con % compliance, summary stati e cambio stato inline (select -> PUT).
- api.js: getAcnRequirements + updateAcnRequirement.
JS validato (node --check). Endpoint E2E già verde (org importante 87 req, PUT implemented ok).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Allinea il PRODOTTO alla guida/normativa portando la compliance dal livello 10 misure Art.21
al livello operativo dei requisiti ACN (Framework Nazionale 2025).
- Migrazione 031: acn_requirements (catalogo) + org_acn_requirement_status (stato per-org)
- Seed da Allegati 1-2 ACN (fonte certa, parsing verificato): 87 importanti + 116 essenziali = 203 requisiti reali
- AuditController: acnRequirements (GET, per entity_type org: importanti 87 / essenziali 116, summary per funzione GV/ID/PR/DE/RS/RC, % compliance) + updateAcnRequirement (PUT stato+evidenza)
- Route audit/acnRequirements GET/PUT
- guida.html: fix refuso cap-5 (residuo 'otto categorie...no' -> '10 categorie x 8, quattro modalita')
E2E prod: org importante -> 87 req; PUT implemented -> compliance aggiornata.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Scaricati da acn.gov.it: Allegato1 (soggetti importanti), Allegato2 (essenziali),
Allegato3/4 (incidenti significativi). PDF + .txt (pdftotext -layout). Sono la fonte
certa per allineare il questionario Gap Analysis ai requisiti operativi reali del
Framework Nazionale 2025 (funzioni GV/ID/PR/DE/RS/RC). Importanti: 43 sottocategorie;
essenziali: 49. (Conteggio requisiti di dettaglio: parsing tabellare da rifinire.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completa l'allineamento iniziato in 5e2534e (FAIR/KRI/benchmark). Ora tutte e 10 le nuove
funzionalita sono documentate nei capitoli pertinenti. Ancore cap-X intatte, HTML bilanciato.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
10 sezioni aggiunte nei capitoli pertinenti (cap-3/4/6/7/8/9/11/12), stile coerente con
l'esistente (in parole semplici / esempio / cosa dice la norma). Ancore cap-X invariate
(help.js continua a linkare i capitoli giusti). HTML bilanciato.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Il commit precedente NON conteneva questo fix (Edit fallito su ancora errata). Ora applicato:
JOIN su MAX(completed_at) -> subquery correlata (ultimo completato, tie-break id, LIMIT 1),
una sola riga per org anche con timestamp identici. E2E: org con 2 assessment stesso TS -> peers=4 (non 5).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- ingestIncident: insert in loop (max 5) -> rigenera incident_code su collisione UNIQUE
(sotto carico SIEM il random a 6 cifre poteva collidere -> 500 = alert perso). Inoltre la
race su external_ref (due alert simultanei) ora ritorna 200 dedup invece di 500.
- controlsMonitoring (services): UPDATE auto-stale avvolto in try/catch come la gemella in
AuditController (degrada con grazia se control_evidence_auto manca).
Verificato E2E: ingest 201, dedup 200.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Migrazione 030: UNIQUE uq_policy_version su policy_versions (de-dup prima, idempotente).
approve() ora usa INSERT ... ON DUPLICATE KEY UPDATE -> riapprovare la stessa versione
aggiorna lo snapshot invece di duplicarlo. Verificato E2E: 2x approve v1.0 -> 1 sola riga.
- diff(): sostituito il confronto set-based (falsi negativi su righe duplicate/riordino) con
un vero diff LCS line-by-line con posizioni. Verificato E2E: bump v1->v2 -> added 2, removed 1 corretti.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Due vulnerabilità trovate dalla review indipendente:
1. connectorOrgGuard usava users.role (GLOBALE) invece del ruolo per-org -> la feature
era ROTTA per gli utenti reali (org_admin reale ha users.role='employee' -> 403 sulla
propria org). Ora ancora l'autorizzazione al parametro di ROUTE {id} e legge
user_organizations.role. Verificato E2E: globale=employee + per-org=org_admin -> 200;
non-membro su altra org -> 403 (no IDOR via header X-Organization-Id).
2. secret-strip era una denylist case-sensitive/non-ricorsiva aggirabile (Client_Secret,
apiKey, connection_string, segreti annidati). Sostituita con ALLOWLIST ricorsiva
(sanitizeConnectorConfig): solo campi non sensibili noti, valori forzati a stringa+troncati.
Verificato E2E: input con 11 varianti di segreti -> DB contiene solo {account_id, region}.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bug PRE-ESISTENTE (commit 7823898 del 2026-02-20, presente anche in 94d7867): gli apostrofi in
"dell'attivita" (riga 512) e "l'anno" (riga 513) chiudevano le stringhe a singolo apice ->
SyntaxError che azzerava l'INTERO blocco <script> della pagina Rischi (tabella, matrice, FAIR,
KRI, dettaglio: tutti gli onclick davano ReferenceError). La pagina era servita 200 ma JS morto.
Trovato dalla review multi-agente (agente frontend). node --check ora OK.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Verificato E2E in prod: list 200 (8 tipi), save m365 201, secret 'client_secret' STRIPPATO (assente da config DB), delete 200, openConnectors servito in companies.html.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Richiesta utente: 'le credenziali si configurano nella card per ogni azienda cliente'.
- Migrazione 029: tabella org_connectors (config NON segreta + vault_key_alias + secret_status). NESSUN segreto nel DB.
- OrganizationController: listConnectors/saveConnector/deleteConnector + connectorOrgGuard (org_admin/compliance_manager propria org, o firm che la gestisce, o super_admin)
- Difesa: i campi segreti (client_secret/api_key/...) inviati vengono STRIPPATI prima del salvataggio (verificato E2E: non finiscono nel DB)
- saveConnector ritorna cli_hint col comando vault-cli per caricare il segreto (write-path vault = solo CLI admin, confermato leggendo server.js: solo GET /v1/credentials/*)
- UI: pannello 'Connettori' nella card di companies.html (8 tipi, tenant/client id, toggle attivo, stato segreto, modal)
- Route organizations/{id}/connectors GET/PUT/DELETE (type nel body)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
I commit 56ce97d/1a5db30/14c06c8 contenevano migrazioni+HTML ma gli Edit dei
metodi controller e delle route erano falliti silenziosamente (ancore errate).
Ora presenti e testati E2E in produzione:
- DashboardController::sectorBenchmark (era 501)
- SupplyChainController: sendQuestionnaire/publicQuestionnaire/submitPublicQuestionnaire/questionnaireStatus/resolveQuestionnaire + route 'supply-chain' (era 404)
- PolicyController: attest/attestations/versions/diff/pendingAttestations + snapshot in approve + route (era 404)
Test: benchmark 200, supplier flow send->submit(score 61)->dedup 409->DB risk_score=39,
policy approve->attest(coverage 50%)->bump v2.0->diff(+2/-1)->pending ricompare.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- DashboardController::sectorBenchmark -> GET /dashboard/sectorBenchmark
confronta score compliance org vs aggregati anonimi del settore (avg/median/p25/p75/percentile)
k-anonymity: aggregati solo se >= 3 organizzazioni nel settore (no de-anonimizzazione, nessun dato per-org)
- UI dashboard: pannello benchmark con barra distribuzione + posizione (top quartile/sopra mediana/...) + delta vs media
- Dato che i competitor single-tenant non possono offrire (gap P2 reporting EVIX)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Il commit 372ccb5 aveva incluso versioni con Edit falliti (ancore errate):
- AuditController::controlsMonitoring ora effettivamente presente (era 501 in prod)
- ServicesController::openapi ora espone incidents-ingest/evidence-ingest/assets-ingest/controls-monitoring
- i18n.js: chiavi nel formato corretto {it,en} (risks.fair_tab/kri_tab, assets.import_btn, audit.monitoring_tab)
- help.js: sezione Monitoraggio Continuo in reports
Verificato in prod: openapi 4/4, controlsMonitoring/fairRegister/kri tutti 200.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Il commit 1be3bd0 conteneva migrazione 026 + FairService + route ma NON i metodi
computeFair/fairRegister/listKri/createKri/updateKri nel controller (Edit fallito
per ancora errata). Ora presenti e testati E2E in prod:
- FAIR compute ALE Monte Carlo (risk 432: ALE mean 174.806 EUR, deterministico)
- fairRegister portfolio ALE, KRI create/update/dashboard con semaforo amber->red
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Competizione coi GRC enterprise sul risk management quantitativo:
- FairService: simulazione Monte Carlo FAIR (PERT su TEF e Loss Magnitude),
ALE in EUR con percentili P10/P50/P90 + istogramma, deterministico (seed da input)
- RiskController::computeFair -> POST /risks/{id}/fair (persiste parametri+ALE)
- RiskController::fairRegister -> GET /risks/fairRegister (portfolio ALE EUR)
- KRI: listKri/createKri/updateKri (GET/POST /risks/kri, PUT /risks/kri/{id})
con stato semaforo green/amber/red su soglie+direzione
- Migrazione 026: risks += parametri FAIR + ale_min/ml/max/mean; nuova tabella kri
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Aggiunto da AgileHub-side (VIGILE) su autorizzazione esplicita utente, dopo che la
sessione NIS2 ha scoperto sul campo 2 lezioni critiche:
1. opcache.validate_timestamps=Off -> ogni edit .php richiede kill -USR2 1 nel
container FPM (bind-mount non basta a servire il nuovo bytecode)
2. modifiche non committate vengono revertate dal cron ticket-agent (caso reale:
commit d5d83bb ha revertato index.php di una Feature 1 WIP)
- Nuovo doc docs/INCOMING_FROM_AGILEHUB_2026_05_30_release_workflow_hot_reload.md
- Sezione ATTIVITA PRIMARIA inserita in CLAUDE.md subito dopo lo standard timezone
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Il FAB ARIA (common.js) chiamava POST /api/ai/ask ma il controller non esisteva
(assistente AI rotto). Creato AiController::ask -> AIService::askWithRag con RAG su KB
+ grounding fonti certe. Verificato in produzione: rag_used=True, cita Ambiti NIS2 / Determina ACN.
Fix DNS Qdrant: nei worker php-fpm (musl) getenv e gethostbyname NON funzionano per
hostname Docker single-label; funziona solo un IP letterale. VectorService fallback ->
172.21.0.3 (fpm-safe); QDRANT_URL compose resta hostname per CLI. Vedi nota drift in VectorService.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
L'IP hardcoded Qdrant 172.21.0.5 era driftato a .3 (container senza IP statico) e
con php-fpm clear_env=no la env QDRANT_URL=172.21.0.5 (morta) veniva usata -> RAG web rotta.
Fix: QDRANT_URL e fallback VectorService usano l'hostname http://nis2-qdrant:6333,
risolto via Docker DNS sia in CLI sia in php-fpm. Verificato retrieval end-to-end (287 chunk).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>