[FEAT] Modello Organizzativo SGSI (ISO 27001/27017/27018) + SoA pre-popolato da NIS2
Nuovo modulo guidato in 6 step (cl. 4-10 + Statement of Applicability): - migration 037 (isms_models/roles/soa/documents) + 038 (dataset 111 controlli: 93 Annex A:2022 + 7 CLD/27017 + 11 PII/27018) + runner scripts/migrate-isms.php - IsmsModelController (16 endpoint) registrato in index.php - SoA pre-popolato dalle risposte Gap Analysis NIS2 (mapping iso27001_control) - estensioni cloud condizionali 27017/27018 via flag uses_public_cloud/ is_cloud_provider/processes_pii_in_cloud - AIService::generateIsmsDocument + fonti ISO in nis2_sources.php - frontend isms.html/isms.js + api client + sidebar + help + i18n IT/EN - ingest KB ISO (scope SYSTEM, solo titoli/sintesi: no testo coperto da copyright) - version.json 1.14.0; doc studio + deploy handoff Strumento di supporto/pre-audit (non certificazione). Migration DA APPLICARE su host. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -88,6 +88,7 @@ $controllerMap = [
|
||||
'organizations' => 'OrganizationController',
|
||||
'assessments' => 'AssessmentController',
|
||||
'acn-gap' => 'AcnAssessmentController', // Gap Analysis ACN (Det. 164179/2025, misure/requisiti)
|
||||
'isms' => 'IsmsModelController', // Modello Organizzativo SGSI (ISO 27001/27017/27018 + SoA)
|
||||
'dashboard' => 'DashboardController',
|
||||
'risks' => 'RiskController',
|
||||
'incidents' => 'IncidentController',
|
||||
@@ -217,6 +218,26 @@ $actionMap = [
|
||||
'POST:{id}/aiAnalyze' => 'aiAnalyze',
|
||||
],
|
||||
|
||||
// ── IsmsModelController — Modello Organizzativo SGSI (ISO 27001/27017/27018) ──
|
||||
'isms' => [
|
||||
'GET:model' => 'getModel',
|
||||
'POST:model' => 'saveModel',
|
||||
'PUT:model' => 'saveModel',
|
||||
'GET:annexControls' => 'annexControls',
|
||||
'GET:soa' => 'getSoa',
|
||||
'POST:soa/derive' => 'deriveSoa',
|
||||
'PUT:soa' => 'updateSoaControl',
|
||||
'GET:roles' => 'listRoles',
|
||||
'POST:roles' => 'saveRole',
|
||||
'DELETE:roles/{subId}' => 'deleteRole',
|
||||
'GET:documents' => 'listDocuments',
|
||||
'POST:documents' => 'createDocument',
|
||||
'POST:documents/aiGenerate' => 'aiGenerateDocument',
|
||||
'PUT:documents/{subId}' => 'updateDocument',
|
||||
'GET:readiness' => 'readiness',
|
||||
'GET:export' => 'export',
|
||||
],
|
||||
|
||||
// ── DashboardController ─────────────────────────
|
||||
'dashboard' => [
|
||||
'GET:overview' => 'overview',
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="it">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Modello Organizzativo SGSI (ISO 27001) - NIS2 Agile</title>
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<style>
|
||||
.isms-intro { background:#eff6ff; border-left:4px solid var(--primary,#2563eb); padding:14px 18px; border-radius:8px; margin-bottom:20px; font-size:.92rem; line-height:1.6; }
|
||||
.isms-steps { display:flex; flex-wrap:wrap; gap:6px; margin-bottom:20px; }
|
||||
.isms-step { flex:1 1 140px; min-width:120px; padding:10px 12px; border:1.5px solid var(--gray-200,#e5e7eb); border-radius:10px; background:#fff; cursor:pointer; font-size:.82rem; text-align:center; }
|
||||
.isms-step.active { border-color:var(--primary,#2563eb); background:#eff6ff; font-weight:700; }
|
||||
.isms-step.done { border-color:#16a34a; }
|
||||
.isms-step .num { display:inline-block; width:22px; height:22px; line-height:22px; border-radius:50%; background:var(--gray-100,#f3f4f6); font-weight:700; margin-bottom:4px; }
|
||||
.isms-step.done .num { background:#16a34a; color:#fff; }
|
||||
.isms-panel { display:none; }
|
||||
.isms-panel.active { display:block; }
|
||||
.isms-disclaimer { font-size:.8rem; color:#92400e; background:#fffbeb; border:1px solid #fde68a; border-radius:8px; padding:10px 12px; margin:12px 0; }
|
||||
.raci-row { display:flex; gap:8px; align-items:center; flex-wrap:wrap; padding:8px 0; border-bottom:1px solid var(--gray-100,#f3f4f6); }
|
||||
.raci-row input, .raci-row select { padding:6px 8px; border:1px solid var(--gray-200,#e5e7eb); border-radius:6px; font-size:.85rem; }
|
||||
.soa-std { border:1px solid var(--gray-200,#e5e7eb); border-radius:10px; margin-bottom:14px; overflow:hidden; }
|
||||
.soa-std-head { background:var(--gray-50,#f9fafb); padding:12px 16px; cursor:pointer; display:flex; justify-content:space-between; align-items:center; }
|
||||
.soa-std-body { padding:6px 14px 14px; display:none; }
|
||||
.soa-std.open .soa-std-body { display:block; }
|
||||
.soa-theme { font-size:.78rem; font-weight:700; text-transform:uppercase; color:var(--gray-500,#6b7280); margin:14px 0 6px; letter-spacing:.04em; }
|
||||
.soa-ctrl { padding:12px 0; border-top:1px solid var(--gray-100,#f3f4f6); }
|
||||
.soa-ctrl-head { display:flex; gap:10px; align-items:flex-start; justify-content:space-between; flex-wrap:wrap; }
|
||||
.soa-code { display:inline-block; background:#eef2ff; color:#3730a3; font-weight:700; font-size:.72rem; padding:2px 8px; border-radius:6px; margin-right:6px; }
|
||||
.soa-derived { background:#ecfdf5; color:#065f46; font-size:.68rem; padding:1px 6px; border-radius:5px; }
|
||||
.soa-opts { display:flex; flex-wrap:wrap; gap:6px; margin-top:8px; }
|
||||
.soa-opt { min-height:34px; padding:5px 10px; border:1.5px solid var(--gray-200,#e5e7eb); border-radius:18px; background:#fff; cursor:pointer; font-size:.8rem; font-weight:600; }
|
||||
.soa-opt.sel-implemented,.soa-opt.sel-verified { background:#16a34a; color:#fff; border-color:#16a34a; }
|
||||
.soa-opt.sel-in_progress { background:#f59e0b; color:#fff; border-color:#f59e0b; }
|
||||
.soa-opt.sel-not_started { background:#9ca3af; color:#fff; border-color:#9ca3af; }
|
||||
.soa-justif { width:100%; margin-top:8px; padding:8px; border:1px solid var(--gray-200,#e5e7eb); border-radius:6px; font-size:.84rem; min-height:46px; }
|
||||
.isms-field { margin-bottom:14px; }
|
||||
.isms-field label { display:block; font-weight:600; font-size:.88rem; margin-bottom:4px; }
|
||||
.isms-field textarea, .isms-field input { width:100%; padding:9px; border:1px solid var(--gray-200,#e5e7eb); border-radius:8px; font-size:.9rem; }
|
||||
.isms-flags label { display:block; padding:8px 0; font-size:.9rem; }
|
||||
.ck-item { display:flex; justify-content:space-between; padding:8px 0; border-bottom:1px solid var(--gray-100,#f3f4f6); font-size:.9rem; }
|
||||
.ck-yes { color:#16a34a; font-weight:700; } .ck-no { color:#9ca3af; }
|
||||
.savehint { font-size:.78rem; color:var(--gray-400,#9ca3af); min-height:16px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app-layout">
|
||||
<aside class="sidebar" id="sidebar"></aside>
|
||||
<main class="main-content">
|
||||
<header class="content-header">
|
||||
<h2 data-i18n="isms.title">Modello Organizzativo SGSI (ISO 27001)</h2>
|
||||
<div class="content-header-actions">
|
||||
<span class="savehint" id="isms-savehint"></span>
|
||||
<button class="btn btn-outline btn-sm" onclick="ismsExportView()" data-i18n="isms.export">Esporta SGSI</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="content-body">
|
||||
<div class="isms-intro">
|
||||
<strong data-i18n="isms.intro.title">Costruisci il tuo Sistema di Gestione della Sicurezza delle Informazioni.</strong>
|
||||
<span data-i18n="isms.intro.body">Procedura guidata ISO/IEC 27001:2022 (clausole 4-10 + Statement of Applicability). Lo Statement of Applicability viene pre-popolato dalle tue risposte alla Gap Analysis NIS2. Attiva le estensioni cloud ISO 27017/27018 dove pertinenti.</span>
|
||||
</div>
|
||||
<div class="isms-disclaimer" data-i18n="isms.disclaimer">Strumento di supporto e pre-audit: non costituisce una certificazione ISO 27001 né un parere professionale vincolante. Gli obblighi normativi in Italia derivano da NIS2 / D.Lgs. 138/2024 / Determinazioni ACN; ISO 27001/27017/27018 sono best practice.</div>
|
||||
|
||||
<div class="isms-steps" id="isms-steps"></div>
|
||||
|
||||
<!-- STEP 1 - Contesto & Ambito (cl.4) -->
|
||||
<div class="isms-panel" id="panel-0">
|
||||
<div class="card"><div class="card-body">
|
||||
<h3 data-i18n="isms.s1.title">1. Contesto e Ambito (cl. 4)</h3>
|
||||
<div class="isms-field"><label data-i18n="isms.s1.scope">Ambito del SGSI (scope statement)</label><textarea id="f-scope" rows="3" placeholder="Es. Tutti i sistemi e processi a supporto dei servizi ICT erogati dalla sede di..."></textarea></div>
|
||||
<div class="isms-field"><label data-i18n="isms.s1.ctx_int">Contesto interno</label><textarea id="f-ctx-int" rows="2"></textarea></div>
|
||||
<div class="isms-field"><label data-i18n="isms.s1.ctx_ext">Contesto esterno</label><textarea id="f-ctx-ext" rows="2"></textarea></div>
|
||||
<div class="isms-field"><label data-i18n="isms.s1.parties">Parti interessate (una per riga)</label><textarea id="f-parties" rows="3" placeholder="Clienti Autorità (ACN/CSIRT) Fornitori ICT"></textarea></div>
|
||||
<div class="isms-field"><label data-i18n="isms.s1.boundaries">Confini del SGSI</label><textarea id="f-boundaries" rows="2"></textarea></div>
|
||||
<div class="isms-field"><label data-i18n="isms.s1.exclusions">Esclusioni motivate</label><textarea id="f-exclusions" rows="2"></textarea></div>
|
||||
<div class="isms-flags">
|
||||
<strong data-i18n="isms.s1.cloud">Estensioni cloud (attivano i controlli condizionali nel SoA)</strong>
|
||||
<label><input type="checkbox" id="f-uses-cloud"> <span data-i18n="isms.s1.uses_cloud">Usiamo servizi cloud pubblici (Microsoft 365, AWS, Google...) → ISO 27017</span></label>
|
||||
<label><input type="checkbox" id="f-cloud-provider"> <span data-i18n="isms.s1.cloud_provider">Siamo fornitori di servizi cloud → ISO 27017 (lato provider)</span></label>
|
||||
<label><input type="checkbox" id="f-pii-cloud"> <span data-i18n="isms.s1.pii_cloud">Trattiamo dati personali (PII) in cloud pubblico come responsabili → ISO 27018</span></label>
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="saveStep1()" data-i18n="isms.save_next">Salva e continua</button>
|
||||
</div></div>
|
||||
</div>
|
||||
|
||||
<!-- STEP 2 - Leadership (cl.5) -->
|
||||
<div class="isms-panel" id="panel-1">
|
||||
<div class="card"><div class="card-body">
|
||||
<h3 data-i18n="isms.s2.title">2. Leadership: ruoli e responsabilità (cl. 5)</h3>
|
||||
<p class="text-muted" data-i18n="isms.s2.hint">Definisci i ruoli del SGSI con la matrice RACI (Responsible, Accountable, Consulted, Informed).</p>
|
||||
<div id="roles-list"></div>
|
||||
<div class="raci-row" style="border-top:2px solid var(--gray-200,#e5e7eb); margin-top:10px;">
|
||||
<input id="r-name" placeholder="Ruolo (es. CISO)" style="flex:1 1 160px;">
|
||||
<input id="r-resp" placeholder="Responsabilità" style="flex:2 1 220px;">
|
||||
<select id="r-raci"><option value="">RACI</option><option>R</option><option>A</option><option>C</option><option>I</option></select>
|
||||
<button class="btn btn-sm btn-primary" onclick="addRole()" data-i18n="isms.s2.add">Aggiungi ruolo</button>
|
||||
</div>
|
||||
<button class="btn btn-outline" style="margin-top:14px;" onclick="goStep(2)" data-i18n="isms.next">Continua</button>
|
||||
</div></div>
|
||||
</div>
|
||||
|
||||
<!-- STEP 3 - Risk (cl.6) -->
|
||||
<div class="isms-panel" id="panel-2">
|
||||
<div class="card"><div class="card-body">
|
||||
<h3 data-i18n="isms.s3.title">3. Risk: metodologia e obiettivi (cl. 6)</h3>
|
||||
<div class="isms-field"><label data-i18n="isms.s3.method">Metodologia di risk assessment</label><textarea id="f-method" rows="3" placeholder="Es. ISO 27005 / NIST SP 800-30; scala probabilità×impatto 5×5 (già usata nel modulo Rischi)."></textarea></div>
|
||||
<div class="isms-field"><label data-i18n="isms.s3.objectives">Obiettivi del SGSI (uno per riga)</label><textarea id="f-objectives" rows="3"></textarea></div>
|
||||
<p class="text-muted" data-i18n="isms.s3.link">Il registro dei rischi vive nel modulo <a href="risks.html">Rischi</a>: questo passo ne dichiara la metodologia.</p>
|
||||
<button class="btn btn-primary" onclick="saveStep3()" data-i18n="isms.save_next">Salva e continua</button>
|
||||
</div></div>
|
||||
</div>
|
||||
|
||||
<!-- STEP 4 - SoA -->
|
||||
<div class="isms-panel" id="panel-3">
|
||||
<div class="card"><div class="card-body">
|
||||
<h3 data-i18n="isms.s4.title">4. Statement of Applicability</h3>
|
||||
<p class="text-muted" data-i18n="isms.s4.hint">I controlli sono pre-popolati dalle risposte NIS2 dove esiste corrispondenza (badge "da NIS2"). Conferma applicabilità, stato e motivazione per ciascuno.</p>
|
||||
<div style="margin:10px 0;">
|
||||
<button class="btn btn-sm btn-outline" onclick="deriveSoa()" data-i18n="isms.s4.derive">Ri-deriva dal NIS2</button>
|
||||
<span id="soa-stats" class="text-muted" style="margin-left:12px; font-size:.85rem;"></span>
|
||||
</div>
|
||||
<div id="soa-groups"></div>
|
||||
<button class="btn btn-outline" style="margin-top:14px;" onclick="goStep(4)" data-i18n="isms.next">Continua</button>
|
||||
</div></div>
|
||||
</div>
|
||||
|
||||
<!-- STEP 5 - Documented information (cl.7-8) -->
|
||||
<div class="isms-panel" id="panel-4">
|
||||
<div class="card"><div class="card-body">
|
||||
<h3 data-i18n="isms.s5.title">5. Documented Information (cl. 7-8)</h3>
|
||||
<div class="raci-row" style="border:none;">
|
||||
<select id="d-type" style="flex:1 1 200px;">
|
||||
<option value="policy_sgsi">Policy SGSI</option>
|
||||
<option value="dichiarazione_applicabilita">Dichiarazione di Applicabilità</option>
|
||||
<option value="politica_controllo_accessi">Politica controllo accessi</option>
|
||||
<option value="procedura_incident">Procedura gestione incidenti</option>
|
||||
<option value="procedura_continuita">Procedura continuità operativa</option>
|
||||
<option value="politica_fornitori">Politica sicurezza fornitori</option>
|
||||
</select>
|
||||
<button class="btn btn-sm btn-primary" onclick="aiGenDoc()" id="btn-aigen" data-i18n="isms.s5.aigen">Genera bozza con AI</button>
|
||||
</div>
|
||||
<div class="savehint" id="aigen-hint"></div>
|
||||
<div id="docs-list" style="margin-top:14px;"></div>
|
||||
<button class="btn btn-outline" style="margin-top:14px;" onclick="goStep(5)" data-i18n="isms.next">Continua</button>
|
||||
</div></div>
|
||||
</div>
|
||||
|
||||
<!-- STEP 6 - Monitoring & Readiness (cl.9-10) -->
|
||||
<div class="isms-panel" id="panel-5">
|
||||
<div class="card"><div class="card-body">
|
||||
<h3 data-i18n="isms.s6.title">6. Monitoraggio e Miglioramento (cl. 9-10)</h3>
|
||||
<p class="text-muted" data-i18n="isms.s6.hint">Gli audit interni e le non conformità si gestiscono nei moduli esistenti: <a href="reports.html">Audit & Report</a> e NCR/CAPA.</p>
|
||||
<h4 data-i18n="isms.s6.readiness">Completamento del SGSI</h4>
|
||||
<div style="text-align:center; margin:14px 0;">
|
||||
<div style="font-size:2.6rem; font-weight:800;" id="readiness-pct">0%</div>
|
||||
</div>
|
||||
<div id="readiness-checklist"></div>
|
||||
</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/common.js"></script>
|
||||
<script src="js/api.js"></script>
|
||||
<script src="js/help.js"></script>
|
||||
<script src="js/isms.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -204,6 +204,26 @@ class NIS2API {
|
||||
acnReport(id) { return this._acn(this.get(`/acn-gap/${id}/report`)); }
|
||||
acnAiAnalyze(id) { return this._acn(this.post(`/acn-gap/${id}/aiAnalyze`, {})); }
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// Modello Organizzativo SGSI (ISO 27001/27017/27018) — vedi IsmsModelController
|
||||
// Stesso contratto degli acn*: ritornano `data`, lanciano su success=false.
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
ismsGetModel() { return this._acn(this.get('/isms/model')); }
|
||||
ismsSaveModel(data) { return this._acn(this.post('/isms/model', data || {})); }
|
||||
ismsAnnexControls() { return this._acn(this.get('/isms/annex-controls')); }
|
||||
ismsGetSoa() { return this._acn(this.get('/isms/soa')); }
|
||||
ismsDeriveSoa() { return this._acn(this.post('/isms/soa/derive', {})); }
|
||||
ismsUpdateSoa(data) { return this._acn(this.put('/isms/soa', data)); }
|
||||
ismsRoles() { return this._acn(this.get('/isms/roles')); }
|
||||
ismsSaveRole(data) { return this._acn(this.post('/isms/roles', data)); }
|
||||
ismsDeleteRole(id) { return this._acn(this.del(`/isms/roles/${id}`)); }
|
||||
ismsDocuments() { return this._acn(this.get('/isms/documents')); }
|
||||
ismsCreateDocument(data) { return this._acn(this.post('/isms/documents', data)); }
|
||||
ismsAiGenerateDocument(data) { return this._acn(this.post('/isms/documents/ai-generate', data)); }
|
||||
ismsUpdateDocument(id, data) { return this._acn(this.put(`/isms/documents/${id}`, data)); }
|
||||
ismsReadiness() { return this._acn(this.get('/isms/readiness')); }
|
||||
ismsExport() { return this._acn(this.get('/isms/export')); }
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// Dashboard
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -189,6 +189,7 @@ function loadSidebar() {
|
||||
{ name: 'Compliance Journey', href: 'workflow.html', icon: `<svg viewBox="0 0 20 20" fill="currentColor"><path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"/></svg>` },
|
||||
{ name: 'Gap Analysis', href: 'assessment.html', icon: iconClipboardCheck(), i18nKey: 'nav.gap_analysis' },
|
||||
{ name: 'Gap Analysis ACN', href: 'acn-gap.html', icon: iconClipboardCheck(), i18nKey: 'nav.acn_gap' },
|
||||
{ name: 'Modello SGSI (ISO 27001)', href: 'isms.html', icon: `<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M2.166 4.999A11.954 11.954 0 0010 1.944 11.954 11.954 0 0017.834 5c.11.65.166 1.32.166 2.001 0 5.225-3.34 9.67-8 11.317C5.34 16.67 2 12.225 2 7c0-.682.057-1.35.166-2.001zm11.541 3.708a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/></svg>`, i18nKey: 'nav.isms' },
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -159,6 +159,48 @@ const HelpSystem = (function () {
|
||||
]
|
||||
},
|
||||
|
||||
'isms': {
|
||||
title: 'Guida - Modello Organizzativo SGSI (ISO 27001)',
|
||||
intro: 'Il modulo Modello Organizzativo ti guida nella costruzione di un Sistema di Gestione della Sicurezza delle Informazioni (SGSI / ISMS) secondo ISO/IEC 27001:2022 (clausole 4-10) + lo Statement of Applicability (SoA) sui controlli Annex A. E\' uno strumento di SUPPORTO e PRE-AUDIT: non sostituisce l\'auditor ne costituisce una certificazione.',
|
||||
sections: [
|
||||
{
|
||||
heading: 'Il wizard in 6 passi (clausole ISO 27001:2022)',
|
||||
items: [
|
||||
'<strong>1. Contesto e Ambito (cl. 4)</strong>: definisci lo scope del SGSI, contesto interno/esterno, parti interessate, confini ed esclusioni motivate. Qui attivi anche le estensioni cloud.',
|
||||
'<strong>2. Leadership (cl. 5)</strong>: ruoli e responsabilita del SGSI con matrice RACI (Responsible, Accountable, Consulted, Informed).',
|
||||
'<strong>3. Risk (cl. 6)</strong>: dichiari la metodologia di risk assessment (es. ISO 27005) e gli obiettivi. Il registro dei rischi resta nel modulo Rischi.',
|
||||
'<strong>4. Statement of Applicability</strong>: i 93 controlli Annex A con applicabilita, stato e motivazione.',
|
||||
'<strong>5. Documented Information (cl. 7-8)</strong>: policy e procedure, con bozze generabili dall\'AI (revisione umana obbligatoria).',
|
||||
'<strong>6. Monitoraggio e Miglioramento (cl. 9-10)</strong>: audit interni e non conformita si gestiscono nei moduli Audit & Report e NCR/CAPA. Una checklist mostra il completamento del SGSI.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'SoA pre-popolato dal NIS2',
|
||||
items: [
|
||||
'Ogni domanda della Gap Analysis NIS2 Art.21 e gia collegata a un controllo ISO 27001 (es. A.5.1): lo Statement of Applicability viene quindi <strong>pre-compilato</strong> a partire dalle tue risposte NIS2 (badge "da NIS2").',
|
||||
'Lo stato derivato e una <strong>proposta da confermare</strong>: implemented=100%, partial=50%, not_implemented=0%; not_applicable esclude il controllo dalla derivazione.',
|
||||
'I controlli senza corrispondenza NIS2 (es. parte dei controlli fisici A.7) restano da compilare manualmente.',
|
||||
'Il pulsante "Ri-deriva dal NIS2" aggiunge solo i controlli mancanti: non sovrascrive le tue modifiche manuali.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Estensioni cloud ISO 27017 / 27018',
|
||||
items: [
|
||||
'Se usi servizi cloud pubblici o sei un fornitore cloud, attiva <strong>ISO/IEC 27017:2015</strong>: aggiunge 7 controlli cloud (CLD.*) al SoA.',
|
||||
'Se tratti dati personali (PII) in cloud pubblico come responsabile, attiva <strong>ISO/IEC 27018:2019</strong>: aggiunge i controlli privacy (PII.*) allineati ai principi ISO 29100, sinergici con il GDPR.',
|
||||
'Le sezioni cloud/PII compaiono nel SoA solo se attivi i relativi flag nel passo 1.'
|
||||
]
|
||||
}
|
||||
],
|
||||
references: [
|
||||
'ISO/IEC 27001:2022 - Requisiti del SGSI (clausole 4-10 + Annex A, 93 controlli)',
|
||||
'ISO/IEC 27002:2022 - Guida implementativa dei controlli Annex A',
|
||||
'ISO/IEC 27017:2015 - Controlli di sicurezza per i servizi cloud (CLD.*)',
|
||||
'ISO/IEC 27018:2019 - Protezione dei PII nel cloud pubblico (PII processor)',
|
||||
'NOTA: ISO e best practice non vincolante; gli obblighi italiani derivano da NIS2 / D.Lgs. 138/2024 / Determinazioni ACN.'
|
||||
]
|
||||
},
|
||||
|
||||
// ─── Risk Management ─────────────────────────────────────────
|
||||
'risks': {
|
||||
title: 'Guida - Gestione Rischi',
|
||||
@@ -911,6 +953,8 @@ const HelpSystem = (function () {
|
||||
'acn-gap.html': 'acn',
|
||||
'acn-gap': 'acn',
|
||||
'acn': 'acn',
|
||||
'isms.html': 'isms',
|
||||
'isms': 'isms',
|
||||
'risks.html': 'risks',
|
||||
'risks': 'risks',
|
||||
'incidents.html': 'incidents',
|
||||
|
||||
@@ -36,6 +36,47 @@ const I18n = (function () {
|
||||
'acn.results.ai': { it: 'Analisi AI dei gap', en: 'AI gap analysis' },
|
||||
'acn.results.gaps': { it: "Requisiti non conformi (piano d'azione)", en: 'Non-compliant requirements (action plan)' },
|
||||
'acn.backstart': { it: "Torna all'inizio", en: 'Back to start' },
|
||||
// ── Modello Organizzativo SGSI (ISO 27001) ──
|
||||
'nav.isms': { it: 'Modello SGSI (ISO 27001)', en: 'ISMS Model (ISO 27001)' },
|
||||
'isms.title': { it: 'Modello Organizzativo SGSI (ISO 27001)', en: 'ISMS Organizational Model (ISO 27001)' },
|
||||
'isms.export': { it: 'Esporta SGSI', en: 'Export ISMS' },
|
||||
'isms.intro.title': { it: 'Costruisci il tuo Sistema di Gestione della Sicurezza delle Informazioni.', en: 'Build your Information Security Management System.' },
|
||||
'isms.intro.body': { it: 'Procedura guidata ISO/IEC 27001:2022 (clausole 4-10 + Statement of Applicability). Lo Statement of Applicability viene pre-popolato dalle tue risposte alla Gap Analysis NIS2. Attiva le estensioni cloud ISO 27017/27018 dove pertinenti.', en: 'Guided ISO/IEC 27001:2022 procedure (clauses 4-10 + Statement of Applicability). The Statement of Applicability is pre-populated from your NIS2 Gap Analysis answers. Enable the ISO 27017/27018 cloud extensions where relevant.' },
|
||||
'isms.disclaimer': { it: 'Strumento di supporto e pre-audit: non costituisce una certificazione ISO 27001 né un parere professionale vincolante. Gli obblighi normativi in Italia derivano da NIS2 / D.Lgs. 138/2024 / Determinazioni ACN; ISO 27001/27017/27018 sono best practice.', en: 'Support and pre-audit tool: it is not an ISO 27001 certification nor binding professional advice. Regulatory obligations in Italy stem from NIS2 / Legislative Decree 138/2024 / ACN Determinations; ISO 27001/27017/27018 are best practices.' },
|
||||
'isms.step1': { it: 'Contesto', en: 'Context' },
|
||||
'isms.step2': { it: 'Leadership', en: 'Leadership' },
|
||||
'isms.step3': { it: 'Risk', en: 'Risk' },
|
||||
'isms.step4': { it: 'SoA', en: 'SoA' },
|
||||
'isms.step5': { it: 'Documenti', en: 'Documents' },
|
||||
'isms.step6': { it: 'Monitoraggio', en: 'Monitoring' },
|
||||
'isms.save_next': { it: 'Salva e continua', en: 'Save and continue' },
|
||||
'isms.next': { it: 'Continua', en: 'Continue' },
|
||||
'isms.s1.title': { it: '1. Contesto e Ambito (cl. 4)', en: '1. Context and Scope (cl. 4)' },
|
||||
'isms.s1.scope': { it: 'Ambito del SGSI (scope statement)', en: 'ISMS scope statement' },
|
||||
'isms.s1.ctx_int': { it: 'Contesto interno', en: 'Internal context' },
|
||||
'isms.s1.ctx_ext': { it: 'Contesto esterno', en: 'External context' },
|
||||
'isms.s1.parties': { it: 'Parti interessate (una per riga)', en: 'Interested parties (one per line)' },
|
||||
'isms.s1.boundaries': { it: 'Confini del SGSI', en: 'ISMS boundaries' },
|
||||
'isms.s1.exclusions': { it: 'Esclusioni motivate', en: 'Justified exclusions' },
|
||||
'isms.s1.cloud': { it: 'Estensioni cloud (attivano i controlli condizionali nel SoA)', en: 'Cloud extensions (enable conditional controls in the SoA)' },
|
||||
'isms.s1.uses_cloud': { it: 'Usiamo servizi cloud pubblici (Microsoft 365, AWS, Google...) → ISO 27017', en: 'We use public cloud services (Microsoft 365, AWS, Google...) → ISO 27017' },
|
||||
'isms.s1.cloud_provider': { it: 'Siamo fornitori di servizi cloud → ISO 27017 (lato provider)', en: 'We are cloud service providers → ISO 27017 (provider side)' },
|
||||
'isms.s1.pii_cloud': { it: 'Trattiamo dati personali (PII) in cloud pubblico come responsabili → ISO 27018', en: 'We process personal data (PII) in public cloud as processors → ISO 27018' },
|
||||
'isms.s2.title': { it: '2. Leadership: ruoli e responsabilità (cl. 5)', en: '2. Leadership: roles and responsibilities (cl. 5)' },
|
||||
'isms.s2.hint': { it: 'Definisci i ruoli del SGSI con la matrice RACI (Responsible, Accountable, Consulted, Informed).', en: 'Define the ISMS roles with the RACI matrix (Responsible, Accountable, Consulted, Informed).' },
|
||||
'isms.s2.add': { it: 'Aggiungi ruolo', en: 'Add role' },
|
||||
'isms.s3.title': { it: '3. Risk: metodologia e obiettivi (cl. 6)', en: '3. Risk: methodology and objectives (cl. 6)' },
|
||||
'isms.s3.method': { it: 'Metodologia di risk assessment', en: 'Risk assessment methodology' },
|
||||
'isms.s3.objectives': { it: 'Obiettivi del SGSI (uno per riga)', en: 'ISMS objectives (one per line)' },
|
||||
'isms.s3.link': { it: 'Il registro dei rischi vive nel modulo Rischi: questo passo ne dichiara la metodologia.', en: 'The risk register lives in the Risks module: this step declares its methodology.' },
|
||||
'isms.s4.title': { it: '4. Statement of Applicability', en: '4. Statement of Applicability' },
|
||||
'isms.s4.hint': { it: 'I controlli sono pre-popolati dalle risposte NIS2 dove esiste corrispondenza (badge "da NIS2"). Conferma applicabilità, stato e motivazione per ciascuno.', en: 'Controls are pre-populated from NIS2 answers where a mapping exists ("from NIS2" badge). Confirm applicability, status and justification for each.' },
|
||||
'isms.s4.derive': { it: 'Ri-deriva dal NIS2', en: 'Re-derive from NIS2' },
|
||||
'isms.s5.title': { it: '5. Documented Information (cl. 7-8)', en: '5. Documented Information (cl. 7-8)' },
|
||||
'isms.s5.aigen': { it: 'Genera bozza con AI', en: 'Generate draft with AI' },
|
||||
'isms.s6.title': { it: '6. Monitoraggio e Miglioramento (cl. 9-10)', en: '6. Monitoring and Improvement (cl. 9-10)' },
|
||||
'isms.s6.hint': { it: 'Gli audit interni e le non conformità si gestiscono nei moduli esistenti: Audit & Report e NCR/CAPA.', en: 'Internal audits and non-conformities are managed in the existing modules: Audit & Reports and NCR/CAPA.' },
|
||||
'isms.s6.readiness': { it: 'Completamento del SGSI', en: 'ISMS completion' },
|
||||
'nav.management': { it: 'Gestione', en: 'Management' },
|
||||
'nav.risks': { it: 'Rischi', en: 'Risks' },
|
||||
'nav.incidents': { it: 'Incidenti', en: 'Incidents' },
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
/**
|
||||
* NIS2 Agile - Modello Organizzativo SGSI (ISO 27001/27017/27018)
|
||||
* Wizard 6 step: Contesto → Leadership → Risk → SoA → Documenti → Monitoraggio.
|
||||
* Pattern coerente con acn-gap.html: client api.* che ritorna `data` e lancia su errore.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const STEP_KEYS = ['isms.step1','isms.step2','isms.step3','isms.step4','isms.step5','isms.step6'];
|
||||
const STEP_FALLBACK = ['Contesto','Leadership','Risk','SoA','Documenti','Monitoraggio'];
|
||||
const THEME_LABELS = {
|
||||
organizational: { it:'Organizzativi', en:'Organizational' },
|
||||
people: { it:'Persone', en:'People' },
|
||||
physical: { it:'Fisici', en:'Physical' },
|
||||
technological: { it:'Tecnologici', en:'Technological' },
|
||||
privacy: { it:'Privacy (PII)', en:'Privacy (PII)' }
|
||||
};
|
||||
|
||||
let ISMS = { model:null, step:0, soaLoaded:false };
|
||||
let soaSaveTimer = {};
|
||||
|
||||
function lang(){ try { return I18n.getLang ? I18n.getLang() : 'it'; } catch(e){ return 'it'; } }
|
||||
function el(id){ return document.getElementById(id); }
|
||||
function esc(s){ const d=document.createElement('div'); d.textContent=s==null?'':String(s); return d.innerHTML; }
|
||||
function hint(id, msg){ const e=el(id); if(e){ e.textContent=msg; } }
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async function(){
|
||||
if (typeof checkAuth==='function' && !checkAuth()) return;
|
||||
if (window.I18n && I18n.init) I18n.init('it');
|
||||
if (typeof loadSidebar==='function') loadSidebar();
|
||||
if (window.HelpSystem && HelpSystem.init) HelpSystem.init();
|
||||
renderSteps();
|
||||
await loadModel();
|
||||
});
|
||||
|
||||
function renderSteps(){
|
||||
let html='';
|
||||
STEP_FALLBACK.forEach(function(lbl, i){
|
||||
const t = (window.I18n && I18n.t) ? I18n.t(STEP_KEYS[i]) : lbl;
|
||||
html += '<div class="isms-step" id="step-'+i+'" onclick="goStep('+i+')"><div class="num">'+(i+1)+'</div><div>'+esc(t&&t!==STEP_KEYS[i]?t:lbl)+'</div></div>';
|
||||
});
|
||||
el('isms-steps').innerHTML = html;
|
||||
}
|
||||
|
||||
function goStep(i){
|
||||
ISMS.step = i;
|
||||
document.querySelectorAll('.isms-panel').forEach(function(p){ p.classList.remove('active'); });
|
||||
const panel = el('panel-'+i); if (panel) panel.classList.add('active');
|
||||
document.querySelectorAll('.isms-step').forEach(function(s,idx){ s.classList.toggle('active', idx===i); });
|
||||
if (i===1) loadRoles();
|
||||
if (i===3) loadSoa();
|
||||
if (i===4) loadDocs();
|
||||
if (i===5) loadReadiness();
|
||||
window.scrollTo({top:0,behavior:'smooth'});
|
||||
}
|
||||
|
||||
async function loadModel(){
|
||||
try {
|
||||
const res = await api.ismsGetModel();
|
||||
ISMS.model = res.model;
|
||||
if (ISMS.model) fillStep1(ISMS.model);
|
||||
markDone();
|
||||
} catch(e){ /* tabella non ancora migrata o nessun modello: si parte da zero */ }
|
||||
goStep(0);
|
||||
}
|
||||
|
||||
function fillStep1(m){
|
||||
el('f-scope').value = m.scope_statement||'';
|
||||
el('f-ctx-int').value = m.context_internal||'';
|
||||
el('f-ctx-ext').value = m.context_external||'';
|
||||
el('f-parties').value = (m.interested_parties||[]).join('\n');
|
||||
el('f-boundaries').value = m.boundaries||'';
|
||||
el('f-exclusions').value = m.exclusions||'';
|
||||
el('f-uses-cloud').checked = !!m.uses_public_cloud;
|
||||
el('f-cloud-provider').checked = !!m.is_cloud_provider;
|
||||
el('f-pii-cloud').checked = !!m.processes_pii_in_cloud;
|
||||
if (m.risk_methodology!==undefined) el('f-method').value = m.risk_methodology||'';
|
||||
el('f-objectives').value = (m.isms_objectives||[]).join('\n');
|
||||
}
|
||||
|
||||
function linesToArr(v){ return (v||'').split('\n').map(function(s){return s.trim();}).filter(Boolean); }
|
||||
|
||||
async function saveStep1(){
|
||||
const data = {
|
||||
scope_statement: el('f-scope').value,
|
||||
context_internal: el('f-ctx-int').value,
|
||||
context_external: el('f-ctx-ext').value,
|
||||
interested_parties: linesToArr(el('f-parties').value),
|
||||
boundaries: el('f-boundaries').value,
|
||||
exclusions: el('f-exclusions').value,
|
||||
uses_public_cloud: el('f-uses-cloud').checked,
|
||||
is_cloud_provider: el('f-cloud-provider').checked,
|
||||
processes_pii_in_cloud: el('f-pii-cloud').checked
|
||||
};
|
||||
try {
|
||||
await api.ismsSaveModel(data);
|
||||
hint('isms-savehint', lang()==='en'?'Saved':'Salvato');
|
||||
ISMS.soaLoaded = false; // i flag cloud cambiano il perimetro SoA
|
||||
await loadModel();
|
||||
goStep(1);
|
||||
} catch(e){ alert((e&&e.message)||'Errore'); }
|
||||
}
|
||||
|
||||
async function saveStep3(){
|
||||
try {
|
||||
await api.ismsSaveModel({ risk_methodology: el('f-method').value, isms_objectives: linesToArr(el('f-objectives').value) });
|
||||
hint('isms-savehint', lang()==='en'?'Saved':'Salvato');
|
||||
await loadModel();
|
||||
goStep(3);
|
||||
} catch(e){ alert((e&&e.message)||'Errore'); }
|
||||
}
|
||||
|
||||
// ── Ruoli (cl.5) ──
|
||||
async function loadRoles(){
|
||||
try {
|
||||
const res = await api.ismsRoles();
|
||||
let html='';
|
||||
(res.roles||[]).forEach(function(r){
|
||||
html += '<div class="raci-row"><span class="soa-code">'+(r.raci||'-')+'</span>'+
|
||||
'<strong style="flex:1 1 140px;">'+esc(r.role_name)+'</strong>'+
|
||||
'<span style="flex:2 1 200px; color:var(--gray-500,#6b7280); font-size:.85rem;">'+esc(r.responsibility||'')+'</span>'+
|
||||
'<button class="btn btn-sm btn-outline" onclick="delRole('+r.id+')">×</button></div>';
|
||||
});
|
||||
el('roles-list').innerHTML = html || '<p class="text-muted">'+(lang()==='en'?'No roles yet.':'Nessun ruolo definito.')+'</p>';
|
||||
} catch(e){ el('roles-list').innerHTML='<p class="text-muted">'+esc((e&&e.message)||'')+'</p>'; }
|
||||
}
|
||||
async function addRole(){
|
||||
const name = el('r-name').value.trim();
|
||||
if(!name){ return; }
|
||||
try {
|
||||
await api.ismsSaveRole({ role_name:name, responsibility:el('r-resp').value, raci:el('r-raci').value });
|
||||
el('r-name').value=''; el('r-resp').value=''; el('r-raci').value='';
|
||||
loadRoles(); markDone();
|
||||
} catch(e){ alert((e&&e.message)||'Errore'); }
|
||||
}
|
||||
async function delRole(id){ try { await api.ismsDeleteRole(id); loadRoles(); } catch(e){} }
|
||||
|
||||
// ── SoA (cl.6.1.3) ──
|
||||
async function loadSoa(){
|
||||
if (ISMS.soaLoaded) return;
|
||||
el('soa-groups').innerHTML = '<p class="text-muted">'+(lang()==='en'?'Loading…':'Caricamento…')+'</p>';
|
||||
try {
|
||||
const res = await api.ismsGetSoa();
|
||||
renderSoa(res);
|
||||
ISMS.soaLoaded = true;
|
||||
markDone();
|
||||
} catch(e){
|
||||
el('soa-groups').innerHTML = '<p class="text-muted">'+esc((e&&e.message)||'')+'</p>';
|
||||
}
|
||||
}
|
||||
async function deriveSoa(){
|
||||
try { await api.ismsDeriveSoa(); ISMS.soaLoaded=false; await loadSoa(); }
|
||||
catch(e){ alert((e&&e.message)||'Errore'); }
|
||||
}
|
||||
function renderSoa(res){
|
||||
showSoaStats(res.stats);
|
||||
let html='';
|
||||
(res.groups||[]).forEach(function(g, gi){
|
||||
// raggruppa per tema
|
||||
const byTheme = {};
|
||||
(g.controls||[]).forEach(function(c){ (byTheme[c.theme]=byTheme[c.theme]||[]).push(c); });
|
||||
let inner='';
|
||||
Object.keys(byTheme).forEach(function(th){
|
||||
inner += '<div class="soa-theme">'+esc(THEME_LABELS[th]?THEME_LABELS[th][lang()]:th)+'</div>';
|
||||
byTheme[th].forEach(function(c){ inner += renderCtrl(c); });
|
||||
});
|
||||
html += '<div class="soa-std'+(gi===0?' open':'')+'"><div class="soa-std-head" onclick="this.parentNode.classList.toggle(\'open\')">'+
|
||||
'<strong>'+esc(g.label)+'</strong><span class="text-muted">'+(g.controls||[]).length+' '+(lang()==='en'?'controls':'controlli')+'</span></div>'+
|
||||
'<div class="soa-std-body">'+inner+'</div></div>';
|
||||
});
|
||||
el('soa-groups').innerHTML = html || '<p class="text-muted">'+(lang()==='en'?'No controls.':'Nessun controllo.')+'</p>';
|
||||
}
|
||||
function renderCtrl(c){
|
||||
const title = lang()==='en' ? (c.title_en||c.title_it) : (c.title_it||c.title_en);
|
||||
const derived = (+c.derived_from_nis2) ? '<span class="soa-derived" title="'+esc(c.source_ref||'')+'">'+(lang()==='en'?'from NIS2':'da NIS2')+'</span>' : '';
|
||||
const applicable = (+c.applicable)===1;
|
||||
const statuses = [
|
||||
['not_started', lang()==='en'?'Not started':'Da iniziare'],
|
||||
['in_progress', lang()==='en'?'In progress':'In corso'],
|
||||
['implemented', lang()==='en'?'Implemented':'Attuato'],
|
||||
['verified', lang()==='en'?'Verified':'Verificato']
|
||||
];
|
||||
let opts='';
|
||||
statuses.forEach(function(s){
|
||||
const sel = c.implementation_status===s[0] ? ('sel-'+s[0]) : '';
|
||||
opts += '<button type="button" class="soa-opt '+sel+'" onclick="setSoaStatus(this,\''+esc(c.control_code)+'\',\''+s[0]+'\')">'+s[1]+'</button>';
|
||||
});
|
||||
return '<div class="soa-ctrl" data-code="'+esc(c.control_code)+'">'+
|
||||
'<div class="soa-ctrl-head"><div><span class="soa-code">'+esc(c.control_code)+'</span>'+esc(title)+' '+derived+'</div>'+
|
||||
'<label style="font-size:.82rem; white-space:nowrap;"><input type="checkbox" '+(applicable?'checked':'')+' onchange="setSoaApplicable(\''+esc(c.control_code)+'\',this.checked)"> '+(lang()==='en'?'Applicable':'Applicabile')+'</label></div>'+
|
||||
'<div class="soa-opts" style="'+(applicable?'':'opacity:.4;pointer-events:none;')+'">'+opts+'</div>'+
|
||||
'<textarea class="soa-justif" placeholder="'+(lang()==='en'?'Justification (inclusion/exclusion)':'Motivazione (inclusione/esclusione)')+'" onchange="setSoaJustif(\''+esc(c.control_code)+'\',this.value,'+applicable+')">'+esc(applicable?(c.justification_inclusion||''):(c.justification_exclusion||''))+'</textarea>'+
|
||||
'</div>';
|
||||
}
|
||||
function setSoaStatus(btn, code, status){
|
||||
const wrap = btn.parentNode;
|
||||
Array.prototype.forEach.call(wrap.querySelectorAll('.soa-opt'), function(b){ b.className='soa-opt'; });
|
||||
btn.className='soa-opt sel-'+status;
|
||||
const pct = {not_started:0,in_progress:50,implemented:100,verified:100}[status];
|
||||
queueSoaSave(code, { control_code:code, implementation_status:status, implementation_pct:pct });
|
||||
}
|
||||
function setSoaApplicable(code, applicable){
|
||||
queueSoaSave(code, { control_code:code, applicable:applicable });
|
||||
ISMS.soaLoaded=false; setTimeout(loadSoa, 400);
|
||||
}
|
||||
function setSoaJustif(code, val, applicable){
|
||||
queueSoaSave(code, applicable ? { control_code:code, justification_inclusion:val } : { control_code:code, justification_exclusion:val });
|
||||
}
|
||||
function queueSoaSave(code, payload){
|
||||
hint('isms-savehint', lang()==='en'?'Saving…':'Salvataggio…');
|
||||
if (soaSaveTimer[code]) clearTimeout(soaSaveTimer[code]);
|
||||
soaSaveTimer[code] = setTimeout(async function(){
|
||||
try { const r = await api.ismsUpdateSoa(payload); showSoaStats(r.stats); hint('isms-savehint', lang()==='en'?'Saved':'Salvato'); }
|
||||
catch(e){ hint('isms-savehint', lang()==='en'?'Save pending':'Salvataggio in sospeso'); }
|
||||
}, 600);
|
||||
}
|
||||
function showSoaStats(s){
|
||||
if(!s){ return; }
|
||||
const e = el('soa-stats');
|
||||
if(e) e.textContent = (lang()==='en'?'Applicable: ':'Applicabili: ')+s.applicable+' · '+(lang()==='en'?'avg impl.: ':'impl. media: ')+s.avg_implementation_pct+'%';
|
||||
}
|
||||
|
||||
// ── Documenti (cl.7-8) ──
|
||||
async function loadDocs(){
|
||||
try {
|
||||
const res = await api.ismsDocuments();
|
||||
let html='';
|
||||
(res.documents||[]).forEach(function(d){
|
||||
const ai = (+d.ai_generated)?'<span class="soa-derived">AI</span>':'';
|
||||
html += '<div class="ck-item"><div><strong>'+esc(d.title)+'</strong> '+ai+' <span class="text-muted" style="font-size:.8rem;">('+esc(d.doc_type)+')</span></div>'+
|
||||
'<span class="badge badge-'+(d.status==='approved'?'success':'warning')+'">'+esc(d.status)+'</span></div>';
|
||||
});
|
||||
el('docs-list').innerHTML = html || '<p class="text-muted">'+(lang()==='en'?'No documents yet.':'Nessun documento.')+'</p>';
|
||||
} catch(e){ el('docs-list').innerHTML='<p class="text-muted">'+esc((e&&e.message)||'')+'</p>'; }
|
||||
}
|
||||
async function aiGenDoc(){
|
||||
const btn = el('btn-aigen'); btn.disabled=true;
|
||||
hint('aigen-hint', lang()==='en'?'Generating draft…':'Generazione bozza in corso…');
|
||||
try {
|
||||
await api.ismsAiGenerateDocument({ doc_type: el('d-type').value });
|
||||
hint('aigen-hint', lang()==='en'?'Draft created (review required).':'Bozza creata (revisione obbligatoria).');
|
||||
loadDocs(); markDone();
|
||||
} catch(e){ hint('aigen-hint',''); alert((e&&e.message)||(lang()==='en'?'AI unavailable':'AI non disponibile')); }
|
||||
finally { btn.disabled=false; }
|
||||
}
|
||||
|
||||
// ── Readiness (cl.9-10) ──
|
||||
async function loadReadiness(){
|
||||
try {
|
||||
const res = await api.ismsReadiness();
|
||||
el('readiness-pct').textContent = (res.overall_pct||0)+'%';
|
||||
let html='';
|
||||
(res.checklist||[]).forEach(function(c){
|
||||
html += '<div class="ck-item"><span><span class="soa-code">'+esc(c.clause)+'</span>'+esc(c.label)+'</span>'+
|
||||
'<span class="'+(c.done?'ck-yes':'ck-no')+'">'+(c.done?'✓':'—')+'</span></div>';
|
||||
});
|
||||
el('readiness-checklist').innerHTML = html;
|
||||
} catch(e){ el('readiness-checklist').innerHTML='<p class="text-muted">'+esc((e&&e.message)||'')+'</p>'; }
|
||||
}
|
||||
|
||||
function markDone(){
|
||||
// marca gli step "completati" in base allo stato del modello (best-effort).
|
||||
const m = ISMS.model;
|
||||
const done = [ m&&!!m.scope_statement, false, m&&!!m.risk_methodology, ISMS.soaLoaded, false, false ];
|
||||
done.forEach(function(d,i){ const s=el('step-'+i); if(s) s.classList.toggle('done', !!d); });
|
||||
}
|
||||
|
||||
async function ismsExportView(){
|
||||
try {
|
||||
const data = await api.ismsExport();
|
||||
const w = window.open('', '_blank');
|
||||
if(!w){ return; }
|
||||
const m = data.model||{};
|
||||
let soa='';
|
||||
(data.soa||[]).forEach(function(c){
|
||||
soa += '<tr><td>'+esc(c.control_code)+'</td><td>'+esc(c.title_it||'')+'</td><td>'+((+c.applicable)?'Sì':'No')+'</td><td>'+esc(c.implementation_status||'')+'</td><td>'+(c.implementation_pct||0)+'%</td></tr>';
|
||||
});
|
||||
w.document.write('<html><head><meta charset="utf-8"><title>SGSI - '+esc((data.organization&&data.organization.name)||'')+'</title>'+
|
||||
'<style>body{font-family:Arial,sans-serif;padding:30px;color:#111} h1,h2{color:#1e40af} table{width:100%;border-collapse:collapse;font-size:12px} td,th{border:1px solid #ccc;padding:5px;text-align:left} .disc{background:#fffbeb;border:1px solid #fde68a;padding:10px;font-size:12px;margin:16px 0}</style></head><body>'+
|
||||
'<h1>Modello Organizzativo SGSI (ISO/IEC 27001:2022)</h1>'+
|
||||
'<p><strong>'+esc((data.organization&&data.organization.name)||'')+'</strong> — generato il '+esc(data.generated_at||'')+'</p>'+
|
||||
'<div class="disc">'+esc(data.disclaimer||'')+'</div>'+
|
||||
'<h2>Ambito</h2><p>'+esc(m.scope_statement||'—')+'</p>'+
|
||||
'<h2>Metodologia di risk assessment</h2><p>'+esc(m.risk_methodology||'—')+'</p>'+
|
||||
'<h2>Statement of Applicability</h2><table><tr><th>Controllo</th><th>Titolo</th><th>Applicabile</th><th>Stato</th><th>%</th></tr>'+soa+'</table>'+
|
||||
'</body></html>');
|
||||
w.document.close();
|
||||
} catch(e){ alert((e&&e.message)||(lang()==='en'?'Start the SGSI first.':'Avvia prima il SGSI.')); }
|
||||
}
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":"1.13.0","build":"2026-06-01-v1.13.0","date":"2026-06-01","changelog":"NUOVO modulo Gap Analysis ACN: assessment di conformita di secondo livello sulle misure e requisiti puntuali della Determinazione ACN 164179/2025 (non solo le 10 lettere generiche Art.21). Distingue soggetti importanti (37 misure/87 requisiti) ed essenziali (43/116), con codifica Framework Nazionale GV/ID/PR/DE/RS/RC, scoring per funzione, piano d'azione gap e analisi AI con grounding sui 203 requisiti ACN. Dataset estratto dai testi ufficiali ACN. Guida, help, traduzioni IT/EN aggiornati."}
|
||||
{"version":"1.14.0","build":"2026-06-11-v1.14.0","date":"2026-06-11","changelog":"NUOVO modulo Modello Organizzativo SGSI (ISO/IEC 27001:2022): procedura guidata in 6 step (Contesto/Ambito cl.4, Leadership+RACI cl.5, Risk cl.6, Statement of Applicability, Documented Information cl.7-8, Monitoraggio cl.9-10). SoA sui 93 controlli Annex A:2022 PRE-POPOLATO dalle risposte Gap Analysis NIS2 (ogni domanda mappa un controllo ISO). Estensioni cloud condizionali ISO/IEC 27017:2015 (7 controlli CLD.*) e ISO/IEC 27018:2019 (controlli PII per processor in cloud, sinergia GDPR). Generazione bozze documenti via AI con grounding fonti certe. Backend IsmsModelController + migration 037 (tabelle SGSI) e 038 (dataset 111 controlli). Frontend isms.html/isms.js, voce sidebar, help online + traduzioni IT/EN, ingest KB ISO. Strumento di supporto/pre-audit (non certificazione). Migration DA APPLICARE su host."}
|
||||
|
||||
Reference in New Issue
Block a user