Dopo la fusione 36->12, corretti i rinvii nei 12 documenti pubblicati: 'Politica per la Sicurezza delle Informazioni' -> 'Politica Generale...'; rinvii a 'Procedura di Controllo dei Documenti' -> 'Procedura di Governance del SGSI'; rinvio all'Istruzione Hardening (assorbita) -> regole di hardening nella Politica Uso Accettabile/Dispositivi/Remoto; riscritta la sez.8 'Struttura documentale' del Manuale sui 12 documenti. Aggiornati anche gli snapshot v2.0. Auto-referenze interne (minuscole) lasciate invariate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Approccio ISO 27001 agile/lean: i 36 documenti v1.0 sono fusi per tema in 12 documenti consolidati v2.0 (Manuale, 6 politiche, 5 procedure), pubblicati. I 36 originali sono ARCHIVIATI (storico preservato, non cancellati). Idempotenza ignora gli archiviati (fix collisione titolo procedura incidenti).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
I diritti sul ciclo di vita dei documenti SGSI sono ora configurabili per ruolo (matrice Ruolo x Azione, org-scoped) invece che cablati.
- mig.061: tabella isms_doc_permissions (org x capability x role x allowed)
- IsmsModelController: requireDocCapability() sostituisce i requireOrgRole cablati nelle 7 transizioni (edit/submit/approve/publish/reject/archive/new_version); default = comportamento precedente; super_admin sempre ok; endpoint GET/PUT /api/isms/docPermissions (canEdit)
- settings.html: tab 'Permessi documentali' con matrice checkbox (lettura per i membri, salvataggio Amministratore/Direzione)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- exportAllDocumentsWord: GET /api/isms/documentsWordAll -> .doc unico (copertina+indice+page-break) di tutto il set non archiviato
- getDocument: GET /api/isms/documents/{id} (full doc per editor)
- updateDocument: guard NOT_EDITABLE su documenti non-bozza/revisione + ruoli estesi a board_member
- UI isms.js: pulsante 'Scarica tutto (Word)', 'Modifica' (editor contenteditable + toolbar + toggle HTML) per bozze/revisione
- help.js: editor in-app + download unico
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ogni documento del Modello SGSI ha un ciclo di vita completo Bozza->In revisione->Approvato->Pubblicato->Archiviato con tracciamento (revisore/approvatore/pubblicatore, date, entrata in vigore, prossimo riesame) e storico versioni (snapshot). Download Word .doc modificabile per documento.
- mig.060: stati published/archived + 10 colonne tracciamento + tabella isms_document_versions
- IsmsModelController: submit/approve/publish/reject/archive/newVersion/versions/exportWord + listDocuments arricchito
- index.php: 8 route documents/{id}/(submit|approve|publish|reject|archive|newVersion|versions|word)
- isms.js: UI badge stato + pulsanti transizione + download Word + storico (IT/EN inline); cache-buster
- help.js: sezione 'Ciclo di vita dei documenti (cl.7.5)'
- 31 documenti di Nuova Agile pubblicati v1.0 (build_nuova_agile_p6_publish)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Audit registrato live nel modulo Audit interni: 21 voci di checklist (clausole ISO 27001 4-10 + controlli Annex A/SoA + misure NIS2 art.23/24/25), 5 Non Conformità aperte (1 maggiore: determinazione applicabilità NIS2 non formalizzata; 4 minori: trattamento rischi, formazione, controlli non eseguiti, DPO non nominato). Verdetto: SGSI documentato ma non ancora operativo, non pronto per Stage 2.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Provisioning idempotente di un'azienda cliente cloud-only (Aruba/Hetzner/AI, no server in sede, PC portatili):
- Fase 1: org + classificazione NIS2 (important, sub_threshold_candidate/preliminary) + 3 membri esistenti collegati + organigramma 8 ruoli
- Fase 2: 12 asset (NIS2-rilevanti) + 4 fornitori + 10 rischi + gap analysis (80 risposte, 45%)
- Fase 3: ISMS model + SoA 111 controlli (27001/27017/27018) + 31 documenti (Manuale/10 politiche/13 procedure/7 istruzioni)
- Fase 4: 15 piani di controllo periodici + 5 corsi formazione
Documenti AI-redatti con regole fonti-certe (D.Lgs.138/2024 art.23/24/25; 24h/72h/1 mese). Sorgenti in _docs_na/.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Indicizza in Qdrant nis2_kb (SYSTEM) la conoscenza prodotto sulle nuove funzioni Policy:
unificazione Policy/Procedura, collegamento M:N con Misure e Requisiti, allegati, matrice RACI
(con la nota fonti-certe: R+A per approvare = regola di prodotto, non obbligo NIS2; ISO distingue
policy/procedura come best practice). Script self-contained sotto application/cli (montata in
nis2-app), doc inline, idempotente per source. Eseguito host-side: 2 chunk, doc_uuid 1df7926a,
tracking kb_uploaded_documents id=17. Verifica RAG: searchForUser ritorna il doc come 1° risultato
(score 0.63-0.67) su 3 query (Policy=Procedura, RACI, allegati/link). Chiude il residuo KB di #384.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sezione 'Matrice RACI' nel dettaglio policy: assegnazione ruoli organigramma
(org_roles) alle responsabilita R/A/C/I via infrastruttura RACI esistente
(raci_assignments, object_type='procedure'). In bozza libera; PolicyController::approve
ora richiede almeno un ruolo R e uno A (422 RACI_REQUIRED). Nessuna migrazione.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Misure e Requisiti: colonna Policy (default) mostra tutte le policy collegate al
requisito come codici Policy.NN cliccabili -> aprono la policy (policies.html?policy=ID).
Policy: colonna Requisito NIS2 mostra tutti i requisiti collegati (codifica MISURA+
REQUISITO, es. 'DE.CM-01 comma 1.') come link -> evidenziano il requisito
(misure-requisiti.html?requisito=ID). Backend: FrameworkController::catalog espone
policies[] per requisito e PolicyController::list espone requisiti[] per policy
(org-scoped, tabella requisito_policy mig.059, try/catch con degrado al dato di default).
Solo lettura/visualizzazione: nessuna modifica dati, nessuna migrazione.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Migrazione 059 additiva/idempotente/reversibile:
- policies.proc_code VARCHAR(16) NULL: ripristina la chiave di join verso il
catalogo canonico (cfg_nis2_procedure/cfg_nis2_requisiti), che andava persa
quando le 42 procedure di default venivano copiate nell'elenco Policy (mig.048).
- tabella requisito_policy: M:N org-scoped requisito<->policy (un requisito puo'
avere piu' policy e una policy piu' requisiti) — base per p.2.2 e p.3.
- backfill sicuro: proc_code valorizzato su 42 policy (match esatto titolo) +
116 associazioni di default popolate (INSERT IGNORE, WHERE proc_code IS NULL).
- PolicyController::ensureFrameworkProcedures: i nuovi seed conservano proc_code
e popolano requisito_policy (try/catch dedicato, non blocca il seed).
Nessun impatto utente in questo passo (nessuna superficie legge ancora i nuovi
campi). UI multi-valore + link bidirezionali (2.2/3) nei prossimi cicli. v1.23.6.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- policies.html: sezione 'Allegati' nel dettaglio policy (Bootstrap Italia card),
upload (entity_type=policy) via /api/audit/evidence/upload, lista con download/autore/data
- AuditController::uploadEvidence: allowlist estensioni server-side (anti stored-XSS same-origin)
- version.json -> 1.23.5
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- #7 (go-live ready): send() aggancia l'invio email del questionario/firma-lettura via
EmailService, GATED dal kill-switch EMAIL_SENDING_ENABLED (oggi=false → ZERO invii;
pronto al go-live). Magic-link assoluto via APP_URL; invio solo ai target con email
valida; i link restano sempre disponibili per la condivisione manuale. Smoke prod:
email_sent=0, email_enabled=false (nessun invio).
- #2 (prevale Simon): nis2_sources.php — aggiunta NOTA DI PROVENIENZA sul 92 vs 87
(prodotto usa 92 = file referente; ACN ufficiale 87; divergenza nota/accettata).
Numero NON modificato per decisione confermata.
Note operative (host, fuori repo):
- #8: EMAIL_MS_URL spostato sul path interno http://172.21.0.1:8081/api/emails
(verificato 400 con X-Internal-Key = funzionante; pre edge-strip). Rollback: URL pubblico.
- #9: pre-equip PHP-CA del DB CONFERMATO (CA presente, app connessa in TLS 1.3); enforce
ALTER USER nis2_user REQUIRE SSL resta azione DB coordinata con VIGILE (utente @%).
v1.21.3. Additivo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- UPLOAD ALLEGATI RIPRISTINATO in prod (causa: il sito è servito da php-fpm HOST e
public/uploads era git:git senza ACL per www-data → move_uploaded_file falliva;
fix host: setfacl -R -m u:www-data:rwx -m d:u:www-data:rwx public/uploads). Collaudato:
upload interno+portale .pdf 201, .html→422. Risolve anche evidence_files/visure.
- nis2_sources.php (fonti-certe): Determina 333017/2025 datata 22 settembre 2025 e marcata
STORICA/superata; aggiunta entry VIGENTE 379887/2025 (efficace dal 31/12/2025); URL ACN
autorevoli (date-giorno marcate [da confermare sul testo ufficiale], verifica nis2-expert).
- UI attività: create+assign non lascia più attività orfane su errore (no duplicati su retry,
messaggio chiaro) + assegnazione individuale richiede almeno un destinatario; blocco PWA di
stakeholder-activities allineato (favicon-16/status-bar/application-name). v1.21.2.
ESCALATION (decisione di Simon, non modificata): nis2_sources.php riga 79 riporta "37 misure /
92 requisiti" per gli importanti (dato file Simon), ma la pagina UFFICIALE ACN indica "37/87".
Da riconciliare con Simon (open item #2). NON cambiato per rispettare la decisione bloccata.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Corretti i finding confermati dalla verifica multi-agente (5 major + minori):
SICUREZZA
- Stored XSS allegati: da blocklist a ALLOWLIST di estensioni innocue (no html/svg/js
renderizzabili same-origin) in StakeholderPortalController::attachment e
StakeholderActivityController::storeUpload; nome file randomizzato (random_bytes).
- Magic-link: scadenza (mig.054 stk_activity_targets.token_expires_at; send() imposta
scadenza attività+30gg o +90gg; resolveTarget() → 410 TOKEN_EXPIRED se scaduto);
rate-limit per-IP sugli endpoint del portale; comment/attachment bloccati su attività chiusa.
CORRETTEZZA
- send(): NON rigenera token né azzera lo stato dei destinatari già responded/acknowledged
(prima ne perdeva l'esito); imposta token_expires_at.
- assign individuale: semantica "replace" (rimuove i deselezionati non ancora conclusi) +
guard su lista vuota (evita 'IN ()').
- update(): conserva assign_mode esistente quando si modifica solo stak_code.
UI/UX/A11Y
- Editor opzioni per domande a scelta singola/multipla (prima degradavano a testo nel portale).
- Etichette stato/tipo localizzate; risposte mostrate inline (no alert()); escAttr nel portale
(escape virgolette negli attributi); ARIA su modali/tab; voce sidebar anche in common.js.
OPEN ITEM (NON regressione C5, pre-esistente e ambientale): l'upload allegati restituisce
UPLOAD_ERROR in prod — move_uploaded_file/is_uploaded_file fallisce nella topologia
proxy→fastcgi (stesso pattern di evidence_files/AuditController, mai funzionato: la dir
uploads/evidence non esiste). rename/copy come www-data funzionano. Da investigare lato infra.
Il resto di C5 (questionari, firma-lettura, commenti, calendario, portale) è pienamente operativo.
Smoke prod OK: allowlist (.html→422, struttura ok), opzioni scelta nel portale, send no-reset,
replace individuale, token_expires_at presente. Additivo. v1.21.1.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completa C5 (Epic C). Gli stakeholder rispondono in self-service tramite magic-link
(token SHA-256 per destinatario, NESSUN account/JWT); il compliance manager vede gli
esiti, i commenti e gli allegati nel dettaglio dell'attività.
- StakeholderPortalController (non-JWT, token-only): access / respond (questionario) /
acknowledge (firma di avvenuta lettura) / comment / attachment. Submit one-shot (409),
validazione risposte obbligatorie, anti-IDOR (un token = un solo destinatario),
auto-completamento attività quando tutti hanno risposto/firmato.
- StakeholderActivityController: feedback (risposte per destinatario), comments
(GET/POST), attachments (upload interno + lista; riuso evidence_files entity_type=
'stk_activity', file sotto public/uploads/stk_activity/{org}/).
- mig.053: stk_activity_responses (answers JSON / acknowledged_at), stk_activity_comments
(interni/esterni). Estende il seeder idempotente.
- Frontend: stk-portal.html (pagina pubblica dependency-free: questionario per tipo di
domanda o testo+firma, commento, upload); dettaglio attività in stakeholder-activities.html
con esiti, thread commenti e allegati.
Email disattivate (kill-switch) → i magic-link si condividono manualmente. Smoke prod OK
(access no-auth, respond+required+409, acknowledge+WRONG_TYPE, comment esterno/interno,
bad-token 404, feedback interno, auto-complete; cleanup org 151 pulita). Additivo. v1.21.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fondazione delle attività verso gli stakeholder (Simon C5 §4):
- Questionari tipo (template): tipo questionario-da-compilare / firma-di-avvenuta-lettura,
domande (JSON) + collegamento m2m a procedure (policies) E a misure/requisiti del
framework (cfg_nis2_misure/requisiti).
- Attività: basate (opz.) su un template, assegnabili a un CODICE stakeholder (tutti
quelli di quel tipo) o a singoli stakeholder; pianificabili con data e scadenza.
- Calendario NIS2: review_schedule esteso (ENUM entity_type += 'stakeholder_activity');
su create/update con scadenza la riga di calendario viene sincronizzata.
- Invio: genera un magic-link per destinatario (token SHA-256) per il portale esterno
(C5.2b). Email disattivate (kill-switch) -> i link si condividono manualmente.
mig.052: stk_questionnaire_templates, stk_template_(procedures|misure|requisiti),
stk_activities, stk_activity_targets, stk_activity_procedures + ALTER review_schedule.
StakeholderActivityController + /api/stakeholder-activities/*; stakeholder-activities.html
(2 tab: attività / questionari tipo + dettaglio invio); voce sidebar V2; help/i18n.
Smoke prod OK (template m2m, attività by_code+calendario, assign, send magic-link,
anti-IDOR individual, negativi 422; cleanup org 151 pulita). Additivo. v1.20.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Registro dedicato degli stakeholder (tabella `stakeholders`, NON una colonna su
suppliers: gli interni non sono fornitori; gli esterni si COLLEGANO opzionalmente
a un supplier esistente senza duplicare il dato).
- mig.051: cfg_stakeholder_types (30 di sistema Stak.01-30 + org-added da Stak.31),
cfg_stakeholder_quadrants (4, range corretti: Q4 potere 0-2), stakeholders,
stakeholder_procedures (m2m -> policies). Seeder idempotente seed_stakeholders.php.
- StakeholderController: list/create/update/delete + types/addType + quadrants + pickers.
Quadrante CALCOLATO a read-time dai range config. Anti-IDOR su org_role/supplier/
policy/tipo; coerenza kind (interni->organigramma, esterni->fornitore); punteggi 0-5.
- api.js: metodi stk*. stakeholders.html riscritta: registro + matrice di Mendelow
in SVG dependency-free + modali (stakeholder, nuovo tipo). Voce sidebar V2 (common-bi.js).
- Help aggiornato; cache-buster ?v=20260626 su tutte le pagine; version 1.19.0; sw cache v1.19.0.
Ancoraggio GV.SC-02 (obbligo, art. 24 D.Lgs. 138/2024). La matrice di Mendelow e'
etichettata come BUONA PRASSI, non obbligo NIS2 (regola fonti-certe). Additivo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Simon C4: nell'inventario DUE sole voci principali; le sottoclassi le crea l'utente.
Riconduzione concordata (no perdita di dettaglio): hardware/network/facility -> ID.AM-01;
software/service/data -> ID.AM-02; personnel -> Organigramma (0 asset personnel presenti).
- Config: cfg_inventory_voci (2: ID.AM-01 Hardware, ID.AM-02 Software/Servizi/Sistemi),
inventory_subclassi (12 default di sistema org NULL + sottoclassi org-scoped).
- assets.voce_code + subclass_id (FK). 20 asset esistenti MIGRATI (5 ID.AM-01 / 15 ID.AM-02).
- AssetController: list con label voce/sottoclasse; create/update validati (voce in whitelist,
sottoclasse appartenente a org/default+voce); GET/POST /assets/subclasses; import (bulkUpsert)
deriva la voce dal tipo legacy. asset_type resta come campo legacy (viste secondarie).
- assets.html: filtro per voce, form con selettore Voce + Sottoclasse (+ '+ aggiungi'),
lista/dettaglio mostrano 'voce · sottoclasse', stat-chips per voce.
- FIX bug pre-esistente: apostrofo non escapato ('l'inventario') che ROMPEVA tutto il JS
della pagina Inventario. + cache-buster common-bi.js (era a v=20260618, non bumpato dal fix C3).
Help + KB + i18n. Verifiche prod: subclasses 200, create voce/legacy OK, validazione 422,
JS valido (node --check), migrazione idempotente. version 1.18.4.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Simon C3: l'analisi gap ACN non è un artefatto normativo e va eliminata; la conformità
emerge da C1 (elenco Misure/Requisiti + valutazione per requisito in org_requisito_state).
Ricognizione → migrazione → backup → rimozione (sicuro, 0 perdite):
- Tabelle acn_assessments/acn_assessment_responses VUOTE (0 righe) -> droppate (mig.049,
guard anti-drop se >0 righe). Ricreabili da 036. Nessun dato utente perso.
- Integrazione lg231: /api/services/gap-analysis legge 'assessments' generale, NON il modulo
ACN -> intatta. Verificato.
- RaciController ripuntato su cfg_nis2_misure (fonte canonica) al posto di acn_measures.json
(rimosso); validazione measure_code verificata (/raci/objects?type=measure -> 43 misure).
Rimossi: AcnAssessmentController, public/acn-gap.html, application/data/acn_measures.json,
routing acn-gap, voci sidebar (common.js + common-bi.js), metodi api.js acn*, sezione help 'acn',
chiavi i18n acn.*, mapping demo-selectors. Tour demo step 3 RIPUNTATO su misure-requisiti
(no step rotto, 10 step). Sidebar BI ora mostra 'Misure e Requisiti' (gap di common-bi.js sanato).
Verifiche prod: /acn-gap.html 404, /api/acn-gap/* 404, /misure-requisiti 200, RaciController OK.
Cache-buster ?v=20260624, version 1.18.3, SW v1.18.3.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Flotta di verifica (14 agenti): sicurezza pulita, fedeltà del dato perfetta (codifica
Simon intatta, 0 scostamenti). Corretti i 6 finding confermati:
- MAJOR: PolicyController::ensureFrameworkProcedures ora in transazione + SELECT...FOR UPDATE
sulla riga org -> no procedure duplicate su fallimento parziale (rollback) o richieste concorrenti.
- MINOR: importer idempotente committato application/cli/seed_framework.php (DDL + upsert da
nis2_framework_seed.json) -> re-seed riproducibile/versionato (verificato 2 run = 0 duplicati).
- MINOR: allineato '92 requisiti' importanti (dato autoritativo Simon) in help.js + nis2_sources.php
(era 87, contraddizione interna); coerente '24 requisiti aggiuntivi essenziali' (116-92).
- MINOR (UI): troncamento anteprima valutazione sul RAW prima di esc() (no entita HTML spezzate).
- MINOR (UI a11y/AGID): modale valutazione con focus iniziale, ripristino focus, focus-trap.
Cache-buster ?v=20260623, version 1.18.2, SW nis2-shell-v1.18.2.
NB residuo da verificare: data esatta Determinazione 333017/2025 (mese senza giorno) in nis2_sources.php.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PolicyController::list ora, al primo accesso al modulo Procedure di un'org reale,
copia le 42 procedure di default (cfg_nis2_procedure) in policies come contenuto
editabile/cancellabile (decisione utente: copia per-org al 1° accesso).
- organizations.framework_procedures_seeded_at = idempotenza (no ri-copia).
- categoria policies assegnata da mappa famiglia NIST->ENUM; nis2_article=misura;
content riporta misure coperte + nota 'personalizzabile e cancellabile'.
- Salta org demo/sandbox (isDemo) per non inquinare il dataset dimostrativo.
- Best-effort: errore nel seed NON blocca la lista (log + return).
Smoke prod: org 151 3->45 policy (42 default, 10 categorie), idempotente al 2° accesso,
golden 151 poi ripristinato (cleanup). Additivo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
org_requisito_state (org x requisito -> stato + valutazione del rischio + note),
la valutazione 'parte vuota'. È da qui che emerge lo stato di conformità (sostituirà il Gap ACN, C3).
- FrameworkController: catalog ora include lo stato per-org di ogni requisito (risoluzione org
OPZIONALE con verifica accesso = anti-IDOR); nuovo POST /framework/state (upsert, requireOrgRole
org_admin/compliance_manager, validazione stato + requisito esistente).
- misure-requisiti.html: colonna Stato/Valutazione (se org) + modale di valutazione (stato,
valutazione del rischio, note). api.frameworkSetState.
Smoke prod: catalog has_org/org_class OK; upsert non_conforme persistito e riletto; ISOLAMENTO
multi-tenant verificato (org 152 non visibile a org 151). Additivo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fondazione data-driven (direttiva utente: configurabile via DB, no JSON hardcoded).
Fonte autoritativa: docs/simon/C1_Misure_Requisiti.xlsx + Vista (codifica Simon NON variata).
Tabelle system-level, sola-lettura per l'utente, configurabili da super_admin:
- cfg_nis2_ambiti (18 categorie NIST GV/ID/PR/DE/RS/RC)
- cfg_nis2_misure (43 misure; 6 solo-essenziali; area politica a..p, applies_important/essential)
- cfg_nis2_requisiti (116 requisiti; 92 importanti / 116 essenziali; ord N immutabile; FK misura/proc/risk)
- cfg_nis2_procedure (42 procedure di default Proc.01-42)
- cfg_nis2_rischi (84 rischi di default Risk.01-85)
Seed via application/data/nis2_framework_seed.json (import-only; runtime legge le cfg_*).
Applicato su prod (additivo, reversibile via DROP). Runner temp self-contained gia rimosso.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Chiude il gap auth: la SPA demo si auto-autentica col demo_jwt (token API nis2_access_token + org dal
payload) PRIMA di checkAuth, cross-origin (niente sessionStorage same-origin). spa_iframe_url ora atterra
su /dashboard.html?demo=..#djwt=... DemoController.DEMO_BASE override via env DEMO_BASE_URL (switch a
sottodominio senza codice). USR2 richiesto per DemoController.
- application/services/DemoSeedService.php: logica seed/clone golden #151 -> 996001/996002 (riusabile)
- DemoController::resetDataset: re-seed reale via service (?scope=demo|sandbox|default both), non piu stub
- scripts/seed-demo-dataset.php: thin CLI wrapper sul service
Testato prod: POST /api/demo/reset-dataset (X-Internal-Key) scope=sandbox -> 200, 996002 ri-seedata.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Increment backend formazione-first (product-demo-protocol v1.0.1):
- BaseController::applyDemoGuard() — SAFE-BY-CONSTRUCTION (flusso auth normale invariato):
riconosce demo_jwt scope=demo:read-only (blocca scritture 403 DEMO_READ_ONLY) e
training:sandbox (scritture solo su org sandbox); contesto sintetico user=0 ruolo
compliance_manager su org range 996xxx; mai super_admin. Short-circuit in requireAuth/
requireOrgAccess/requireOrgRole. php -l OK. DA DEPLOYARE (USR2) + testare quando host disponibile.
- scripts/seed-demo-dataset.php — clona golden DataCore #151 in 996001 (demo RO) + 996002
(sandbox scrivibile), idempotente, richiamabile da resetDataset. php -l OK. DA ESEGUIRE su host.
NB: chiave ssh host revocata a meta-sessione → seed/USR2/push in attesa di ri-provisioning.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Lezione 2026-06-10 (3 outage): forzare SSL su una connessione via socket (DB_HOST
'localhost'/vuoto/path) da' PDOException [2002] "Cannot connect to MySQL using SSL"
su ogni richiesta -> app giu'. sslOptions() ora ritorna [] (resta in chiaro + log) se
DB_HOST indica un socket, anche col flag/env attivo. SSL si applica solo alle
connessioni TCP. Il socket e' IPC locale: nessun transito di rete da cifrare.
Cosi' riaccendere .db_ssl_on mentre l'app e' su socket NON puo' piu' causare outage.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
database.php: Database::sslOptions() — TLS verso MySQL gated (env DB_SSL=true o
flag-file application/config/.db_ssl_on), DEFAULT OFF. PDO::MYSQL_ATTR_SSL_CA +
VERIFY_SERVER_CERT=false; fail-safe se CA assente (resta in chiaro + log: un
VERIFY=false senza CA connetterebbe in chiaro silenziosamente). Merge con '+'
(preserva chiavi-intere PDO). Default-OFF provato inerte (base + [] === base).
Validato da 2 agenti: runtime app coperto 100% da Database::getInstance (0 raw PDO
in application/); review adversariale = SICURO default-OFF, nessun bug.
.gitignore: flag-file + db-ca.pem. Runbook: docs/TLS_DB_PREEQUIP_NIS2.md.
NB topologia verificata: l'app usa il DB CONTAINER (db->172.21.0.4, 8.0.45), non il
MySQL host -> CA + enforce vanno sul container db (a cura VIGILE).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
#1 CRITICO aiAnalyze: askWithRag ritorna ['answer','sources','rag_used'], non
una stringa. Ora estrae 'answer' (ai_summary) e salva 'sources' in
ai_recommendations. Prima salvava il JSON intero in ai_summary.
#2 ALTO corpus RAG: acn_requirements.json aveva 188/203 testi TRONCATI alla
prima riga PDF (es. GV.PO-01#1: 84 char invece di 838). Rigenerato dai testi
INTEGRALI di acn_measures.json (87+116, zero troncamenti). Ri-ingest Qdrant.
#3 MEDIO catalog(): org non classificata dava entity_level=null + warning PHP
$totals[null] + TypeError frontend. Ora 422 ENTITY_LEVEL_REQUIRED come create().
#4 MEDIO guida cap-5 GV.RR-04: "figure chiave dell'organigramma" era errato e
auto-contraddittorio -> "personale autorizzato + amministratori di sistema,
valutazione esperienza/capacita/affidabilita" (allineato testo ACN).
#5 BASSI: openAcn try/catch (no unhandled rejection su Riprendi); badge
importante/essenziale IT/EN; overall_score=null (non 0.0) se tutti N/A.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Assessment di SECONDO LIVELLO sulla Determinazione ACN 164179/2025 (non le 10
lettere generiche Art.21, gia coperte). Distingue soggetti importanti/essenziali:
- IMPORTANTI (All.1): 37 misure, 87 requisiti
- ESSENZIALI (All.2): 43 misure, 116 requisiti
- application/data/acn_measures.json: dataset canonico estratto dai testi
UFFICIALI ACN (Allegati 1+2), testi requisiti INTEGRALI (no troncamenti),
flag per-requisito importante/essenziale. Validato 37/87 + 43/116, zero
discrepanze vs codici di riferimento.
- AcnAssessmentController: catalog/list/create/get/requirements/respond/complete/
report/aiAnalyze. Pre-popola requisiti applicabili per entity_level, scoring
per funzione FW (GOVERN/IDENTIFY/PROTECT/DETECT/RESPOND/RECOVER), grounding AI
sui 203 requisiti ACN gia in KB. Anti-IDOR, snapshot testo immutabile.
- Migrazione 036: acn_assessments + acn_assessment_responses (APPLICATA su host).
- Router: acn-gap controllerMap + actionMap.
Origine: finding revisore (la Gap Analysis Art.21 non e l'autovalutazione ACN).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>