[FEAT] Integrazione analisi docs/nis2 v1.7.0 — scoring asset, tassonomia incidenti, PIR, NIST CSF, fonti certe
Fase 1 - Asset Relevance Scoring NIS2 (GV.OC-04): metodologia 0-100 a 6 criteri, AssetScoringService + endpoint scoringGrid/score/relevantSystems + UI assets.html + registro stampabile. Fase 2 - Tassonomia incidenti Determina ACN 164179/2025: IS-1..4 + regime essenziale/importante (Allegati 3/4). Fase 3 - Post-Incident Review (5-Whys) + metriche TTD/TTC/TTR + timestamp di fase. Fase 4 - Mapping NIST CSF 2.0 (43 controlli) reference-only. Fonti certe: registry config/nis2_sources.php + grounding AI (vieta riferimenti inventati) + citazioni help.js + ingest PDF normativi nella KB RAG (scripts/ingest-nis2-sources.php). Migrazioni 020/021/022 (additive idempotenti). Fix VectorService IP Qdrant (drift .5->.3). Analisi concorrenza Evix (docs/EVIX_ANALISI_CONCORRENZA.html, gap-driven). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a7a21faa82
commit
5c545ea3d0
+107
-1
@@ -466,6 +466,7 @@
|
||||
<th>Tipo</th>
|
||||
<th>Categoria</th>
|
||||
<th>Criticita'</th>
|
||||
<th>Rilevanza NIS2</th>
|
||||
<th>Owner</th>
|
||||
<th>Stato</th>
|
||||
<th>Azioni</th>
|
||||
@@ -482,9 +483,13 @@
|
||||
<td>${typeLabels[asset.asset_type] || asset.asset_type || '-'}</td>
|
||||
<td>${escapeHtml(asset.category || '-')}</td>
|
||||
<td><span class="criticality-badge ${crit}">${criticalityLabels[crit] || crit}</span></td>
|
||||
<td>${relevanceBadge(asset)}</td>
|
||||
<td>${escapeHtml(asset.owner_name || '-')}</td>
|
||||
<td><span class="status-badge ${st}">${statusLabels[st] || st}</span></td>
|
||||
<td>
|
||||
<td style="white-space:nowrap;">
|
||||
<button class="btn-icon" onclick="event.stopPropagation(); showScoringModal(${asset.id})" title="Valuta rilevanza NIS2">
|
||||
<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M3 3a1 1 0 011 1v12h12a1 1 0 110 2H3a1 1 0 01-1-1V4a1 1 0 011-1zm14.707 4.707a1 1 0 00-1.414-1.414L12 10.586 9.707 8.293a1 1 0 00-1.414 0L4.586 12 6 13.414l2.293-2.293L10.586 13l5.121-5.293z" clip-rule="evenodd"/></svg>
|
||||
</button>
|
||||
<button class="btn-icon" onclick="event.stopPropagation(); showEditAssetModal(${asset.id})" title="Modifica">
|
||||
<svg viewBox="0 0 20 20" fill="currentColor"><path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z"/></svg>
|
||||
</button>
|
||||
@@ -496,6 +501,106 @@
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
// ── Rilevanza NIS2 (GV.OC-04) ───────────────────────────
|
||||
const relevanceClassColors = {
|
||||
critico: '#dc2626', alto: '#ea580c', medio: '#ca8a04',
|
||||
basso: '#2563eb', trascurabile: '#6b7280'
|
||||
};
|
||||
|
||||
function relevanceBadge(asset) {
|
||||
if (asset.relevance_score === null || asset.relevance_score === undefined || asset.relevance_score === '') {
|
||||
return '<span style="color:var(--gray-400); font-size:0.8rem;">Da valutare</span>';
|
||||
}
|
||||
const cls = asset.relevance_class || 'trascurabile';
|
||||
const color = relevanceClassColors[cls] || '#6b7280';
|
||||
const rel = Number(asset.is_nis2_relevant) ? ' ✓' : '';
|
||||
return `<span style="display:inline-flex;align-items:center;gap:6px;font-size:0.8rem;font-weight:600;color:${color};">
|
||||
<span style="display:inline-block;min-width:30px;text-align:center;padding:2px 6px;border-radius:6px;background:${color}1a;">${asset.relevance_score}</span>
|
||||
${cls.charAt(0).toUpperCase() + cls.slice(1)}${rel}</span>`;
|
||||
}
|
||||
|
||||
let _scoringGrid = null;
|
||||
async function loadScoringGrid() {
|
||||
if (_scoringGrid) return _scoringGrid;
|
||||
const r = await api.getScoringGrid();
|
||||
if (r.success) _scoringGrid = r.data;
|
||||
return _scoringGrid;
|
||||
}
|
||||
|
||||
async function showScoringModal(id) {
|
||||
try {
|
||||
const [assetRes, grid] = await Promise.all([api.getAsset(id), loadScoringGrid()]);
|
||||
if (!assetRes.success || !grid) { showNotification('Errore caricamento dati.', 'error'); return; }
|
||||
const a = assetRes.data;
|
||||
let prev = a.relevance_criteria;
|
||||
if (typeof prev === 'string') { try { prev = JSON.parse(prev); } catch (e) { prev = null; } }
|
||||
|
||||
let body = `<p style="font-size:0.85rem;color:var(--gray-600);margin-bottom:1rem;">
|
||||
Metodologia di scoring rilevanza NIS2 (requisito <strong>GV.OC-04</strong>). Soglia rilevanza: <strong>≥${grid.threshold} punti</strong>.
|
||||
Il punteggio aggiorna automaticamente anche la criticita dell'asset.</p>`;
|
||||
|
||||
for (const [key, def] of Object.entries(grid.grid)) {
|
||||
const sel = prev && prev[key] ? prev[key].value : '';
|
||||
let opts = `<option value="">— seleziona —</option>`;
|
||||
for (const [ov, od] of Object.entries(def.options)) {
|
||||
opts += `<option value="${ov}" data-pts="${od.points}" ${ov === sel ? 'selected' : ''}>${od.label} (${od.points})</option>`;
|
||||
}
|
||||
body += `<div class="form-group" style="margin-bottom:0.75rem;">
|
||||
<label class="form-label" style="font-weight:600;">${def.label} <span style="color:var(--gray-400);font-weight:400;">(max ${def.max})</span></label>
|
||||
<div style="font-size:0.78rem;color:var(--gray-500);margin-bottom:4px;">${def.help}</div>
|
||||
<select class="form-select score-criterion" data-key="${key}" onchange="updateScorePreview()">${opts}</select>
|
||||
</div>`;
|
||||
}
|
||||
body += `<div id="score-preview" style="margin-top:1rem;padding:0.9rem;border-radius:10px;background:var(--gray-50);font-weight:600;text-align:center;">
|
||||
Totale: <span id="score-total">0</span>/100 — <span id="score-class">—</span></div>`;
|
||||
|
||||
showModal(`Valuta Rilevanza NIS2 — ${escapeHtml(a.name)}`, body, {
|
||||
size: 'lg',
|
||||
footer: `<button class="btn btn-secondary" onclick="closeModal()">Annulla</button>
|
||||
<button class="btn btn-primary" onclick="submitScoring(${id})">Calcola e Salva</button>`
|
||||
});
|
||||
updateScorePreview();
|
||||
} catch (e) {
|
||||
showNotification('Errore nell\'apertura della valutazione.', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function updateScorePreview() {
|
||||
let total = 0, complete = true;
|
||||
document.querySelectorAll('.score-criterion').forEach(s => {
|
||||
if (!s.value) { complete = false; return; }
|
||||
total += parseInt(s.selectedOptions[0].dataset.pts || '0', 10);
|
||||
});
|
||||
let cls = total >= 80 ? 'critico' : total >= 60 ? 'alto' : total >= 40 ? 'medio' : total >= 20 ? 'basso' : 'trascurabile';
|
||||
const color = relevanceClassColors[cls];
|
||||
document.getElementById('score-total').textContent = total;
|
||||
const clsEl = document.getElementById('score-class');
|
||||
clsEl.textContent = complete ? `${cls.charAt(0).toUpperCase() + cls.slice(1)}${total >= 40 ? ' — Rilevante NIS2 ✓' : ''}` : '(completa tutti i criteri)';
|
||||
clsEl.style.color = complete ? color : 'var(--gray-400)';
|
||||
}
|
||||
|
||||
async function submitScoring(id) {
|
||||
const criteria = {};
|
||||
let complete = true;
|
||||
document.querySelectorAll('.score-criterion').forEach(s => {
|
||||
if (!s.value) complete = false;
|
||||
criteria[s.dataset.key] = s.value;
|
||||
});
|
||||
if (!complete) { showNotification('Compila tutti i 6 criteri.', 'warning'); return; }
|
||||
try {
|
||||
const r = await api.scoreAsset(id, criteria);
|
||||
if (r.success) {
|
||||
showNotification(`Rilevanza calcolata: ${r.data.score}/100 (${r.data.class}).`, 'success');
|
||||
closeModal();
|
||||
loadAssets();
|
||||
} else {
|
||||
showNotification(r.message || 'Errore nel calcolo.', 'error');
|
||||
}
|
||||
} catch (e) {
|
||||
showNotification('Errore di connessione.', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// ── Asset Detail View ───────────────────────────────────
|
||||
async function showAssetDetail(id) {
|
||||
try {
|
||||
@@ -581,6 +686,7 @@
|
||||
size: 'lg',
|
||||
footer: `
|
||||
<button class="btn btn-secondary" onclick="closeModal()">Chiudi</button>
|
||||
<button class="btn btn-secondary" onclick="closeModal(); showScoringModal(${a.id})">Valuta Rilevanza NIS2</button>
|
||||
<button class="btn btn-primary" onclick="closeModal(); showEditAssetModal(${a.id})">Modifica</button>
|
||||
`
|
||||
});
|
||||
|
||||
+9
-1
@@ -230,6 +230,9 @@ $actionMap = [
|
||||
'POST:{id}/notification' => 'sendNotification',
|
||||
'POST:{id}/finalReport' => 'sendFinalReport',
|
||||
'POST:{id}/aiClassify' => 'aiClassify',
|
||||
'GET:{id}/metrics' => 'metrics',
|
||||
'GET:{id}/pir' => 'getPir',
|
||||
'POST:{id}/pir' => 'savePir',
|
||||
],
|
||||
|
||||
// ── PolicyController ────────────────────────────
|
||||
@@ -269,10 +272,13 @@ $actionMap = [
|
||||
'assets' => [
|
||||
'GET:list' => 'list',
|
||||
'POST:create' => 'create',
|
||||
'GET:scoringGrid' => 'scoringGrid',
|
||||
'GET:relevantSystems' => 'relevantSystems',
|
||||
'GET:dependencyMap' => 'dependencyMap',
|
||||
'GET:{id}' => 'get',
|
||||
'PUT:{id}' => 'update',
|
||||
'DELETE:{id}' => 'delete',
|
||||
'GET:dependencyMap' => 'dependencyMap',
|
||||
'POST:{id}/score' => 'score',
|
||||
],
|
||||
|
||||
// ── AuditController ─────────────────────────────
|
||||
@@ -284,7 +290,9 @@ $actionMap = [
|
||||
'GET:report' => 'generateReport',
|
||||
'GET:logs' => 'getAuditLogs',
|
||||
'GET:iso27001Mapping' => 'getIsoMapping',
|
||||
'GET:nistCsfMapping' => 'getNistCsfMapping',
|
||||
'GET:executiveReport' => 'executiveReport',
|
||||
'GET:relevantSystemsRegister' => 'relevantSystemsRegister',
|
||||
'GET:export' => 'export',
|
||||
'GET:chainVerify' => 'chainVerify',
|
||||
'GET:exportCertified' => 'exportCertified',
|
||||
|
||||
@@ -212,6 +212,10 @@ class NIS2API {
|
||||
sendEarlyWarning(id) { return this.post(`/incidents/${id}/early-warning`, {}); }
|
||||
sendNotification(id) { return this.post(`/incidents/${id}/notification`, {}); }
|
||||
sendFinalReport(id) { return this.post(`/incidents/${id}/final-report`, {}); }
|
||||
aiClassifyIncident(id) { return this.post(`/incidents/${id}/aiClassify`, {}); }
|
||||
getIncidentMetrics(id) { return this.get(`/incidents/${id}/metrics`); }
|
||||
getIncidentPir(id) { return this.get(`/incidents/${id}/pir`); }
|
||||
saveIncidentPir(id, data) { return this.post(`/incidents/${id}/pir`, data); }
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// Policies
|
||||
@@ -251,6 +255,11 @@ class NIS2API {
|
||||
createAsset(data) { return this.post('/assets/create', data); }
|
||||
getAsset(id) { return this.get(`/assets/${id}`); }
|
||||
updateAsset(id, data) { return this.put(`/assets/${id}`, data); }
|
||||
deleteAsset(id) { return this.delete(`/assets/${id}`); }
|
||||
// NIS2 relevance scoring (GV.OC-04)
|
||||
getScoringGrid() { return this.get('/assets/scoringGrid'); }
|
||||
scoreAsset(id, criteria) { return this.post(`/assets/${id}/score`, { criteria }); }
|
||||
listRelevantSystems() { return this.get('/assets/relevantSystems'); }
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// Audit
|
||||
|
||||
+21
-9
@@ -207,11 +207,13 @@ const HelpSystem = (function () {
|
||||
}
|
||||
],
|
||||
references: [
|
||||
'Art. 23.1 - Obbligo di notifica degli incidenti significativi',
|
||||
'Art. 23.4 (a) - Early warning entro 24 ore',
|
||||
'Art. 23.4 (b) - Notifica entro 72 ore',
|
||||
'Art. 23.4 (d) - Relazione finale entro un mese',
|
||||
'Art. 23.3 - Definizione di incidente significativo'
|
||||
'Direttiva (UE) 2022/2555 - Art. 23.1 - Obbligo di notifica degli incidenti significativi',
|
||||
'Direttiva (UE) 2022/2555 - Art. 23.4 (a) - Early warning entro 24 ore',
|
||||
'Direttiva (UE) 2022/2555 - Art. 23.4 (b) - Notifica entro 72 ore',
|
||||
'Direttiva (UE) 2022/2555 - Art. 23.4 (d) - Relazione finale entro un mese',
|
||||
'D.Lgs. 4 settembre 2024, n. 138 - Art. 23 - Notifica degli incidenti (recepimento NIS2)',
|
||||
'Determinazione ACN n. 164179 del 14/04/2025 - Classificazione incidenti significativi (Allegato 3 soggetti essenziali, Allegato 4 soggetti importanti) e tipologie IS-1/IS-2/IS-3/IS-4',
|
||||
'Determinazione ACN n. 333017/2025 - Piattaforma digitale ACN per le notifiche'
|
||||
]
|
||||
},
|
||||
|
||||
@@ -381,6 +383,15 @@ const HelpSystem = (function () {
|
||||
'Il livello di criticita\' influenza la valutazione dei rischi associati.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Rilevanza NIS2 (scoring 0-100)',
|
||||
items: [
|
||||
'Il pulsante <strong>Valuta Rilevanza NIS2</strong> applica una metodologia di scoring documentata a 6 criteri pesati: Criticita Operativa (0-25), Impatto Interruzione (0-25), Dati Trattati (0-20), Dipendenze (0-15), Esposizione (0-10), Obblighi Normativi (0-5).',
|
||||
'Un sistema e considerato <strong>rilevante NIS2 quando il punteggio ≥ 40</strong>. Classi: ≥80 Critico, 60-79 Alto, 40-59 Medio, 20-39 Basso, <20 Trascurabile.',
|
||||
'Il punteggio aggiorna automaticamente anche la criticita dell\'asset e alimenta il registro formale dei <strong>Sistemi Rilevanti</strong>.',
|
||||
'La metodologia supporta il requisito di censimento e classificazione dei sistemi informativi e di rete rilevanti, da approvare a livello di Direzione.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Mappa delle Dipendenze',
|
||||
items: [
|
||||
@@ -399,10 +410,11 @@ const HelpSystem = (function () {
|
||||
}
|
||||
],
|
||||
references: [
|
||||
'Art. 21.2 (i) - Sicurezza delle risorse umane, politiche di controllo dell\'accesso e gestione degli attivi',
|
||||
'Art. 21.2 (a) - Politiche di analisi dei rischi e di sicurezza dei sistemi informatici',
|
||||
'Art. 21.2 (c) - Continuita\' operativa, gestione dei backup e ripristino in caso di disastro',
|
||||
'Considerando 79 - Adeguatezza delle misure rispetto ai rischi per le reti e i sistemi informativi'
|
||||
'Direttiva (UE) 2022/2555 - Art. 21.2 (i) - Sicurezza delle risorse umane, controllo degli accessi e gestione degli attivi',
|
||||
'Direttiva (UE) 2022/2555 - Art. 21.2 (a) - Politiche di analisi dei rischi e di sicurezza dei sistemi informatici',
|
||||
'Direttiva (UE) 2022/2555 - Art. 21.2 (c) - Continuita\' operativa, gestione dei backup e ripristino',
|
||||
'D.Lgs. 4 settembre 2024, n. 138 - Art. 24 - Obblighi in materia di misure di gestione del rischio',
|
||||
'Identificazione e classificazione dei sistemi rilevanti - metodologia di scoring 0-100 approvata dalla Direzione'
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":"1.6.1","build":"20260529g","date":"2026-05-29T14:55:00+02:00","changelog":"Doc: aggiornati help.js (sezione Impostazioni con Sessioni/Preferenze/Branding/Reset/Tenant), i18n.js (chiavi IT/EN per Fasi 2-5), product knowledge AI AgileHub (card NIS2 id=914)"}
|
||||
{"version":"1.7.0","build":"20260529h","date":"2026-05-29T16:30:00+02:00","changelog":"FEAT integrazione analisi docs/nis2: (1) Asset Relevance Scoring NIS2 0-100 a 6 criteri (GV.OC-04) + registro formale stampabile; (2) Tassonomia incidenti Determina ACN 164179/2025 (IS-1..4, regime essenziale/importante Allegati 3-4); (3) Post-Incident Review strutturato 5-Whys + metriche TTD/TTC/TTR; (4) Layer mapping NIST CSF 2.0 (43 controlli); (5) Fonti normative certe: registry citabile + grounding AI + citazioni help + ingest PDF normativi nella KB RAG."}
|
||||
|
||||
Reference in New Issue
Block a user