#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Manuale d'uso COMPLETO di NIS2 Agile (.docx, solo stdlib). Contenuti moduli da help.js reale.""" import zipfile, re, json, html as _html, subprocess, os from xml.sax.saxutils import escape 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" # Auto-estrae i contenuti dall'help reale del prodotto (public/js/help.js) via node. _node = ('const fs=require("fs");let s=fs.readFileSync(process.argv[1],"utf8");' 'const a=s.indexOf("var _helpContent");const start=s.indexOf("{",a);' 'const end=s.indexOf("var _pageMap");let obj=s.slice(start,s.lastIndexOf("}",end)+1);' 'fs.writeFileSync(process.argv[2],JSON.stringify(eval("("+obj+")")));') subprocess.run(["node","-e",_node, os.path.join(BASE,"public","js","help.js"), HELP_JSON], check=True) HELP = json.load(open(HELP_JSON, encoding="utf-8")) DATE = "11 giugno 2026" C = [] def add(*b): C.append(b) def html2mk(s): """Converte l'HTML dell'help nel markup interno (**bold**) + testo pulito.""" s = re.sub(r'|', '**', s) s = re.sub(r'<[^>]+>', '', s) # rimuovi altri tag (span/em/...) s = _html.unescape(s) # < ≥   ... s = s.replace(' ', ' ') s = re.sub(r'\s+', ' ', s).strip() # bilancia eventuali ** dispari if s.count('**') % 2: s += '**' return s def module_chapter(num, title, topic_key, lead=None): t = HELP.get(topic_key, {}) add('h1', f"{num}. {title}") if lead: add('p', lead) if t.get('intro'): add('p', html2mk(t['intro'])) for sec in t.get('sections', []): add('h3', html2mk(sec.get('heading',''))) items = [html2mk(x) for x in sec.get('items', [])] if items: add('ul', items) 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') # ══════════════════ 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('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', "Dalla versione 1.14.0 include anche il **Modello Organizzativo SGSI** secondo **ISO/IEC 27001:2022**, con estensioni cloud ISO 27017/27018.") add('h3', "A chi è rivolto") add('ul', ["**PMI** che devono adeguarsi agli obblighi NIS2;", "**Grandi imprese** con processi di compliance strutturati;", "**Consulenti e CISO** che seguono più aziende clienti tramite uno studio."]) add('h3', "Come è organizzato il lavoro nella piattaforma") 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('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.") # ══════════════════ 2. ACCESSO ══════════════════ add('h1', "2. Requisiti e accesso alla piattaforma") add('h3', "Requisiti") add('ul', ["Un browser web aggiornato (Chrome, Edge, Firefox o Safari);", "una connessione a Internet;", "le credenziali di accesso (email e password) oppure un invito ricevuto dal proprio studio/partner."]) add('h3', "Indirizzo della piattaforma") add('p', "Apri il browser e vai su **https://nis2.agile.software/**. Si apre la pagina di accesso.") add('h3', "Come accedere (login)") add('ol', ["Nel campo **Indirizzo Email** inserisci l'email con cui sei registrato (es. nome@azienda.it).", "Nel campo **Password** inserisci la tua password. Puoi usare l'icona a forma di occhio per mostrarla/nasconderla.", "Premi **Accedi**.", "Se le credenziali sono corrette: se hai già un'azienda configurata vieni portato alla **Dashboard**, altrimenti alla procedura di **Onboarding**."]) add('p', "In caso di errore compare un messaggio in rosso sotto il titolo. Verifica email e password e riprova.") add('h3', "Password dimenticata") add('ol', ["Dalla pagina di accesso scegli **Password dimenticata**.", "Inserisci l'email dell'account: riceverai un messaggio con un link per impostare una nuova password.", "Apri il link, scegli la nuova password (rispettando i requisiti di sicurezza) e conferma."]) 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.") add('h3', "Disconnessione e sessioni") 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.") # ══════════════════ 3. REGISTRAZIONE ══════════════════ add('h1', "3. Registrazione di un nuovo utente") 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('h3', "Passo 1 — Ruolo e (eventuale) codice invito") add('p', "Scegli il **ruolo** con cui ti registri, selezionando una delle schede disponibili:") add('table', ["Scheda ruolo","Quando sceglierlo"], [["Responsabile compliance","Gestisci direttamente assessment, rischi, policy e SGSI dell'azienda"], ["Amministratore organizzazione","Amministri l'azienda, gli utenti e le impostazioni"], ["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. ONBOARDING ══════════════════ add('h1', "4. Onboarding: configurazione dell'azienda") 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('h3', "Passo 1 — Inserisci la Partita IVA") 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**.") add('h3', "Passo 2 — Verifica e completa i dati aziendali") 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:") add('ul', ["**Ragione sociale** (es. Acme S.r.l.);", "**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. NAVIGAZIONE ══════════════════ add('h1', "5. Come muoversi nell'interfaccia") 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('h3', "Le sezioni del menu") add('table', ["Sezione","Voci"], [["Principale","Dashboard, Compliance Journey, Gap Analysis (NIS2), Gap Analysis ACN, Modello SGSI (ISO 27001)"], ["Gestione","Rischi, Incidenti, Policy, Supply Chain, Segnalazioni (whistleblowing), Normative, AI Cross-Analysis, Knowledge Base"], ["Operativo","Formazione, Asset, Audit & Report"], ["Sistema","Guida all'uso, Impostazioni, Architettura, Simulazione Demo, Integrazioni Esterne"]]) 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..23 MODULI (da help.js) ══════════════════ module_chapter(6, "Dashboard", "dashboard") module_chapter(7, "Gap Analysis NIS2 (Art. 21)", "assessment", "Apri la voce **Gap Analysis** dal menu. È l'analisi di primo livello sulle 10 categorie di misure dell'Art. 21 NIS2.") module_chapter(8, "Gap Analysis ACN (Determinazione 164179/2025)", "acn", "Apri la voce **Gap Analysis ACN**. È l'analisi di secondo livello sui requisiti puntuali ACN.") module_chapter(9, "Modello Organizzativo SGSI (ISO 27001/27017/27018)", "isms", "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.") module_chapter(10, "Gestione dei Rischi", "risks", "Apri la voce **Rischi**.") module_chapter(11, "Gestione degli Incidenti (Art. 23)", "incidents", "Apri la voce **Incidenti**.") module_chapter(12, "Policy e procedure", "policies", "Apri la voce **Policy**.") module_chapter(13, "Supply Chain (fornitori)", "supply-chain", "Apri la voce **Supply Chain**.") module_chapter(14, "Formazione", "training", "Apri la voce **Formazione**.") module_chapter(15, "Asset", "assets", "Apri la voce **Asset**.") module_chapter(16, "Audit & Report", "reports", "Apri la voce **Audit & Report**.") module_chapter(17, "Whistleblowing (Art. 32)", "whistleblowing", "Apri la voce **Segnalazioni**.") module_chapter(18, "Aggiornamenti normativi", "normative", "Apri la voce **Normative**.") module_chapter(19, "Knowledge Base", "kb", "Apri la voce **Knowledge Base**.") module_chapter(20, "Compliance Journey", "workflow", "Apri la voce **Compliance Journey**: un percorso guidato che indica i prossimi passi consigliati.") module_chapter(21, "Segnalazioni (feedback)", "feedback", "Usa lo strumento di feedback per segnalare problemi o suggerimenti: ogni segnalazione cattura automaticamente versione e superficie del prodotto.") 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.") # ══════════════════ 24. FAQ ══════════════════ add('h1', "24. Domande frequenti (FAQ)") add('h3', "Non riesco ad accedere") add('p', "Verifica email e password; usa **Password dimenticata** per reimpostarla. Se usi il SSO della suite, accedi con le credenziali centralizzate.") add('h3', "Ho sbagliato la classificazione NIS2") add('p', "Puoi correggerla in **Impostazioni › Classificazione NIS2**: il perimetro dei moduli (es. Gap Analysis ACN) si aggiorna di conseguenza.") 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.") # ══════════════════ 25. GLOSSARIO ══════════════════ add('h1', "25. Glossario") add('table', ["Termine","Significato"], [["NIS2","Direttiva (UE) 2022/2555 sulla cibersicurezza"], ["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)"], ["SoA","Statement of Applicability: dichiarazione di applicabilità dei controlli"], ["Annex A","I 93 controlli di sicurezza di ISO/IEC 27001:2022"], ["RACI","Matrice di responsabilità: Responsible, Accountable, Consulted, Informed"], ["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)"], ["RAG","Recupero da knowledge base a supporto delle risposte AI"], ["PII","Dati personali (Personally Identifiable Information)"]]) # ══════════════════ 26. RIFERIMENTI ══════════════════ add('h1', "26. 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);", "Determinazione ACN n. 333017/2025;", "ISO/IEC 27001:2022, 27002:2022, 27017:2015, 27018:2019 (best practice, non vincolanti)."]) 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 ════════════════════════════════ def runs(text): out = [] for i, part in enumerate(re.split(r'\*\*', text)): if part == '': continue rpr = '' if (i % 2 == 1) else '' out.append('%s%s' % (rpr, escape(part))) return ''.join(out) or '' def para(text, style=None): ppr = '' % style if style else '' return '%s%s' % (ppr, runs(text)) def listitem(text, numid): return ('' '%s' % (numid, runs(text))) def cell(text, header=False): if header: shd = '' r = '%s' % escape(text) else: shd = ''; r = runs(text) return '%s%s' % (shd, r) def table(headers, rows): borders = '' + ''.join(''%s for s in ['top','left','bottom','right','insideH','insideV']) + '' out = ['%s' % borders] out.append('' + ''.join(cell(h, True) for h in headers) + '') for row in rows: out.append('' + ''.join(cell(c) for c in row) + '') out.append('') return ''.join(out) body = [] for b in C: k = b[0] if k == 'title': body.append(para(b[1],'Title')+para(b[2],'Subtitle')+para(b[3],'Meta')) elif k in ('h1','h2','h3'): body.append(para(b[1], {'h1':'Heading1','h2':'Heading2','h3':'Heading3'}[k])) 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('') SECT = ('') document = ('' '' + ''.join(body) + SECT + '') STYLES = ('' '' '' '' '' '' '' '' '' '' '' '' '') NUMBERING = ('' '' '' '' '') CT = ('' '' '' '' '' '' '' '') RELS = ('' '' '' '') DRELS = ('' '' '' '') CORE = ('' '' 'Manuale d\'uso NIS2 AgileNIS2 AgileNIS2 Agile' '2026-06-11T00:00:00Z2026-06-11T00:00:00Z') with zipfile.ZipFile(OUT, 'w', zipfile.ZIP_DEFLATED) as z: z.writestr('[Content_Types].xml', CT) 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', DRELS) z.writestr('docProps/core.xml', CORE) print("OK ->", OUT, "| blocchi:", len(C))