[DOCS] Manuale d'uso prodotto NIS2 Agile (Word/.docx) v1.14.0 + generatore

Manuale completo in docs/MANUALE_UTENTE_NIS2_AGILE.docx (22 capitoli: intro,
onboarding, dashboard, Gap Analysis NIS2/ACN, Modello SGSI ISO 27001/17/18,
rischi, incidenti, policy, supply chain, formazione, asset, audit&report,
whistleblowing, normative, KB/AI, feedback, impostazioni, admin, API,
glossario, riferimenti). Generato con scripts/genera-manuale-utente.py
(OOXML puro, nessuna dipendenza esterna).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-06-11 17:43:46 +02:00
co-authored by Claude Opus 4.8
parent 9964b2b73c
commit 0a883b7cae
2 changed files with 407 additions and 0 deletions
Binary file not shown.
+407
View File
@@ -0,0 +1,407 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Genera il Manuale d'uso di NIS2 Agile in .docx usando solo stdlib (zipfile+XML)."""
import zipfile, re, datetime
from xml.sax.saxutils import escape
OUT = "/projects/nis2-agile/docs/MANUALE_UTENTE_NIS2_AGILE.docx"
# ─────────────────────────────────────────────────────────────────────────
# CONTENUTO DEL MANUALE (DSL: lista di blocchi)
# ('title', titolo, sottotitolo, riga_meta)
# ('h1'|'h2'|'h3', testo) | ('p', testo) | ('note', testo)
# ('ul', [voci]) | ('ol', [voci])
# ('table', [intestazioni], [[riga],...])
# ('pb',) page break
# Nel testo: **grassetto** supportato.
# ─────────────────────────────────────────────────────────────────────────
DATE = "11 giugno 2026"
C = []
def add(*b): C.append(b)
add('title', "NIS2 Agile", "Manuale d'uso del prodotto",
"Versione piattaforma 1.14.0 · Aggiornato all'%s" % DATE)
# ===== INDICE (statico, i numeri di pagina non sono calcolati) =====
add('h1', "Indice dei contenuti")
for n in ["1. Introduzione","2. Primi passi (accesso, registrazione, onboarding)",
"3. Dashboard","4. Gap Analysis NIS2 (Art. 21)","5. Gap Analysis ACN (Determinazione 164179/2025)",
"6. Modello Organizzativo SGSI (ISO 27001/27017/27018)","7. Gestione dei Rischi",
"8. Gestione degli Incidenti (Art. 23)","9. Policy e procedure","10. Supply Chain (fornitori)",
"11. Formazione","12. Asset","13. Audit & Report","14. Whistleblowing (Art. 32)",
"15. Aggiornamenti normativi","16. Knowledge Base e Assistente AI","17. Segnalazioni (feedback)",
"18. Impostazioni","19. Amministrazione","20. Integrazioni e API","21. Glossario","22. Riferimenti normativi"]:
add('p', n)
add('pb')
# ===== 1. INTRODUZIONE =====
add('h1', "1. Introduzione")
add('h2', "1.1 Che cos'è NIS2 Agile")
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 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('h2', "1.2 A chi è rivolto")
add('ul', ["**PMI** che devono adeguarsi agli obblighi NIS2 con risorse limitate;",
"**Grandi imprese / Enterprise** con processi di compliance strutturati;",
"**Consulenti e CISO** che gestiscono più organizzazioni clienti tramite uno studio (consulting firm)."])
add('h2', "1.3 Quadro normativo di riferimento")
add('table', ["Fonte","Ruolo"],
[["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 =====
add('pb'); add('h1', "2. Primi passi")
add('h2', "2.1 Accesso e indirizzo")
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('h2', "2.2 Registrazione")
add('ol', ["Apri la pagina di **Registrazione** e inserisci i dati richiesti (email, password conforme alla policy di sicurezza).",
"Al termine vieni indirizzato automaticamente alla procedura di **onboarding** aziendale.",
"Se possiedi un **invito/licenza** (token `inv_...`) fornito da un partner, inseriscilo per attivare l'organizzazione pre-configurata."])
add('h2', "2.3 Recupero password")
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('h2', "2.4 Onboarding dell'organizzazione")
add('p', "Il wizard di onboarding crea l'organizzazione e ne determina la classificazione NIS2. Si articola in quattro passi:")
add('ol', ["**Dati azienda** — inserimento manuale oppure precompilazione tramite **Partita IVA** (lookup automatico) o caricamento della **visura camerale** (estrazione AI dei dati).",
"**Verifica e completamento** — settore (ATECO → settore NIS2), numero dipendenti, fatturato, sede, contatti.",
"**Profilo utente** — nome, ruolo, telefono.",
"**Classificazione NIS2** — la piattaforma propone se il soggetto è **essenziale**, **importante** o **non applicabile** in base a settore e dimensione; è possibile aderire volontariamente."])
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.")
add('h2', "2.5 Ruoli e permessi")
add('table', ["Ruolo","Descrizione"],
[["super_admin","Amministratore di piattaforma (accesso a tutte le organizzazioni)"],
["org_admin","Amministratore dell'organizzazione"],
["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 =====
add('pb'); add('h1', "3. Dashboard")
add('p', "La Dashboard è la pagina iniziale dopo l'accesso e offre una visione d'insieme dello stato di conformità.")
add('ul', ["**Avanzamento implementazione misure Art. 21 NIS2** — indicatore complessivo (gauge);",
"**Scadenze imminenti** — adempimenti e revisioni in arrivo da più moduli;",
"**Attività recenti** — ultime azioni tracciate;",
"**Heatmap dei rischi** — distribuzione dei rischi su probabilità × impatto;",
"**Benchmark settoriale** (anonimizzato) — confronto con il proprio settore;",
"**Compliance Journey** — percorso guidato che indica i prossimi passi consigliati."])
# ===== 4. GAP ANALYSIS NIS2 =====
add('pb'); add('h1', "4. Gap Analysis NIS2 (Art. 21)")
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('h2', "4.1 Come si svolge")
add('ol', ["Crea un nuovo assessment: la piattaforma pre-popola tutte le domande.",
"Per ogni domanda indica lo stato: **Attuato**, **Parziale**, **Non attuato**, **Non applicabile**. Le risposte si salvano automaticamente.",
"Completa l'assessment per ottenere un **punteggio** per categoria e complessivo.",
"Avvia l'**analisi AI** per ricevere sintesi esecutiva, priorità, quick win e una roadmap di conformità."])
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.")
# ===== 5. GAP ANALYSIS ACN =====
add('pb'); add('h1', "5. Gap Analysis ACN (Determinazione 164179/2025)")
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('table', ["Classificazione","Perimetro applicabile"],
[["Soggetto IMPORTANTE (Allegato 1)","37 misure / 87 requisiti"],
["Soggetto ESSENZIALE (Allegato 2)","43 misure / 116 requisiti"]])
add('ul', ["Il perimetro è filtrato automaticamente in base alla classificazione NIS2 dell'organizzazione;",
"scoring per funzione del Framework Nazionale e punteggio complessivo;",
"**piano d'azione** dei requisiti non conformi e **analisi AI** ancorata ai 203 requisiti ACN reali (nessun riferimento inventato);",
"**Catalogo requisiti** consultabile in sola lettura."])
# ===== 6. MODELLO SGSI ISO 27001 (NUOVO) =====
add('pb'); add('h1', "6. Modello Organizzativo SGSI (ISO 27001/27017/27018)")
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.")
add('h2', "6.1 Il wizard in 6 passi")
add('table', ["Passo","Clausola ISO 27001","Contenuto"],
[["1. Contesto e Ambito","cl. 4","Scope del SGSI, contesto interno/esterno, parti interessate, confini, esclusioni motivate, attivazione estensioni cloud"],
["2. Leadership","cl. 5","Ruoli e responsabilità con matrice RACI (Responsible, Accountable, Consulted, Informed)"],
["3. Risk","cl. 6","Metodologia di risk assessment e obiettivi del SGSI (il registro rischi resta nel modulo Rischi)"],
["4. Statement of Applicability","cl. 6.1.3","Applicabilità, stato e motivazione dei 93 controlli Annex A + estensioni cloud"],
["5. Documented Information","cl. 7–8","Policy e procedure, con bozze generabili dall'AI"],
["6. Monitoraggio e Miglioramento","cl. 9–10","Audit interni e non conformità (moduli Audit & Report e NCR/CAPA); checklist di completamento"]])
add('h2', "6.2 SoA pre-popolato dalla Gap Analysis NIS2")
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.")
add('note', "Il pulsante \"Ri-deriva dal NIS2\" aggiunge soltanto i controlli mancanti e non sovrascrive le modifiche manuali già inserite.")
add('h2', "6.3 Estensioni cloud (condizionali)")
add('ul', ["**ISO/IEC 27017:2015** — attivala se usi servizi cloud pubblici o sei un fornitore cloud: aggiunge 7 controlli cloud (CLD.*) al SoA;",
"**ISO/IEC 27018:2019** — attivala se tratti dati personali (PII) in cloud pubblico come responsabile: aggiunge i controlli privacy (PII.*), sinergici con il GDPR.",
"Le sezioni cloud/PII compaiono nel SoA **solo** se attivi i relativi flag nel passo 1."])
add('h2', "6.4 Documenti e generazione AI")
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.")
add('h2', "6.5 Esportazione")
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.")
# ===== 7. RISCHI =====
add('pb'); add('h1', "7. Gestione dei Rischi")
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('ul', ["Creazione del rischio con scala probabilità/impatto (dettagli ISO 27005);",
"**Piani di trattamento** (mitigare, accettare, trasferire, evitare) e stato di avanzamento;",
"**Suggerimento AI** dei rischi per settore e asset (dati anonimizzati);",
"Funzioni avanzate: registro quantitativo **FAIR** (calcolo ALE Monte Carlo) e dashboard **KRI**."])
# ===== 8. INCIDENTI =====
add('pb'); add('h1', "8. Gestione degli Incidenti (Art. 23)")
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"],
[["NIS2","Direttiva (UE) 2022/2555 sulla cibersicurezza"],
["ACN","Agenzia per la Cybersicurezza Nazionale"],
["SGSI / ISMS","Sistema di Gestione della Sicurezza delle Informazioni (ISO 27001)"],
["SoA","Statement of Applicability: dichiarazione di applicabilità dei controlli"],
["Annex A","Elenco dei 93 controlli di sicurezza di ISO/IEC 27001:2022"],
["RACI","Matrice di responsabilità: Responsible, Accountable, Consulted, Informed"],
["CSIRT","Computer Security Incident Response Team (autorità di notifica)"],
["PIR","Post-Incident Review (revisione post-incidente)"],
["RAG","Retrieval-Augmented Generation: AI con recupero da knowledge base"],
["PII","Personally Identifiable Information (dati personali)"],
["Soggetto essenziale / importante","Categorie NIS2 che determinano gli obblighi applicabili"]])
# ===== 22. RIFERIMENTI NORMATIVI =====
add('pb'); add('h1', "22. Riferimenti normativi")
add('ul', ["Direttiva (UE) 2022/2555 (NIS2);",
"Direttiva (UE) 2022/2557 (CER);",
"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. 333017/2025 (piattaforma ACN, designazione rappresentanti);",
"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.")
# ─────────────────────────────────────────────────────────────────────────
# EMITTER OOXML
# ─────────────────────────────────────────────────────────────────────────
def runs(text):
"""Converte **bold** in run WordprocessingML."""
out = []
for i, part in enumerate(re.split(r'\*\*', text)):
if part == '':
continue
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)))
return ''.join(out) or '<w:r><w:t/></w:r>'
def para(text, style=None):
ppr = '<w:pPr><w:pStyle w:val="%s"/></w:pPr>' % style if style else ''
return '<w:p>%s%s</w:p>' % (ppr, runs(text))
def listitem(text, numid):
return ('<w:p><w:pPr><w:pStyle w:val="ListParagraph"/>'
'<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):
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:
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:
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))
def table(headers, rows):
borders = ('<w:tblBorders>' + ''.join(
'<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>')
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>')
for row in rows:
out.append('<w:tr>' + ''.join(cell(c) for c in row) + '</w:tr>')
out.append('</w:tbl>')
out.append('<w:p/>') # spacing after table
return ''.join(out)
def title_block(t, sub, meta):
return (para(t, 'Title') + para(sub, 'Subtitle') + para(meta, 'Meta'))
body = []
for b in C:
k = b[0]
if k == 'title': body.append(title_block(b[1], b[2], b[3]))
elif k == 'h1': body.append(para(b[1], 'Heading1'))
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 == 'note': body.append(para(b[1], 'Note'))
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 == '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>')
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>')
document = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
'<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">'
'<w:body>' + ''.join(body) + SECT + '</w:body></w:document>')
STYLES = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
'<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: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="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="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="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="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="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:styles>')
NUMBERING = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
'<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="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="2"><w:abstractNumId w:val="1"/></w:num>'
'</w:numbering>')
CONTENT_TYPES = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
'<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'
'<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>'
'<Default Extension="xml" ContentType="application/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/numbering.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml"/>'
'<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>'
'</Types>')
RELS = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
'<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="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>'
'</Relationships>')
DOC_RELS = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
'<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="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering" Target="numbering.xml"/>'
'</Relationships>')
CORE = ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
'<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">'
'<dc:title>Manuale d\'uso NIS2 Agile</dc:title><dc:creator>NIS2 Agile</dc:creator>'
'<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:
z.writestr('[Content_Types].xml', CONTENT_TYPES)
z.writestr('_rels/.rels', RELS)
z.writestr('word/document.xml', document)
z.writestr('word/styles.xml', STYLES)
z.writestr('word/numbering.xml', NUMBERING)
z.writestr('word/_rels/document.xml.rels', DOC_RELS)
z.writestr('docProps/core.xml', CORE)
print("OK ->", OUT)
print("blocchi:", len(C))