[DOCS] Manuale d'uso COMPLETO v1.14.0: accesso/registrazione/onboarding passo-passo + 18 moduli dall'help reale

Riscrittura completa: istruzioni concrete (login, registrazione 3 step, onboarding
4 step con campi reali, navigazione), per ogni modulo "cosa trovi e come si usa"
estratto dall'help contestuale del prodotto (help.js), FAQ, glossario. Rimosse le
interruzioni di pagina che lasciavano pagine vuote. Generatore self-contained
(estrae i contenuti da public/js/help.js via node).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-06-11 18:19:54 +02:00
co-authored by Claude Opus 4.8
parent 0a883b7cae
commit ae7bb072d7
2 changed files with 222 additions and 309 deletions
Binary file not shown.
+222 -309
View File
@@ -1,287 +1,228 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Genera il Manuale d'uso di NIS2 Agile in .docx usando solo stdlib (zipfile+XML).""" """Manuale d'uso COMPLETO di NIS2 Agile (.docx, solo stdlib). Contenuti moduli da help.js reale."""
import zipfile, re, datetime import zipfile, re, json, html as _html, subprocess, os
from xml.sax.saxutils import escape from xml.sax.saxutils import escape
OUT = "/projects/nis2-agile/docs/MANUALE_UTENTE_NIS2_AGILE.docx" BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # repo root
OUT = os.path.join(BASE, "docs", "MANUALE_UTENTE_NIS2_AGILE.docx")
# ───────────────────────────────────────────────────────────────────────── HELP_JSON = "/tmp/help.json"
# CONTENUTO DEL MANUALE (DSL: lista di blocchi) # Auto-estrae i contenuti dall'help reale del prodotto (public/js/help.js) via node.
# ('title', titolo, sottotitolo, riga_meta) _node = ('const fs=require("fs");let s=fs.readFileSync(process.argv[1],"utf8");'
# ('h1'|'h2'|'h3', testo) | ('p', testo) | ('note', testo) 'const a=s.indexOf("var _helpContent");const start=s.indexOf("{",a);'
# ('ul', [voci]) | ('ol', [voci]) 'const end=s.indexOf("var _pageMap");let obj=s.slice(start,s.lastIndexOf("}",end)+1);'
# ('table', [intestazioni], [[riga],...]) 'fs.writeFileSync(process.argv[2],JSON.stringify(eval("("+obj+")")));')
# ('pb',) page break subprocess.run(["node","-e",_node, os.path.join(BASE,"public","js","help.js"), HELP_JSON], check=True)
# Nel testo: **grassetto** supportato. HELP = json.load(open(HELP_JSON, encoding="utf-8"))
# ─────────────────────────────────────────────────────────────────────────
DATE = "11 giugno 2026" DATE = "11 giugno 2026"
C = [] C = []
def add(*b): C.append(b) def add(*b): C.append(b)
add('title', "NIS2 Agile", "Manuale d'uso del prodotto", def html2mk(s):
"Versione piattaforma 1.14.0 · Aggiornato all'%s" % DATE) """Converte l'HTML dell'help nel markup interno (**bold**) + testo pulito."""
s = re.sub(r'</?strong>|</?b>', '**', s)
s = re.sub(r'<[^>]+>', '', s) # rimuovi altri tag (span/em/...)
s = _html.unescape(s) # &lt; &ge; &nbsp; ...
s = s.replace(' ', ' ')
s = re.sub(r'\s+', ' ', s).strip()
# bilancia eventuali ** dispari
if s.count('**') % 2: s += '**'
return s
# ===== INDICE (statico, i numeri di pagina non sono calcolati) ===== def module_chapter(num, title, topic_key, lead=None):
add('h1', "Indice dei contenuti") t = HELP.get(topic_key, {})
for n in ["1. Introduzione","2. Primi passi (accesso, registrazione, onboarding)", add('h1', f"{num}. {title}")
"3. Dashboard","4. Gap Analysis NIS2 (Art. 21)","5. Gap Analysis ACN (Determinazione 164179/2025)", if lead: add('p', lead)
"6. Modello Organizzativo SGSI (ISO 27001/27017/27018)","7. Gestione dei Rischi", if t.get('intro'): add('p', html2mk(t['intro']))
"8. Gestione degli Incidenti (Art. 23)","9. Policy e procedure","10. Supply Chain (fornitori)", for sec in t.get('sections', []):
"11. Formazione","12. Asset","13. Audit & Report","14. Whistleblowing (Art. 32)", add('h3', html2mk(sec.get('heading','')))
"15. Aggiornamenti normativi","16. Knowledge Base e Assistente AI","17. Segnalazioni (feedback)", items = [html2mk(x) for x in sec.get('items', [])]
"18. Impostazioni","19. Amministrazione","20. Integrazioni e API","21. Glossario","22. Riferimenti normativi"]: if items: add('ul', items)
add('p', n) refs = [html2mk(x) for x in t.get('references', [])]
if refs:
add('h3', "Riferimenti normativi")
add('ul', refs)
# ══════════════════ COPERTINA ══════════════════
add('title', "NIS2 Agile", "Manuale d'uso del prodotto",
f"Guida operativa completa · Versione piattaforma 1.14.0 · {DATE}")
add('p', "Questo manuale spiega passo dopo passo come accedere alla piattaforma, registrarsi, completare l'onboarding aziendale e utilizzare ogni modulo. Per ogni schermata trovi cosa contiene, a cosa serve e come operare.")
add('pb') add('pb')
# ===== 1. INTRODUZIONE ===== # ══════════════════ INDICE ══════════════════
add('h1', "Indice")
toc = ["1. Introduzione","2. Requisiti e accesso alla piattaforma","3. Registrazione di un nuovo utente",
"4. Onboarding: configurazione dell'azienda","5. Come muoversi nell'interfaccia","6. Dashboard",
"7. Gap Analysis NIS2 (Art. 21)","8. Gap Analysis ACN (Determinazione 164179/2025)",
"9. Modello Organizzativo SGSI (ISO 27001/27017/27018)","10. Gestione dei Rischi",
"11. Gestione degli Incidenti (Art. 23)","12. Policy e procedure","13. Supply Chain (fornitori)",
"14. Formazione","15. Asset","16. Audit & Report","17. Whistleblowing (Art. 32)",
"18. Aggiornamenti normativi","19. Knowledge Base","20. Compliance Journey","21. Segnalazioni (feedback)",
"22. Impostazioni","23. Gestione delle aziende (consulenti)","24. Domande frequenti (FAQ)",
"25. Glossario","26. Riferimenti normativi"]
for x in toc: add('p', x)
add('note', "I nomi delle voci e l'aspetto delle schermate possono variare leggermente con gli aggiornamenti. In ogni pagina è disponibile il pulsante di aiuto contestuale (icona ?) con la guida e i riferimenti normativi.")
# ══════════════════ 1. INTRODUZIONE ══════════════════
add('h1', "1. Introduzione") add('h1', "1. Introduzione")
add('h2', "1.1 Che cos'è NIS2 Agile") add('p', "**NIS2 Agile** è una piattaforma web (SaaS) che accompagna le aziende nella conformità alla **Direttiva (UE) 2022/2555 (NIS2)**, al suo recepimento italiano (**D.Lgs. 138/2024**) e alle **Determinazioni dell'Agenzia per la Cybersicurezza Nazionale (ACN)**. Integra l'intelligenza artificiale per gap analysis, generazione di policy, classificazione degli incidenti e suggerimento dei rischi.")
add('p', "NIS2 Agile è una piattaforma SaaS multi-tenant che supporta le aziende nel percorso di conformità alla **Direttiva (UE) 2022/2555 (NIS2)** e al suo recepimento italiano (**D.Lgs. 138/2024**), comprese le **Determinazioni dell'Agenzia per la Cybersicurezza Nazionale (ACN)**. Integra l'intelligenza artificiale (Claude) per gap analysis, generazione di policy, classificazione degli incidenti e suggerimento dei rischi.") add('p', "Dalla versione 1.14.0 include anche il **Modello Organizzativo SGSI** secondo **ISO/IEC 27001:2022**, con estensioni cloud ISO 27017/27018.")
add('p', "Dalla versione 1.14.0 la piattaforma include anche un modulo guidato per la costruzione del **Modello Organizzativo SGSI secondo ISO/IEC 27001:2022**, con estensioni cloud ISO/IEC 27017:2015 e 27018:2019.") add('h3', "A chi è rivolto")
add('h2', "1.2 A chi è rivolto") add('ul', ["**PMI** che devono adeguarsi agli obblighi NIS2;",
add('ul', ["**PMI** che devono adeguarsi agli obblighi NIS2 con risorse limitate;", "**Grandi imprese** con processi di compliance strutturati;",
"**Grandi imprese / Enterprise** con processi di compliance strutturati;", "**Consulenti e CISO** che seguono più aziende clienti tramite uno studio."])
"**Consulenti e CISO** che gestiscono più organizzazioni clienti tramite uno studio (consulting firm)."]) add('h3', "Come è organizzato il lavoro nella piattaforma")
add('h2', "1.3 Quadro normativo di riferimento") add('p', "Il percorso tipico è: **(1)** accedi o registrati → **(2)** completa l'onboarding dell'azienda (che ne determina la classificazione NIS2) → **(3)** esegui la Gap Analysis NIS2 → **(4)** approfondisci con la Gap Analysis ACN e costruisci il Modello SGSI → **(5)** gestisci rischi, incidenti, policy, fornitori, formazione e audit nel tempo.")
add('table', ["Fonte","Ruolo"], add('note', "Disclaimer: NIS2 Agile è uno strumento di supporto operativo e pre-audit. Non costituisce consulenza legale né parere professionale vincolante e non rilascia certificazioni ISO. Le valutazioni proposte vanno confermate con il referente compliance/legale dell'organizzazione.")
[["Direttiva (UE) 2022/2555 (NIS2)","Principi e obblighi europei di cibersicurezza"],
["D.Lgs. 138/2024","Recepimento italiano della NIS2 (obblighi operativi)"],
["Determinazione ACN 164179/2025","Misure di sicurezza di base, classificazione e notifica incidenti (Allegati)"],
["ISO/IEC 27001:2022","Requisiti del Sistema di Gestione della Sicurezza delle Informazioni (best practice)"],
["ISO/IEC 27017:2015 / 27018:2019","Controlli per servizi cloud / protezione PII in cloud (best practice)"]])
add('note', "Disclaimer: NIS2 Agile è uno strumento di supporto operativo e pre-audit. Non costituisce consulenza legale né parere professionale vincolante, e non rilascia certificazioni ISO. Le valutazioni proposte (perimetro, applicabilità, significatività) vanno confermate con il referente compliance/legale dell'organizzazione.")
# ===== 2. PRIMI PASSI ===== # ══════════════════ 2. ACCESSO ══════════════════
add('pb'); add('h1', "2. Primi passi") add('h1', "2. Requisiti e accesso alla piattaforma")
add('h2', "2.1 Accesso e indirizzo") add('h3', "Requisiti")
add('p', "La piattaforma è raggiungibile all'indirizzo **https://nis2.agile.software/**. L'accesso avviene con email e password (autenticazione JWT). Se la tua organizzazione usa il Single Sign-On centralizzato (AgileHub), le credenziali sono condivise tra i prodotti della suite.") add('ul', ["Un browser web aggiornato (Chrome, Edge, Firefox o Safari);",
add('h2', "2.2 Registrazione") "una connessione a Internet;",
add('ol', ["Apri la pagina di **Registrazione** e inserisci i dati richiesti (email, password conforme alla policy di sicurezza).", "le credenziali di accesso (email e password) oppure un invito ricevuto dal proprio studio/partner."])
"Al termine vieni indirizzato automaticamente alla procedura di **onboarding** aziendale.", add('h3', "Indirizzo della piattaforma")
"Se possiedi un **invito/licenza** (token `inv_...`) fornito da un partner, inseriscilo per attivare l'organizzazione pre-configurata."]) add('p', "Apri il browser e vai su **https://nis2.agile.software/**. Si apre la pagina di accesso.")
add('h2', "2.3 Recupero password") add('h3', "Come accedere (login)")
add('p', "Dalla pagina di login usa **Password dimenticata** per ricevere un link di reset via email. Il cambio password effettuato da AgileHub si propaga automaticamente entro pochi minuti grazie al sync SSO.") add('ol', ["Nel campo **Indirizzo Email** inserisci l'email con cui sei registrato (es. nome@azienda.it).",
add('h2', "2.4 Onboarding dell'organizzazione") "Nel campo **Password** inserisci la tua password. Puoi usare l'icona a forma di occhio per mostrarla/nasconderla.",
add('p', "Il wizard di onboarding crea l'organizzazione e ne determina la classificazione NIS2. Si articola in quattro passi:") "Premi **Accedi**.",
add('ol', ["**Dati azienda** — inserimento manuale oppure precompilazione tramite **Partita IVA** (lookup automatico) o caricamento della **visura camerale** (estrazione AI dei dati).", "Se le credenziali sono corrette: se hai già un'azienda configurata vieni portato alla **Dashboard**, altrimenti alla procedura di **Onboarding**."])
"**Verifica e completamento** — settore (ATECO → settore NIS2), numero dipendenti, fatturato, sede, contatti.", add('p', "In caso di errore compare un messaggio in rosso sotto il titolo. Verifica email e password e riprova.")
"**Profilo utente** — nome, ruolo, telefono.", add('h3', "Password dimenticata")
"**Classificazione NIS2** — la piattaforma propone se il soggetto è **essenziale**, **importante** o **non applicabile** in base a settore e dimensione; è possibile aderire volontariamente."]) add('ol', ["Dalla pagina di accesso scegli **Password dimenticata**.",
add('note', "La classificazione (essenziale/importante) determina il perimetro applicabile in più moduli, in particolare la Gap Analysis ACN e gli obblighi sugli incidenti. Puoi rivederla in Impostazioni › Classificazione NIS2.") "Inserisci l'email dell'account: riceverai un messaggio con un link per impostare una nuova password.",
add('h2', "2.5 Ruoli e permessi") "Apri il link, scegli la nuova password (rispettando i requisiti di sicurezza) e conferma."])
add('table', ["Ruolo","Descrizione"], add('note', "Se la tua azienda usa il Single Sign-On della suite (AgileHub), la password è condivisa tra i prodotti: un cambio password si propaga automaticamente entro pochi minuti.")
[["super_admin","Amministratore di piattaforma (accesso a tutte le organizzazioni)"], add('h3', "Disconnessione e sessioni")
["org_admin","Amministratore dell'organizzazione"], add('p', "Per uscire usa la voce **Esci/Logout** nel menu utente. Dalle Impostazioni puoi vedere i **dispositivi collegati** (sessioni attive) e revocarli singolarmente o tutti.")
["compliance_manager","Responsabile compliance: gestisce assessment, rischi, policy, SGSI"],
["board_member","Organo direttivo: visione e approvazioni"],
["auditor","Revisore: lettura e compilazione evidenze/controlli"],
["employee","Dipendente: formazione, segnalazioni"],
["consultant","Consulente di studio che opera su più organizzazioni clienti"]])
add('h2', "2.6 Multi-organizzazione, lingua e aiuto")
add('ul', ["Chi appartiene a più organizzazioni (es. consulenti) può **cambiare contesto** dall'intestazione/menu.",
"L'interfaccia è disponibile in **Italiano** ed **Inglese** (selettore lingua).",
"Ogni pagina dispone di **Aiuto contestuale** (icona ?) con guida e riferimenti normativi certi."])
# ===== 3. DASHBOARD ===== # ══════════════════ 3. REGISTRAZIONE ══════════════════
add('pb'); add('h1', "3. Dashboard") add('h1', "3. Registrazione di un nuovo utente")
add('p', "La Dashboard è la pagina iniziale dopo l'accesso e offre una visione d'insieme dello stato di conformità.") add('p', "Se non hai un account, dalla pagina di accesso scegli **Registrati**. La registrazione è una procedura guidata in tre passi, con un indicatore di avanzamento in alto.")
add('ul', ["**Avanzamento implementazione misure Art. 21 NIS2** — indicatore complessivo (gauge);", add('h3', "Passo 1 — Ruolo e (eventuale) codice invito")
"**Scadenze imminenti** — adempimenti e revisioni in arrivo da più moduli;", add('p', "Scegli il **ruolo** con cui ti registri, selezionando una delle schede disponibili:")
"**Attività recenti** — ultime azioni tracciate;", add('table', ["Scheda ruolo","Quando sceglierlo"],
"**Heatmap dei rischi** — distribuzione dei rischi su probabilità × impatto;", [["Responsabile compliance","Gestisci direttamente assessment, rischi, policy e SGSI dell'azienda"],
"**Benchmark settoriale** (anonimizzato) — confronto con il proprio settore;", ["Amministratore organizzazione","Amministri l'azienda, gli utenti e le impostazioni"],
"**Compliance Journey** — percorso guidato che indica i prossimi passi consigliati."]) ["Consulente","Segui più aziende clienti per conto di uno studio"],
["Auditor","Esegui verifiche e compili evidenze/controlli (lettura prevalente)"],
["Membro del board","Organo direttivo: visione e approvazioni"]])
add('p', "Se hai ricevuto un **Codice Invito** da un partner o dal tuo studio (formato `inv_...`), inseriscilo nell'apposito campo: il sistema lo verifica e mostra l'azienda/piano associati. Premi **Avanti** per proseguire.")
add('h3', "Passo 2 — Dati personali e password")
add('ul', ["**Nome** e **Cognome** (es. Mario Rossi);",
"**Email** (sarà il tuo identificativo di accesso);",
"eventuale **Partita IVA** dell'azienda (verificata in tempo reale, dove richiesto);",
"**Password** (minimo 8 caratteri): un indicatore mostra la robustezza della password mentre la digiti;",
"**Conferma password**: ripeti la stessa password."])
add('h3', "Passo 3 — Conferma e avvio")
add('p', "Rivedi i dati, accetta le condizioni e completa la registrazione. Al termine vieni indirizzato automaticamente all'**Onboarding** per configurare l'azienda (se non sei entrato tramite un invito che la pre-configura).")
# ===== 4. GAP ANALYSIS NIS2 ===== # ══════════════════ 4. ONBOARDING ══════════════════
add('pb'); add('h1', "4. Gap Analysis NIS2 (Art. 21)") add('h1', "4. Onboarding: configurazione dell'azienda")
add('p', "Analisi di **primo livello** sulle 10 categorie di misure dell'Art. 21.2 NIS2, basata su un questionario di 80 domande (10 categorie), ciascuna collegata anche al controllo ISO 27001 corrispondente.") add('p', "L'onboarding crea l'organizzazione e ne stabilisce la **classificazione NIS2** (essenziale, importante o non applicabile), che determina gli obblighi e il perimetro applicabile negli altri moduli. È una procedura guidata con uno **stepper** in alto che mostra i passi.")
add('h2', "4.1 Come si svolge") add('h3', "Passo 1 — Inserisci la Partita IVA")
add('ol', ["Crea un nuovo assessment: la piattaforma pre-popola tutte le domande.", add('p', "Nella prima schermata (\"Inserisci la Partita IVA dell'azienda\") digiti la **Partita IVA** (es. 12345678901) e premi il pulsante di ricerca: la piattaforma tenta di **recuperare automaticamente** i dati aziendali. In alternativa puoi caricare la **visura camerale** (i dati vengono estratti dall'AI) oppure proseguire e inserire tutto manualmente. Premi **Continua**.")
"Per ogni domanda indica lo stato: **Attuato**, **Parziale**, **Non attuato**, **Non applicabile**. Le risposte si salvano automaticamente.", add('h3', "Passo 2 — Verifica e completa i dati aziendali")
"Completa l'assessment per ottenere un **punteggio** per categoria e complessivo.", add('p', "Nella schermata \"Verifica e Completa i Dati Aziendali\" un banner indica la fonte dei dati (visura, lookup P.IVA o inserimento manuale). Controlla e completa i campi:")
"Avvia l'**analisi AI** per ricevere sintesi esecutiva, priorità, quick win e una roadmap di conformità."]) add('ul', ["**Ragione sociale** (es. Acme S.r.l.);",
add('note', "Le risposte della Gap Analysis NIS2 alimentano automaticamente il Modello SGSI (capitolo 6): il loro stato pre-popola lo Statement of Applicability ISO 27001.") "**Partita IVA** e **Codice Fiscale**;",
"**Indirizzo**, **Città**, **Sito web**;",
"**Email aziendale** e **Telefono aziendale**;",
"**Settore** — scegli dall'elenco il settore NIS2 (es. Energia, Trasporti, Sanità, Infrastrutture digitali, ecc.): è determinante per la classificazione;",
"**Numero di dipendenti** e **fatturato** annuo, usati per stabilire la dimensione dell'impresa."])
add('h3', "Passo 3 — Il tuo profilo")
add('p', "Inserisci i dati del tuo profilo utente: **nome completo**, **ruolo** in azienda e **telefono** di contatto.")
add('h3', "Passo 4 — Classificazione NIS2")
add('p', "In base a settore e dimensione, la piattaforma **propone la classificazione**: soggetto **essenziale**, **importante** oppure **non applicabile**. Se non rientri obbligatoriamente ma vuoi adeguarti, puoi indicare l'**adesione volontaria**. Conferma per completare l'onboarding e accedere alla Dashboard.")
add('note', "Puoi rivedere o aggiornare la classificazione in qualsiasi momento da Impostazioni › Classificazione NIS2. La classificazione filtra automaticamente, ad esempio, i requisiti applicabili nella Gap Analysis ACN e gli obblighi sugli incidenti.")
# ===== 5. GAP ANALYSIS ACN ===== # ══════════════════ 5. NAVIGAZIONE ══════════════════
add('pb'); add('h1', "5. Gap Analysis ACN (Determinazione 164179/2025)") add('h1', "5. Come muoversi nell'interfaccia")
add('p', "Analisi di **secondo livello** sulle misure e i requisiti puntuali stabiliti dall'ACN, articolati secondo il **Framework Nazionale** (funzioni Governo GV, Identificazione ID, Protezione PR, Rilevamento DE, Risposta RS, Ripristino RC).") add('p', "Dopo l'accesso l'interfaccia è composta da una **barra laterale** (sidebar) a sinistra con il menu, e dall'**area principale** a destra che mostra la pagina selezionata. In alto a destra di ogni pagina trovi il pulsante di **aiuto contestuale (?)** e le azioni della pagina.")
add('table', ["Classificazione","Perimetro applicabile"], add('h3', "Le sezioni del menu")
[["Soggetto IMPORTANTE (Allegato 1)","37 misure / 87 requisiti"], add('table', ["Sezione","Voci"],
["Soggetto ESSENZIALE (Allegato 2)","43 misure / 116 requisiti"]]) [["Principale","Dashboard, Compliance Journey, Gap Analysis (NIS2), Gap Analysis ACN, Modello SGSI (ISO 27001)"],
add('ul', ["Il perimetro è filtrato automaticamente in base alla classificazione NIS2 dell'organizzazione;", ["Gestione","Rischi, Incidenti, Policy, Supply Chain, Segnalazioni (whistleblowing), Normative, AI Cross-Analysis, Knowledge Base"],
"scoring per funzione del Framework Nazionale e punteggio complessivo;", ["Operativo","Formazione, Asset, Audit & Report"],
"**piano d'azione** dei requisiti non conformi e **analisi AI** ancorata ai 203 requisiti ACN reali (nessun riferimento inventato);", ["Sistema","Guida all'uso, Impostazioni, Architettura, Simulazione Demo, Integrazioni Esterne"]])
"**Catalogo requisiti** consultabile in sola lettura."]) add('h3', "Lingua e aiuto")
add('ul', ["L'interfaccia è disponibile in **Italiano** ed **Inglese** (selettore lingua).",
"Il pulsante **?** in ogni pagina apre la guida contestuale con spiegazioni e riferimenti normativi.",
"Chi appartiene a più aziende (consulenti) può **cambiare organizzazione** dal menu."])
# ===== 6. MODELLO SGSI ISO 27001 (NUOVO) ===== # ══════════════════ 6..23 MODULI (da help.js) ══════════════════
add('pb'); add('h1', "6. Modello Organizzativo SGSI (ISO 27001/27017/27018)") module_chapter(6, "Dashboard", "dashboard")
add('p', "Procedura guidata per costruire il **Sistema di Gestione della Sicurezza delle Informazioni (SGSI / ISMS)** secondo **ISO/IEC 27001:2022** (clausole 4–10) e il relativo **Statement of Applicability (SoA)** sui 93 controlli dell'Annex A. È uno strumento di supporto e pre-audit, non una certificazione.") module_chapter(7, "Gap Analysis NIS2 (Art. 21)", "assessment",
add('h2', "6.1 Il wizard in 6 passi") "Apri la voce **Gap Analysis** dal menu. È l'analisi di primo livello sulle 10 categorie di misure dell'Art. 21 NIS2.")
add('table', ["Passo","Clausola ISO 27001","Contenuto"], module_chapter(8, "Gap Analysis ACN (Determinazione 164179/2025)", "acn",
[["1. Contesto e Ambito","cl. 4","Scope del SGSI, contesto interno/esterno, parti interessate, confini, esclusioni motivate, attivazione estensioni cloud"], "Apri la voce **Gap Analysis ACN**. È l'analisi di secondo livello sui requisiti puntuali ACN.")
["2. Leadership","cl. 5","Ruoli e responsabilità con matrice RACI (Responsible, Accountable, Consulted, Informed)"], module_chapter(9, "Modello Organizzativo SGSI (ISO 27001/27017/27018)", "isms",
["3. Risk","cl. 6","Metodologia di risk assessment e obiettivi del SGSI (il registro rischi resta nel modulo Rischi)"], "Apri la voce **Modello SGSI (ISO 27001)**. Una procedura guidata in 6 passi costruisce il Sistema di Gestione della Sicurezza delle Informazioni; lo Statement of Applicability è pre-popolato dalle risposte della Gap Analysis NIS2.")
["4. Statement of Applicability","cl. 6.1.3","Applicabilità, stato e motivazione dei 93 controlli Annex A + estensioni cloud"], module_chapter(10, "Gestione dei Rischi", "risks", "Apri la voce **Rischi**.")
["5. Documented Information","cl. 7–8","Policy e procedure, con bozze generabili dall'AI"], module_chapter(11, "Gestione degli Incidenti (Art. 23)", "incidents", "Apri la voce **Incidenti**.")
["6. Monitoraggio e Miglioramento","cl. 9–10","Audit interni e non conformità (moduli Audit & Report e NCR/CAPA); checklist di completamento"]]) module_chapter(12, "Policy e procedure", "policies", "Apri la voce **Policy**.")
add('h2', "6.2 SoA pre-popolato dalla Gap Analysis NIS2") module_chapter(13, "Supply Chain (fornitori)", "supply-chain", "Apri la voce **Supply Chain**.")
add('p', "Ogni domanda della Gap Analysis NIS2 è collegata a un controllo ISO 27001 (es. A.5.1). Per questo lo Statement of Applicability viene **pre-compilato** a partire dalle risposte NIS2 già date: i controlli con corrispondenza riportano il badge **\"da NIS2\"** e uno stato proposto (Attuato = 100%, Parziale = 50%, Non attuato = 0%). I controlli non coperti dal NIS2 (es. parte dei controlli fisici A.7) restano da compilare manualmente.") module_chapter(14, "Formazione", "training", "Apri la voce **Formazione**.")
add('note', "Il pulsante \"Ri-deriva dal NIS2\" aggiunge soltanto i controlli mancanti e non sovrascrive le modifiche manuali già inserite.") module_chapter(15, "Asset", "assets", "Apri la voce **Asset**.")
add('h2', "6.3 Estensioni cloud (condizionali)") module_chapter(16, "Audit & Report", "reports", "Apri la voce **Audit & Report**.")
add('ul', ["**ISO/IEC 27017:2015** — attivala se usi servizi cloud pubblici o sei un fornitore cloud: aggiunge 7 controlli cloud (CLD.*) al SoA;", module_chapter(17, "Whistleblowing (Art. 32)", "whistleblowing", "Apri la voce **Segnalazioni**.")
"**ISO/IEC 27018:2019** — attivala se tratti dati personali (PII) in cloud pubblico come responsabile: aggiunge i controlli privacy (PII.*), sinergici con il GDPR.", module_chapter(18, "Aggiornamenti normativi", "normative", "Apri la voce **Normative**.")
"Le sezioni cloud/PII compaiono nel SoA **solo** se attivi i relativi flag nel passo 1."]) module_chapter(19, "Knowledge Base", "kb", "Apri la voce **Knowledge Base**.")
add('h2', "6.4 Documenti e generazione AI") module_chapter(20, "Compliance Journey", "workflow", "Apri la voce **Compliance Journey**: un percorso guidato che indica i prossimi passi consigliati.")
add('p', "Nel passo 5 puoi generare bozze di policy e procedure (Policy SGSI, Dichiarazione di Applicabilità, controllo accessi, gestione incidenti, continuità, sicurezza fornitori). Le bozze sono prodotte dall'AI con ancoraggio alle fonti certe e richiedono **revisione umana** prima dell'approvazione.") module_chapter(21, "Segnalazioni (feedback)", "feedback",
add('h2', "6.5 Esportazione") "Usa lo strumento di feedback per segnalare problemi o suggerimenti: ogni segnalazione cattura automaticamente versione e superficie del prodotto.")
add('p', "Il pulsante **Esporta SGSI** apre una versione stampabile del modello (ambito, metodologia, SoA con stato dei controlli), utile come evidenza in vista di un audit.") module_chapter(22, "Impostazioni", "settings", "Apri la voce **Impostazioni**.")
module_chapter(23, "Gestione delle aziende (consulenti)", "companies",
"Disponibile per i consulenti: consente di gestire più aziende clienti da un unico accesso.")
# ===== 7. RISCHI ===== # ══════════════════ 24. FAQ ══════════════════
add('pb'); add('h1', "7. Gestione dei Rischi") add('h1', "24. Domande frequenti (FAQ)")
add('p', "Registro dei rischi con matrice **Probabilità × Impatto 5×5** e supporto AI, conforme all'Art. 21 NIS2 e alla metodologia ISO 27005.") add('h3', "Non riesco ad accedere")
add('ul', ["Creazione del rischio con scala probabilità/impatto (dettagli ISO 27005);", add('p', "Verifica email e password; usa **Password dimenticata** per reimpostarla. Se usi il SSO della suite, accedi con le credenziali centralizzate.")
"**Piani di trattamento** (mitigare, accettare, trasferire, evitare) e stato di avanzamento;", add('h3', "Ho sbagliato la classificazione NIS2")
"**Suggerimento AI** dei rischi per settore e asset (dati anonimizzati);", add('p', "Puoi correggerla in **Impostazioni › Classificazione NIS2**: il perimetro dei moduli (es. Gap Analysis ACN) si aggiorna di conseguenza.")
"Funzioni avanzate: registro quantitativo **FAIR** (calcolo ALE Monte Carlo) e dashboard **KRI**."]) add('h3', "Lo Statement of Applicability del Modello SGSI è quasi vuoto")
add('p', "Viene pre-popolato dalle risposte della **Gap Analysis NIS2**: completa prima quell'assessment, poi usa **Ri-deriva dal NIS2** nel SoA. I controlli senza corrispondenza NIS2 (es. alcuni controlli fisici) vanno compilati manualmente.")
add('h3', "Le email non vengono inviate")
add('p', "Negli ambienti dimostrativi l'invio email può essere disattivato per sicurezza. In produzione le notifiche (CSIRT, formazione, inviti) vengono inviate regolarmente.")
add('h3', "Le bozze generate dall'AI sono definitive?")
add('p', "No: sono **bozze** da rivedere e approvare. Riportano sempre un avviso e citano le fonti normative.")
# ===== 8. INCIDENTI ===== # ══════════════════ 25. GLOSSARIO ══════════════════
add('pb'); add('h1', "8. Gestione degli Incidenti (Art. 23)") add('h1', "25. Glossario")
add('p', "Gestione del ciclo di vita degli incidenti significativi e degli obblighi di notifica al CSIRT secondo l'Art. 23 e la Determinazione ACN 164179/2025.")
add('h2', "8.1 Tempistiche di notifica")
add('table', ["Adempimento","Termine"],
[["Pre-allarme (early warning)","entro 24 ore dalla conoscenza"],
["Notifica completa","entro 72 ore dalla conoscenza"],
["Relazione finale","entro 1 mese dalla notifica delle 72 ore"]])
add('h2', "8.2 Funzioni")
add('ul', ["**Albero decisionale** per valutare la significatività (utenti ≥ 500, durata > 4h, cross-border, attacco cyber, danni > €100k);",
"**timeline** dell'incidente e metriche (triage, contenimento, eradicazione, ripristino);",
"invio di **early warning, notifica e relazione finale** al CSIRT;",
"**classificazione AI** (tipo IS-1..IS-4, severità) con citazione della fonte (Allegato 3 = importanti, Allegato 4 = essenziali);",
"**PIR** (Post-Incident Review) per l'apprendimento."])
# ===== 9. POLICY =====
add('pb'); add('h1', "9. Policy e procedure")
add('ul', ["Creazione di policy da **template** o **generazione AI** (bozze marcate da revisionare);",
"flusso di **approvazione** e **versioning** con confronto delle versioni (diff);",
"**attestazione** di presa visione da parte del personale e relativo tracciamento."])
add('note', "Le bozze generate dall'AI riportano un banner di avviso: vanno riviste e approvate da un responsabile prima della pubblicazione.")
# ===== 10. SUPPLY CHAIN =====
add('pb'); add('h1', "10. Supply Chain (fornitori)")
add('p', "Gestione della sicurezza della catena di approvvigionamento (Art. 21.2.d).")
add('ul', ["Anagrafica fornitori, categorie e **import** massivo;",
"**questionari** configurabili (categorie, template, domande) inviati ai fornitori;",
"**portale fornitore** dedicato con accesso separato via OTP/magic-link (autosalvataggio risposte);",
"**campagne** di valutazione e cruscotto del rischio fornitori (risk overview)."])
# ===== 11. FORMAZIONE =====
add('pb'); add('h1', "11. Formazione")
add('p', "Gestione della formazione e dell'igiene informatica (Art. 21.2.g) e degli obblighi formativi per gli organi direttivi (Art. 20).")
add('ul', ["Catalogo **corsi** e **assegnazioni** al personale;",
"monitoraggio della **compliance formativa** e dei corsi obbligatori non completati."])
# ===== 12. ASSET =====
add('pb'); add('h1', "12. Asset")
add('p', "Inventario degli asset informativi e delle dipendenze.")
add('ul', ["Anagrafica asset e **import** (CMDB/cloud);",
"**scoring di rilevanza NIS2** (0–100, criterio GV.OC-04) e classificazione dei sistemi rilevanti;",
"**mappa delle dipendenze** tra asset."])
# ===== 13. AUDIT & REPORT =====
add('pb'); add('h1', "13. Audit & Report")
add('ul', ["Gestione dei **controlli di conformità** (framework NIS2 / ISO 27001) con stato ed evidenze;",
"caricamento **evidenze** e registro dei **log di audit**;",
"**mappature**: NIS2 ↔ ISO 27001 e NIST CSF 2.0; **registro dei sistemi rilevanti** (GV.OC-04);",
"**report esecutivo** stampabile ed **export CSV**;",
"**catena di integrità (hash chain SHA-256)**: ogni voce del log è concatenata alla precedente; verifica integrità ed **export certificato**."])
# ===== 14. WHISTLEBLOWING =====
add('pb'); add('h1', "14. Whistleblowing (Art. 32)")
add('p', "Canale per le segnalazioni, anche anonime.")
add('ul', ["Invio segnalazione (anche **anonima**, con codice di tracciamento);",
"assegnazione, gestione e chiusura della segnalazione;",
"consultazione anonima dello stato tramite codice di tracciamento."])
# ===== 15. NORMATIVE =====
add('pb'); add('h1', "15. Aggiornamenti normativi")
add('ul', ["Feed degli aggiornamenti **NIS2 / ACN / DORA**;",
"presa visione (**ACK**) tracciata per ciascun aggiornamento;",
"elenco aggiornamenti **in attesa** di presa visione e statistiche."])
# ===== 16. KB & AI =====
add('pb'); add('h1', "16. Knowledge Base e Assistente AI")
add('h2', "16.1 Knowledge Base (RAG)")
add('p', "La Knowledge Base alimenta le risposte dell'AI tramite ricerca semantica (RAG). Prevede tre livelli di visibilità:")
add('table', ["Livello","Chi possiede","Chi vede"],
[["SYSTEM","Fornitore della piattaforma","Tutti gli utenti"],
["FIRM","Studio di consulenza","Collaboratori dello studio + organizzazioni condivise"],
["ORG","Singola organizzazione","Solo gli utenti di quella organizzazione"]])
add('ul', ["Caricamento documenti (testo) con scelta dello scope;",
"ricerca semantica e cancellazione documenti;",
"le fonti normative certe (NIS2, D.Lgs. 138/2024, Determinazioni ACN) e i cataloghi **ISO 27001/27002/27017/27018** sono già indicizzati a livello SYSTEM."])
add('h2', "16.2 Assistente AI")
add('p', "L'assistente risponde alle domande pescando dalla Knowledge Base e **cita sempre la fonte normativa pertinente**, senza inventare riferimenti. Le risposte sono di orientamento operativo e non sostituiscono il parere legale.")
# ===== 17. FEEDBACK =====
add('pb'); add('h1', "17. Segnalazioni (feedback)")
add('p', "Strumento integrato per segnalare bug o suggerimenti di UX. Le segnalazioni vengono classificate dall'AI e, una volta risolte, l'esito può essere comunicato ai membri dell'organizzazione. Ogni segnalazione cattura automaticamente la **versione del prodotto** e la **superficie** (web/mobile) per facilitare la diagnosi.")
# ===== 18. IMPOSTAZIONI =====
add('pb'); add('h1', "18. Impostazioni")
add('ul', ["**Organizzazione** — dati, classificazione NIS2;",
"**Profilo** — dati personali, cambio password, preferenze;",
"**Membri** — invito e gestione degli utenti e dei ruoli;",
"**Sessioni** — gestione dei dispositivi collegati (multi-device) e revoca;",
"**Branding** (white-label per studi) — personalizzazione per lo studio di consulenza."])
# ===== 19. AMMINISTRAZIONE =====
add('pb'); add('h1', "19. Amministrazione")
add('p', "Area riservata al ruolo **super_admin**: gestione delle organizzazioni, degli utenti e statistiche di piattaforma.")
# ===== 20. INTEGRAZIONI E API =====
add('pb'); add('h1', "20. Integrazioni e API")
add('ul', ["**Services API** (autenticazione tramite API Key con scope) per integrazioni esterne, es. GRC/231: gap-analysis, misure, incidenti, formazione, scadenze e snapshot completo;",
"**Webhook** con firma HMAC-SHA256 e retry, per notifiche verso sistemi terzi;",
"**Licenze/inviti B2B** per provisioning di organizzazioni da parte di partner;",
"**SSO** centralizzato della suite AgileHub (sincronizzazione password)."])
# ===== 21. GLOSSARIO =====
add('pb'); add('h1', "21. Glossario")
add('table', ["Termine","Significato"], add('table', ["Termine","Significato"],
[["NIS2","Direttiva (UE) 2022/2555 sulla cibersicurezza"], [["NIS2","Direttiva (UE) 2022/2555 sulla cibersicurezza"],
["ACN","Agenzia per la Cybersicurezza Nazionale"], ["ACN","Agenzia per la Cybersicurezza Nazionale"],
["Soggetto essenziale / importante","Categorie NIS2 che determinano gli obblighi applicabili"],
["Gap Analysis","Analisi degli scostamenti tra stato attuale e requisiti"],
["SGSI / ISMS","Sistema di Gestione della Sicurezza delle Informazioni (ISO 27001)"], ["SGSI / ISMS","Sistema di Gestione della Sicurezza delle Informazioni (ISO 27001)"],
["SoA","Statement of Applicability: dichiarazione di applicabilità dei controlli"], ["SoA","Statement of Applicability: dichiarazione di applicabilità dei controlli"],
["Annex A","Elenco dei 93 controlli di sicurezza di ISO/IEC 27001:2022"], ["Annex A","I 93 controlli di sicurezza di ISO/IEC 27001:2022"],
["RACI","Matrice di responsabilità: Responsible, Accountable, Consulted, Informed"], ["RACI","Matrice di responsabilità: Responsible, Accountable, Consulted, Informed"],
["CSIRT","Computer Security Incident Response Team (autorità di notifica)"], ["CSIRT","Squadra nazionale di risposta agli incidenti (autorità di notifica)"],
["NCR / CAPA","Non conformità / azioni correttive e preventive"],
["PIR","Post-Incident Review (revisione post-incidente)"], ["PIR","Post-Incident Review (revisione post-incidente)"],
["RAG","Retrieval-Augmented Generation: AI con recupero da knowledge base"], ["RAG","Recupero da knowledge base a supporto delle risposte AI"],
["PII","Personally Identifiable Information (dati personali)"], ["PII","Dati personali (Personally Identifiable Information)"]])
["Soggetto essenziale / importante","Categorie NIS2 che determinano gli obblighi applicabili"]])
# ===== 22. RIFERIMENTI NORMATIVI ===== # ══════════════════ 26. RIFERIMENTI ══════════════════
add('pb'); add('h1', "22. Riferimenti normativi") add('h1', "26. Riferimenti normativi")
add('ul', ["Direttiva (UE) 2022/2555 (NIS2);", add('ul', ["Direttiva (UE) 2022/2555 (NIS2);",
"Direttiva (UE) 2022/2557 (CER);", "Direttiva (UE) 2022/2557 (CER);",
"D.Lgs. 4 settembre 2024, n. 138;", "D.Lgs. 4 settembre 2024, n. 138;",
"Determinazione ACN n. 164179 del 14/04/2025 (misure di base, classificazione e notifica incidenti, Allegati);", "Determinazione ACN n. 164179 del 14/04/2025 (misure di base, classificazione e notifica incidenti);",
"Determinazione ACN n. 333017/2025 (piattaforma ACN, designazione rappresentanti);", "Determinazione ACN n. 333017/2025;",
"ISO/IEC 27001:2022, 27002:2022, 27017:2015, 27018:2019 (best practice, non vincolanti)."]) "ISO/IEC 27001:2022, 27002:2022, 27017:2015, 27018:2019 (best practice, non vincolanti)."])
add('note', "Questo manuale descrive le funzionalità della piattaforma NIS2 Agile alla versione 1.14.0. Le schermate e le voci di menu possono variare con gli aggiornamenti. Per l'aiuto contestuale usa l'icona ? presente in ogni pagina.") add('note', f"Manuale d'uso di NIS2 Agile — versione piattaforma 1.14.0, aggiornato all'{DATE}. Per assistenza usa il pulsante di aiuto (?) presente in ogni pagina o la voce \"Guida all'uso\" nel menu.")
# ───────────────────────────────────────────────────────────────────────── # ════════════════════════════════ EMITTER OOXML ════════════════════════════════
# EMITTER OOXML
# ─────────────────────────────────────────────────────────────────────────
def runs(text): def runs(text):
"""Converte **bold** in run WordprocessingML."""
out = [] out = []
for i, part in enumerate(re.split(r'\*\*', text)): for i, part in enumerate(re.split(r'\*\*', text)):
if part == '': if part == '': continue
continue rpr = '<w:rPr><w:b/></w:rPr>' if (i % 2 == 1) else ''
bold = (i % 2 == 1)
rpr = '<w:rPr><w:b/></w:rPr>' if bold else ''
out.append('<w:r>%s<w:t xml:space="preserve">%s</w:t></w:r>' % (rpr, escape(part))) out.append('<w:r>%s<w:t xml:space="preserve">%s</w:t></w:r>' % (rpr, escape(part)))
return ''.join(out) or '<w:r><w:t/></w:r>' return ''.join(out) or '<w:r><w:t/></w:r>'
@@ -294,62 +235,48 @@ def listitem(text, numid):
'<w:numPr><w:ilvl w:val="0"/><w:numId w:val="%d"/></w:numPr></w:pPr>%s</w:p>' % (numid, runs(text))) '<w:numPr><w:ilvl w:val="0"/><w:numId w:val="%d"/></w:numPr></w:pPr>%s</w:p>' % (numid, runs(text)))
def cell(text, header=False): def cell(text, header=False):
shd = '<w:shd w:val="clear" w:fill="1F3864"/>' if header else ''
t = ('**%s**' % text) if header else text
# nelle intestazioni il testo bianco
if header: if header:
shd = '<w:shd w:val="clear" w:fill="1F3864"/>'
r = '<w:r><w:rPr><w:b/><w:color w:val="FFFFFF"/></w:rPr><w:t xml:space="preserve">%s</w:t></w:r>' % escape(text) r = '<w:r><w:rPr><w:b/><w:color w:val="FFFFFF"/></w:rPr><w:t xml:space="preserve">%s</w:t></w:r>' % escape(text)
else: else:
r = runs(text) shd = ''; r = runs(text)
return ('<w:tc><w:tcPr><w:tcW w:w="0" w:type="auto"/>%s</w:tcPr><w:p>%s</w:p></w:tc>' % (shd, r)) return '<w:tc><w:tcPr><w:tcW w:w="0" w:type="auto"/>%s</w:tcPr><w:p>%s</w:p></w:tc>' % (shd, r)
def table(headers, rows): def table(headers, rows):
borders = ('<w:tblBorders>' + ''.join( borders = '<w:tblBorders>' + ''.join('<w:%s w:val="single" w:sz="4" w:space="0" w:color="BFBFBF"/>'%s
'<w:%s w:val="single" w:sz="4" w:space="0" w:color="BFBFBF"/>' % s for s in ['top','left','bottom','right','insideH','insideV']) + '</w:tblBorders>'
for s in ['top','left','bottom','right','insideH','insideV']) + '</w:tblBorders>') out = ['<w:tbl><w:tblPr><w:tblW w:w="5000" w:type="pct"/>%s<w:tblLook w:firstRow="1"/></w:tblPr>' % borders]
tblpr = '<w:tblPr><w:tblW w:w="5000" w:type="pct"/>%s<w:tblLook w:firstRow="1"/></w:tblPr>' % borders
out = ['<w:tbl>', tblpr]
out.append('<w:tr>' + ''.join(cell(h, True) for h in headers) + '</w:tr>') out.append('<w:tr>' + ''.join(cell(h, True) for h in headers) + '</w:tr>')
for row in rows: for row in rows: out.append('<w:tr>' + ''.join(cell(c) for c in row) + '</w:tr>')
out.append('<w:tr>' + ''.join(cell(c) for c in row) + '</w:tr>') out.append('</w:tbl><w:p/>')
out.append('</w:tbl>')
out.append('<w:p/>') # spacing after table
return ''.join(out) return ''.join(out)
def title_block(t, sub, meta):
return (para(t, 'Title') + para(sub, 'Subtitle') + para(meta, 'Meta'))
body = [] body = []
for b in C: for b in C:
k = b[0] k = b[0]
if k == 'title': body.append(title_block(b[1], b[2], b[3])) if k == 'title': body.append(para(b[1],'Title')+para(b[2],'Subtitle')+para(b[3],'Meta'))
elif k == 'h1': body.append(para(b[1], 'Heading1')) elif k in ('h1','h2','h3'): body.append(para(b[1], {'h1':'Heading1','h2':'Heading2','h3':'Heading3'}[k]))
elif k == 'h2': body.append(para(b[1], 'Heading2'))
elif k == 'h3': body.append(para(b[1], 'Heading3'))
elif k == 'p': body.append(para(b[1])) elif k == 'p': body.append(para(b[1]))
elif k == 'note': body.append(para(b[1], 'Note')) elif k == 'note': body.append(para(b[1],'Note'))
elif k == 'ul': body.append(''.join(listitem(x, 1) for x in b[1])) elif k == 'ul': body.append(''.join(listitem(x,1) for x in b[1]))
elif k == 'ol': body.append(''.join(listitem(x, 2) for x in b[1])) elif k == 'ol': body.append(''.join(listitem(x,2) for x in b[1]))
elif k == 'table': body.append(table(b[1], b[2])) elif k == 'table': body.append(table(b[1], b[2]))
elif k == 'pb': body.append('<w:p><w:r><w:br w:type="page"/></w:r></w:p>') elif k == 'pb': body.append('<w:p><w:r><w:br w:type="page"/></w:r></w:p>')
SECT = ('<w:sectPr><w:pgSz w:w="11906" w:h="16838"/>' SECT = ('<w:sectPr><w:pgSz w:w="11906" w:h="16838"/><w:pgMar w:top="1417" w:right="1417" w:bottom="1417" w:left="1417" w:header="708" w:footer="708" w:gutter="0"/></w:sectPr>')
'<w:pgMar w:top="1417" w:right="1417" w:bottom="1417" w:left="1417" w:header="708" w:footer="708" w:gutter="0"/>'
'</w:sectPr>')
document = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' document = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
'<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">' '<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:body>'
'<w:body>' + ''.join(body) + SECT + '</w:body></w:document>') + ''.join(body) + SECT + '</w:body></w:document>')
STYLES = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' STYLES = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
'<w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">' '<w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">'
'<w:docDefaults><w:rPrDefault><w:rPr><w:rFonts w:ascii="Calibri" w:hAnsi="Calibri"/><w:sz w:val="22"/></w:rPr></w:rPrDefault></w:docDefaults>' '<w:docDefaults><w:rPrDefault><w:rPr><w:rFonts w:ascii="Calibri" w:hAnsi="Calibri"/><w:sz w:val="22"/></w:rPr></w:rPrDefault></w:docDefaults>'
'<w:style w:type="paragraph" w:default="1" w:styleId="Normal"><w:name w:val="Normal"/><w:pPr><w:spacing w:after="140" w:line="276" w:lineRule="auto"/></w:pPr></w:style>' '<w:style w:type="paragraph" w:default="1" w:styleId="Normal"><w:name w:val="Normal"/><w:pPr><w:spacing w:after="140" w:line="276" w:lineRule="auto"/></w:pPr></w:style>'
'<w:style w:type="paragraph" w:styleId="Title"><w:name w:val="Title"/><w:pPr><w:spacing w:before="2400" w:after="120"/></w:pPr><w:rPr><w:b/><w:color w:val="1F3864"/><w:sz w:val="72"/></w:rPr></w:style>' '<w:style w:type="paragraph" w:styleId="Title"><w:name w:val="Title"/><w:pPr><w:spacing w:before="2600" w:after="120"/></w:pPr><w:rPr><w:b/><w:color w:val="1F3864"/><w:sz w:val="72"/></w:rPr></w:style>'
'<w:style w:type="paragraph" w:styleId="Subtitle"><w:name w:val="Subtitle"/><w:pPr><w:spacing w:after="80"/></w:pPr><w:rPr><w:color w:val="2E74B5"/><w:sz w:val="36"/></w:rPr></w:style>' '<w:style w:type="paragraph" w:styleId="Subtitle"><w:name w:val="Subtitle"/><w:pPr><w:spacing w:after="80"/></w:pPr><w:rPr><w:color w:val="2E74B5"/><w:sz w:val="34"/></w:rPr></w:style>'
'<w:style w:type="paragraph" w:styleId="Meta"><w:name w:val="Meta"/><w:rPr><w:color w:val="808080"/><w:sz w:val="20"/></w:rPr></w:style>' '<w:style w:type="paragraph" w:styleId="Meta"><w:name w:val="Meta"/><w:rPr><w:color w:val="808080"/><w:sz w:val="20"/></w:rPr></w:style>'
'<w:style w:type="paragraph" w:styleId="Heading1"><w:name w:val="heading 1"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:pPr><w:keepNext/><w:spacing w:before="320" w:after="120"/><w:outlineLvl w:val="0"/></w:pPr><w:rPr><w:b/><w:color w:val="1F3864"/><w:sz w:val="34"/></w:rPr></w:style>' '<w:style w:type="paragraph" w:styleId="Heading1"><w:name w:val="heading 1"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:pPr><w:keepNext/><w:spacing w:before="360" w:after="120"/><w:pBdr><w:bottom w:val="single" w:sz="6" w:space="3" w:color="2E74B5"/></w:pBdr><w:outlineLvl w:val="0"/></w:pPr><w:rPr><w:b/><w:color w:val="1F3864"/><w:sz w:val="32"/></w:rPr></w:style>'
'<w:style w:type="paragraph" w:styleId="Heading2"><w:name w:val="heading 2"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:pPr><w:keepNext/><w:spacing w:before="240" w:after="80"/><w:outlineLvl w:val="1"/></w:pPr><w:rPr><w:b/><w:color w:val="2E74B5"/><w:sz w:val="28"/></w:rPr></w:style>' '<w:style w:type="paragraph" w:styleId="Heading2"><w:name w:val="heading 2"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:pPr><w:keepNext/><w:spacing w:before="240" w:after="80"/><w:outlineLvl w:val="1"/></w:pPr><w:rPr><w:b/><w:color w:val="2E74B5"/><w:sz w:val="27"/></w:rPr></w:style>'
'<w:style w:type="paragraph" w:styleId="Heading3"><w:name w:val="heading 3"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:pPr><w:keepNext/><w:spacing w:before="200" w:after="60"/><w:outlineLvl w:val="2"/></w:pPr><w:rPr><w:b/><w:color w:val="404040"/><w:sz w:val="24"/></w:rPr></w:style>' '<w:style w:type="paragraph" w:styleId="Heading3"><w:name w:val="heading 3"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:pPr><w:keepNext/><w:spacing w:before="200" w:after="60"/><w:outlineLvl w:val="2"/></w:pPr><w:rPr><w:b/><w:color w:val="404040"/><w:sz w:val="24"/></w:rPr></w:style>'
'<w:style w:type="paragraph" w:styleId="ListParagraph"><w:name w:val="List Paragraph"/><w:basedOn w:val="Normal"/><w:pPr><w:ind w:left="567"/><w:spacing w:after="60"/></w:pPr></w:style>' '<w:style w:type="paragraph" w:styleId="ListParagraph"><w:name w:val="List Paragraph"/><w:basedOn w:val="Normal"/><w:pPr><w:ind w:left="567"/><w:spacing w:after="60"/></w:pPr></w:style>'
'<w:style w:type="paragraph" w:styleId="Note"><w:name w:val="Note"/><w:basedOn w:val="Normal"/><w:pPr><w:pBdr><w:left w:val="single" w:sz="18" w:space="6" w:color="F59E0B"/></w:pBdr><w:ind w:left="227"/><w:spacing w:before="120" w:after="120"/><w:shd w:val="clear" w:fill="FFFBEB"/></w:pPr><w:rPr><w:i/><w:color w:val="7A5B00"/></w:rPr></w:style>' '<w:style w:type="paragraph" w:styleId="Note"><w:name w:val="Note"/><w:basedOn w:val="Normal"/><w:pPr><w:pBdr><w:left w:val="single" w:sz="18" w:space="6" w:color="F59E0B"/></w:pBdr><w:ind w:left="227"/><w:spacing w:before="120" w:after="120"/><w:shd w:val="clear" w:fill="FFFBEB"/></w:pPr><w:rPr><w:i/><w:color w:val="7A5B00"/></w:rPr></w:style>'
@@ -359,49 +286,35 @@ NUMBERING = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
'<w:numbering xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">' '<w:numbering xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">'
'<w:abstractNum w:abstractNumId="0"><w:lvl w:ilvl="0"><w:start w:val="1"/><w:numFmt w:val="bullet"/><w:lvlText w:val="•"/><w:lvlJc w:val="left"/><w:pPr><w:ind w:left="567" w:hanging="284"/></w:pPr><w:rPr><w:rFonts w:ascii="Symbol" w:hAnsi="Symbol" w:hint="default"/></w:rPr></w:lvl></w:abstractNum>' '<w:abstractNum w:abstractNumId="0"><w:lvl w:ilvl="0"><w:start w:val="1"/><w:numFmt w:val="bullet"/><w:lvlText w:val="•"/><w:lvlJc w:val="left"/><w:pPr><w:ind w:left="567" w:hanging="284"/></w:pPr><w:rPr><w:rFonts w:ascii="Symbol" w:hAnsi="Symbol" w:hint="default"/></w:rPr></w:lvl></w:abstractNum>'
'<w:abstractNum w:abstractNumId="1"><w:lvl w:ilvl="0"><w:start w:val="1"/><w:numFmt w:val="decimal"/><w:lvlText w:val="%1."/><w:lvlJc w:val="left"/><w:pPr><w:ind w:left="567" w:hanging="284"/></w:pPr></w:lvl></w:abstractNum>' '<w:abstractNum w:abstractNumId="1"><w:lvl w:ilvl="0"><w:start w:val="1"/><w:numFmt w:val="decimal"/><w:lvlText w:val="%1."/><w:lvlJc w:val="left"/><w:pPr><w:ind w:left="567" w:hanging="284"/></w:pPr></w:lvl></w:abstractNum>'
'<w:num w:numId="1"><w:abstractNumId w:val="0"/></w:num>' '<w:num w:numId="1"><w:abstractNumId w:val="0"/></w:num><w:num w:numId="2"><w:abstractNumId w:val="1"/></w:num></w:numbering>')
'<w:num w:numId="2"><w:abstractNumId w:val="1"/></w:num>'
'</w:numbering>')
CONTENT_TYPES = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' CT = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
'<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">' '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'
'<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>' '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>'
'<Default Extension="xml" ContentType="application/xml"/>' '<Default Extension="xml" ContentType="application/xml"/>'
'<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>' '<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>'
'<Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>' '<Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>'
'<Override PartName="/word/numbering.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml"/>' '<Override PartName="/word/numbering.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml"/>'
'<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>' '<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/></Types>')
'</Types>')
RELS = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' RELS = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
'<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>' '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>'
'<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>' '<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/></Relationships>')
'</Relationships>') DRELS = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
DOC_RELS = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
'<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>' '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>'
'<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering" Target="numbering.xml"/>' '<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering" Target="numbering.xml"/></Relationships>')
'</Relationships>')
CORE = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' CORE = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
'<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" ' '<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
'xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' '<dc:title>Manuale d\'uso NIS2 Agile</dc:title><dc:creator>NIS2 Agile</dc:creator><cp:lastModifiedBy>NIS2 Agile</cp:lastModifiedBy>'
'<dc:title>Manuale d\'uso NIS2 Agile</dc:title><dc:creator>NIS2 Agile</dc:creator>' '<dcterms:created xsi:type="dcterms:W3CDTF">2026-06-11T00:00:00Z</dcterms:created><dcterms:modified xsi:type="dcterms:W3CDTF">2026-06-11T00:00:00Z</dcterms:modified></cp:coreProperties>')
'<cp:lastModifiedBy>NIS2 Agile</cp:lastModifiedBy>'
'<dcterms:created xsi:type="dcterms:W3CDTF">2026-06-11T00:00:00Z</dcterms:created>'
'<dcterms:modified xsi:type="dcterms:W3CDTF">2026-06-11T00:00:00Z</dcterms:modified>'
'</cp:coreProperties>')
with zipfile.ZipFile(OUT, 'w', zipfile.ZIP_DEFLATED) as z: with zipfile.ZipFile(OUT, 'w', zipfile.ZIP_DEFLATED) as z:
z.writestr('[Content_Types].xml', CONTENT_TYPES) z.writestr('[Content_Types].xml', CT)
z.writestr('_rels/.rels', RELS) z.writestr('_rels/.rels', RELS)
z.writestr('word/document.xml', document) z.writestr('word/document.xml', document)
z.writestr('word/styles.xml', STYLES) z.writestr('word/styles.xml', STYLES)
z.writestr('word/numbering.xml', NUMBERING) z.writestr('word/numbering.xml', NUMBERING)
z.writestr('word/_rels/document.xml.rels', DOC_RELS) z.writestr('word/_rels/document.xml.rels', DRELS)
z.writestr('docProps/core.xml', CORE) z.writestr('docProps/core.xml', CORE)
print("OK ->", OUT, "| blocchi:", len(C))
print("OK ->", OUT)
print("blocchi:", len(C))