[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:
DevEnv nis2-agile 2026-05-29 17:15:13 +02:00
parent a7a21faa82
commit 5c545ea3d0
41 changed files with 21078 additions and 24 deletions

View File

@ -911,3 +911,28 @@ Standard cross-suite NAVIGAI per piattaforma multitenant esplicita di AgileHub.
**Cosa impatta questo prodotto**: se in futuro questo prodotto chiamerà API multitenant-aware di AgileHub (es. /api/marketing, /api/rag, /api/ai/personas), deve passare JWT con tenant_id + tenant_slug claims oppure header `X-Tenant-Slug`. Vedi §6 contracts shared lib `@agile/tenant-auth` per pattern integrazione (Node + Python). **Cosa impatta questo prodotto**: se in futuro questo prodotto chiamerà API multitenant-aware di AgileHub (es. /api/marketing, /api/rag, /api/ai/personas), deve passare JWT con tenant_id + tenant_slug claims oppure header `X-Tenant-Slug`. Vedi §6 contracts shared lib `@agile/tenant-auth` per pattern integrazione (Node + Python).
Status adoption: acknowledged 2026-05-17. Status adoption: acknowledged 2026-05-17.
---
## Integrazione analisi `docs/nis2/` — v1.7.0 (2026-05-29)
> Integrati i mockup + testi normativi PDF in `docs/nis2/`. Dettaglio e comandi deploy: `docs/nis2/INTEGRAZIONE_COMPLETATA.md`. **Migrazioni 020-022 e ingest KB DA ESEGUIRE su Hetzner** (host MySQL, non `docker exec nis2-db`).
### Nuove migrazioni (additive, idempotenti)
- `020_asset_relevance.sql` — assets += `relevance_score`, `relevance_criteria` JSON, `relevance_class`, `is_nis2_relevant`, `relevance_assessed_at/by`
- `021_incident_nis2_taxonomy.sql` — incidents += `nis2_incident_type` ENUM(IS-1..IS-4), `entity_obligation` ENUM(essential/important)
- `022_incident_metrics_pir.sql` — incidents += `triaged_at`/`contained_at`/`eradicated_at`/`recovered_at`; nuova tabella `incident_pir`
### Nuovi file
- `application/config/nis2_sources.php`**registry canonico FONTI NORMATIVE CERTE** (single source of truth AI + help)
- `application/services/AssetScoringService.php` — scoring rilevanza NIS2 0-100 (6 criteri, GV.OC-04)
- `scripts/ingest-nis2-sources.php` — ingest PDF normativi nella KB Qdrant `nis2_kb` scope SYSTEM
### Nuovi endpoint
- Assets: `GET /api/assets/scoringGrid`, `POST /api/assets/{id}/score`, `GET /api/assets/relevantSystems`
- Incidents: `GET /api/incidents/{id}/metrics`, `GET /api/incidents/{id}/pir`, `POST /api/incidents/{id}/pir`
- Audit: `GET /api/audit/nistCsfMapping`, `GET /api/audit/relevantSystemsRegister` (registro GV.OC-04 stampabile)
### REGOLA: Fonti certe (AI + help)
Ogni affermazione normativa di AI e help **deve citare** una fonte di `application/config/nis2_sources.php`.
`AIService::authoritativeSourcesBlock()` è iniettato nei system prompt e **vieta riferimenti inventati**.

View File

@ -0,0 +1,75 @@
<?php
/**
* NIS2 Agile - Registro Fonti Normative Certe
* ----------------------------------------------------------------------------
* SINGLE SOURCE OF TRUTH per le fonti normative autoritative citabili.
*
* Principio (richiesta utente 2026-05-29): ogni scelta/risposta della
* piattaforma sia nell'AI sia nell'help DEVE riferirsi a fonti certe e
* citarle esplicitamente. Vietato inventare riferimenti normativi.
*
* I PDF originali risiedono in docs/nis2/ (repo) e sono ingeriti nella
* Knowledge Base (collection Qdrant nis2_kb, scope SYSTEM) per il grounding RAG.
*
* Usato da:
* - AIService (iniezione nei system prompt: "cita queste fonti")
* - public/js/help.js (riferimenti normativi nell'help contestuale, via /api/... o statico)
* - scripts/ingest-nis2-sources.php (ingest PDF -> KB)
*/
return [
'nis2_directive' => [
'key' => 'nis2_directive',
'short' => 'Direttiva NIS2',
'citation' => 'Direttiva (UE) 2022/2555 (NIS2)',
'full' => 'Direttiva (UE) 2022/2555 del Parlamento europeo e del Consiglio del 14 dicembre 2022 relativa a misure per un livello comune elevato di cibersicurezza nell\'Unione (NIS2)',
'file' => 'docs/nis2/Dir2022_2555_UE_NIS2_ITA.pdf',
'authority' => 'Parlamento europeo e Consiglio UE',
'url' => 'https://eur-lex.europa.eu/legal-content/IT/TXT/?uri=CELEX:32022L2555',
],
'cer_directive' => [
'key' => 'cer_directive',
'short' => 'Direttiva CER',
'citation' => 'Direttiva (UE) 2022/2557 (CER)',
'full' => 'Direttiva (UE) 2022/2557 del Parlamento europeo e del Consiglio del 14 dicembre 2022 relativa alla resilienza dei soggetti critici (Critical Entities Resilience)',
'file' => 'docs/nis2/Dir2022_2557_UE_ITA.pdf',
'authority' => 'Parlamento europeo e Consiglio UE',
'url' => 'https://eur-lex.europa.eu/legal-content/IT/TXT/?uri=CELEX:32022L2557',
],
'dlgs_138_2024' => [
'key' => 'dlgs_138_2024',
'short' => 'D.Lgs. 138/2024',
'citation' => 'D.Lgs. 4 settembre 2024, n. 138',
'full' => 'Decreto legislativo 4 settembre 2024, n. 138 - Recepimento della direttiva (UE) 2022/2555 (decreto NIS)',
'file' => null,
'authority' => 'Repubblica Italiana',
'url' => 'https://www.gazzettaufficiale.it/eli/id/2024/09/01/24G00155/sg',
],
'determina_164179_2025' => [
'key' => 'determina_164179_2025',
'short' => 'Determina ACN 164179/2025',
'citation' => 'Determinazione ACN n. 164179 del 14 aprile 2025',
'full' => 'Determinazione del Direttore generale dell\'Agenzia per la Cybersicurezza Nazionale n. 164179 del 14 aprile 2025, in attuazione del D.Lgs. 138/2024, che stabilisce modalita e specifiche di base per gli obblighi di cui agli articoli 23, 24, 25, 29 e 32 (incluse classificazione e notifica degli incidenti significativi, Allegati 3 e 4).',
'file' => 'docs/nis2/Determina164179_apr2025.pdf',
'authority' => 'Agenzia per la Cybersicurezza Nazionale (ACN)',
'url' => 'https://www.acn.gov.it/',
],
'determina_333017_2025' => [
'key' => 'determina_333017_2025',
'short' => 'Determina ACN 333017/2025',
'citation' => 'Determinazione ACN n. 333017 del settembre 2025',
'full' => 'Determinazione del Direttore generale ACN n. 333017 (settembre 2025): termini, modalita e procedimenti di utilizzo e accesso alla piattaforma digitale ACN, ulteriori informazioni che i soggetti devono fornire e designazione dei rappresentanti NIS sul territorio nazionale.',
'file' => 'docs/nis2/Determina333017_sett2025.pdf',
'authority' => 'Agenzia per la Cybersicurezza Nazionale (ACN)',
'url' => 'https://www.acn.gov.it/',
],
'ambiti_nis2' => [
'key' => 'ambiti_nis2',
'short' => 'Ambiti NIS2 (Allegati I/II)',
'citation' => 'Allegati I e II - Settori NIS2',
'full' => 'Ambiti di applicazione NIS2 - Allegato I (Settori ad alta criticita: energia, trasporti, bancario, infrastrutture dei mercati finanziari, sanitario, acqua potabile, acque reflue, infrastrutture digitali, gestione servizi TIC B2B, PA, spazio) e Allegato II (Altri settori critici).',
'file' => 'docs/nis2/AmbitiNIS2_ITA.pdf',
'authority' => 'ACN / Allegati al D.Lgs. 138/2024',
'url' => 'https://www.acn.gov.it/',
],
];

View File

@ -6,6 +6,7 @@
*/ */
require_once __DIR__ . '/BaseController.php'; require_once __DIR__ . '/BaseController.php';
require_once __DIR__ . '/../services/AssetScoringService.php';
class AssetController extends BaseController class AssetController extends BaseController
{ {
@ -29,6 +30,10 @@ class AssetController extends BaseController
$where .= ' AND status = ?'; $where .= ' AND status = ?';
$params[] = $this->getParam('status'); $params[] = $this->getParam('status');
} }
if ($this->hasParam('nis2_relevant')) {
$where .= ' AND is_nis2_relevant = ?';
$params[] = $this->getParam('nis2_relevant') ? 1 : 0;
}
$total = Database::count('assets', $where, $params); $total = Database::count('assets', $where, $params);
$assets = Database::fetchAll( $assets = Database::fetchAll(
@ -157,4 +162,116 @@ class AssetController extends BaseController
$this->jsonSuccess(['nodes' => $nodes, 'edges' => $edges]); $this->jsonSuccess(['nodes' => $nodes, 'edges' => $edges]);
} }
/**
* GET /api/assets/scoringGrid
* Ritorna la griglia ufficiale di valutazione rilevanza NIS2 (GV.OC-04)
* per costruire la UI di scoring lato client.
*/
public function scoringGrid(): void
{
$this->requireOrgAccess();
$this->jsonSuccess([
'grid' => AssetScoringService::GRID,
'threshold' => AssetScoringService::RELEVANCE_THRESHOLD,
'classes' => [
['key' => 'critico', 'min' => 80, 'max' => 100, 'label' => 'Critico - Priorita Massima'],
['key' => 'alto', 'min' => 60, 'max' => 79, 'label' => 'Alto - Priorita Alta'],
['key' => 'medio', 'min' => 40, 'max' => 59, 'label' => 'Medio - Rilevante'],
['key' => 'basso', 'min' => 20, 'max' => 39, 'label' => 'Basso - Monitoraggio'],
['key' => 'trascurabile', 'min' => 0, 'max' => 19, 'label' => 'Trascurabile'],
],
]);
}
/**
* POST /api/assets/{id}/score
* Calcola e salva la rilevanza NIS2 dell'asset a partire dalle selezioni
* sui 6 criteri. Body: { criteria: { c1_operational_criticality: 'critical', ... } }
*/
public function score(int $id): void
{
$this->requireOrgRole(['org_admin', 'compliance_manager']);
$asset = Database::fetchOne(
'SELECT id FROM assets WHERE id = ? AND organization_id = ?',
[$id, $this->getCurrentOrgId()]
);
if (!$asset) {
$this->jsonError('Asset non trovato', 404, 'ASSET_NOT_FOUND');
}
$criteria = $this->getParam('criteria');
if (!is_array($criteria)) {
$this->jsonError('Campo "criteria" mancante o non valido', 422, 'INVALID_CRITERIA');
}
try {
$result = AssetScoringService::calculate($criteria);
} catch (InvalidArgumentException $e) {
$this->jsonError($e->getMessage(), 422, 'INVALID_CRITERIA');
return;
}
Database::update('assets', [
'relevance_score' => $result['score'],
'relevance_criteria' => json_encode($result['breakdown'], JSON_UNESCAPED_UNICODE),
'relevance_class' => $result['class'],
'is_nis2_relevant' => $result['is_relevant'] ? 1 : 0,
'criticality' => $result['criticality'],
'relevance_assessed_at' => date('Y-m-d H:i:s'),
'relevance_assessed_by' => $this->getCurrentUserId(),
], 'id = ? AND organization_id = ?', [$id, $this->getCurrentOrgId()]);
$this->logAudit('asset_scored', 'asset', $id, [
'score' => $result['score'],
'class' => $result['class'],
]);
$this->jsonSuccess([
'score' => $result['score'],
'class' => $result['class'],
'is_nis2_relevant' => $result['is_relevant'],
'breakdown' => $result['breakdown'],
'required_measures'=> AssetScoringService::requiredMeasures($result['class']),
], 'Rilevanza NIS2 calcolata');
}
/**
* GET /api/assets/relevantSystems
* Elenco dei sistemi classificati rilevanti NIS2 (score >= 40), ordinati
* per punteggio. Alimenta il registro formale "Sistemi Rilevanti" (GV.OC-04).
*/
public function relevantSystems(): void
{
$this->requireOrgAccess();
$rows = Database::fetchAll(
"SELECT a.id, a.name, a.asset_type, a.category, a.ip_address, a.location,
a.relevance_score, a.relevance_class, a.relevance_criteria,
a.relevance_assessed_at, u.full_name AS owner_name
FROM assets a
LEFT JOIN users u ON u.id = a.owner_user_id
WHERE a.organization_id = ? AND a.is_nis2_relevant = 1
ORDER BY a.relevance_score DESC, a.name",
[$this->getCurrentOrgId()]
);
$stats = ['critico' => 0, 'alto' => 0, 'medio' => 0];
foreach ($rows as &$r) {
$r['relevance_criteria'] = json_decode($r['relevance_criteria'] ?? 'null', true);
$r['required_measures'] = AssetScoringService::requiredMeasures($r['relevance_class'] ?? '');
if (isset($stats[$r['relevance_class']])) {
$stats[$r['relevance_class']]++;
}
}
unset($r);
$this->jsonSuccess([
'systems' => $rows,
'count' => count($rows),
'by_class' => $stats,
'threshold' => AssetScoringService::RELEVANCE_THRESHOLD,
]);
}
} }

View File

@ -195,6 +195,83 @@ class AuditController extends BaseController
$this->jsonSuccess($mapping); $this->jsonSuccess($mapping);
} }
/**
* GET /api/audit/nistCsfMapping
* Layer di mapping NIST CSF 2.0 (43 controlli) -> NIS2 Art.21 / D.Lgs.138/2024 -> modulo piattaforma.
* Reference-only (nessuna persistenza): arricchisce l'assessment Art.21 con i codici controllo
* NIST CSF 2.0 usati come standard de-facto. Fonte mapping: NIST CSF 2.0 + Direttiva (UE) 2022/2555.
*/
public function getNistCsfMapping(): void
{
$this->requireOrgAccess();
// [code, function, nis2, module]
$rows = [
// GOVERN
['GV.OC-04', 'Govern', '21.1', 'Asset - Sistemi rilevanti (GV.OC-04)'],
['GV.RM-03', 'Govern', '21.2.a', 'Risk Management'],
['GV.RR-02', 'Govern', '20', 'Organizzazione - Ruoli e responsabilita'],
['GV.RR-04', 'Govern', '20', 'Organizzazione - Risorse cybersecurity'],
['GV.PO-01', 'Govern', '21.2.a', 'Policy - Politica di sicurezza'],
['GV.PO-02', 'Govern', '21.2.a', 'Policy - Revisione politiche'],
['GV.SC-01', 'Govern', '21.2.d', 'Supply Chain - Strategia'],
['GV.SC-02', 'Govern', '21.2.d', 'Supply Chain - Ruoli fornitori'],
['GV.SC-04', 'Govern', '21.2.d', 'Supply Chain - Valutazione fornitori'],
['GV.SC-05', 'Govern', '21.2.d', 'Supply Chain - Requisiti contrattuali'],
['GV.SC-07', 'Govern', '21.2.d', 'Supply Chain - Monitoraggio rischio fornitori'],
// IDENTIFY
['ID.AM-01', 'Identify', '21.2.i', 'Asset - Inventario hardware'],
['ID.AM-02', 'Identify', '21.2.i', 'Asset - Inventario software'],
['ID.AM-03', 'Identify', '21.2.i', 'Asset - Diagrammi flussi/rete (essenziali)'],
['ID.AM-04', 'Identify', '21.2.i', 'Asset - Catalogo servizi'],
['ID.RA-01', 'Identify', '21.2.a', 'Risk Management - Vulnerabilita'],
['ID.RA-05', 'Identify', '21.2.a', 'Risk Management - Valutazione rischio'],
['ID.RA-06', 'Identify', '21.2.a', 'Risk Management - Trattamento rischio'],
['ID.RA-08', 'Identify', '21.2.e', 'Risk Management - Gestione vulnerabilita/disclosure'],
['ID.IM-01', 'Identify', '21.2.f', 'Audit - Miglioramento da valutazioni'],
['ID.IM-04', 'Identify', '21.2.c', 'Incidenti - Piani BC/DR e test'],
// PROTECT
['PR.AA-01', 'Protect', '21.2.i', 'Asset/Access - Gestione identita'],
['PR.AA-03', 'Protect', '21.2.i', 'Access - Autenticazione'],
['PR.AA-05', 'Protect', '21.2.i', 'Access - Privilegi e accessi'],
['PR.AA-06', 'Protect', '21.2.i', 'Access - Accesso fisico'],
['PR.AT-01', 'Protect', '21.2.g', 'Training - Awareness'],
['PR.AT-02', 'Protect', '21.2.g', 'Training - Ruoli privilegiati'],
['PR.DS-01', 'Protect', '21.2.h', 'Policy - Protezione dati a riposo'],
['PR.DS-02', 'Protect', '21.2.h', 'Policy - Protezione dati in transito'],
['PR.DS-11', 'Protect', '21.2.c', 'Incidenti - Backup'],
['PR.PS-01', 'Protect', '21.2.e', 'Policy - Configurazione sicura'],
['PR.PS-02', 'Protect', '21.2.e', 'Asset - Gestione software'],
['PR.PS-03', 'Protect', '21.2.e', 'Asset - Gestione hardware'],
['PR.PS-04', 'Protect', '21.2.b', 'Audit - Log generation'],
['PR.PS-06', 'Protect', '21.2.e', 'Policy - Secure development lifecycle'],
['PR.IR-01', 'Protect', '21.2.i', 'Asset - Protezione reti'],
['PR.IR-03', 'Protect', '21.2.c', 'Incidenti - Resilienza/ridondanza'],
// DETECT
['DE.CM-01', 'Detect', '21.2.b', 'Incidenti - Monitoraggio reti'],
['DE.CM-09', 'Detect', '21.2.b', 'Incidenti - Monitoraggio asset/sistemi'],
// RESPOND / RECOVER
['RS.MA-01', 'Respond', '21.2.b / 23', 'Incidenti - Gestione incidenti'],
['RS.CO-02', 'Respond', '23', 'Incidenti - Notifica CSIRT'],
['RC.RP-01', 'Recover', '21.2.c', 'Incidenti - Piano di ripristino'],
['RC.CO-03', 'Recover', '21.2.c', 'Incidenti - Post-Incident Review'],
];
$mapping = array_map(fn($r) => [
'csf_code' => $r[0],
'function' => $r[1],
'nis2_art' => $r[2],
'module' => $r[3],
], $rows);
$this->jsonSuccess([
'mapping' => $mapping,
'count' => count($mapping),
'functions' => ['Govern', 'Identify', 'Protect', 'Detect', 'Respond', 'Recover'],
'source' => 'NIST Cybersecurity Framework 2.0 + Direttiva (UE) 2022/2555 (NIS2) Art.20-21-23 / D.Lgs. 138/2024',
]);
}
/** /**
* GET /api/audit/executive-report * GET /api/audit/executive-report
* Genera report esecutivo HTML (stampabile come PDF) * Genera report esecutivo HTML (stampabile come PDF)
@ -211,6 +288,22 @@ class AuditController extends BaseController
exit; exit;
} }
/**
* GET /api/audit/relevantSystemsRegister
* Registro formale "Sistemi Rilevanti NIS2" (GV.OC-04), HTML stampabile.
*/
public function relevantSystemsRegister(): void
{
$this->requireOrgRole(['org_admin', 'compliance_manager', 'board_member', 'auditor']);
$reportService = new ReportService();
$html = $reportService->generateRelevantSystemsRegister($this->getCurrentOrgId());
header('Content-Type: text/html; charset=utf-8');
echo $html;
exit;
}
/** /**
* GET /api/audit/export/{type} * GET /api/audit/export/{type}
* Esporta dati in CSV * Esporta dati in CSV

View File

@ -59,6 +59,17 @@ class IncidentController extends BaseController
$detectedAt = $this->getParam('detected_at'); $detectedAt = $this->getParam('detected_at');
$isSignificant = (bool) $this->getParam('is_significant', false); $isSignificant = (bool) $this->getParam('is_significant', false);
// Regime obblighi NIS2 (Determina ACN 164179/2025): Allegato 3 essenziali / Allegato 4 importanti.
$org = Database::fetchOne('SELECT entity_type FROM organizations WHERE id = ?', [$this->getCurrentOrgId()]);
$entityObligation = ($org && ($org['entity_type'] ?? '') === 'essential') ? 'essential' : 'important';
// IS-4 (incidenti ricorrenti) non si applica ai soggetti importanti.
$isType = $this->getParam('nis2_incident_type');
$validIs = $entityObligation === 'essential' ? ['IS-1','IS-2','IS-3','IS-4'] : ['IS-1','IS-2','IS-3'];
if ($isType !== null && !in_array($isType, $validIs, true)) {
$isType = null;
}
$data = [ $data = [
'organization_id' => $this->getCurrentOrgId(), 'organization_id' => $this->getCurrentOrgId(),
'incident_code' => $this->generateCode('INC'), 'incident_code' => $this->generateCode('INC'),
@ -67,6 +78,8 @@ class IncidentController extends BaseController
'classification' => $this->getParam('classification'), 'classification' => $this->getParam('classification'),
'severity' => $this->getParam('severity'), 'severity' => $this->getParam('severity'),
'is_significant' => $isSignificant ? 1 : 0, 'is_significant' => $isSignificant ? 1 : 0,
'nis2_incident_type' => $isType,
'entity_obligation' => $entityObligation,
'detected_at' => $detectedAt, 'detected_at' => $detectedAt,
'affected_services' => $this->getParam('affected_services'), 'affected_services' => $this->getParam('affected_services'),
'affected_users_count' => $this->getParam('affected_users_count'), 'affected_users_count' => $this->getParam('affected_users_count'),
@ -173,9 +186,9 @@ class IncidentController extends BaseController
$updates = []; $updates = [];
$allowedFields = [ $allowedFields = [
'title', 'description', 'classification', 'severity', 'is_significant', 'title', 'description', 'classification', 'severity', 'is_significant',
'status', 'affected_services', 'affected_users_count', 'cross_border_impact', 'nis2_incident_type', 'status', 'affected_services', 'affected_users_count',
'malicious_action', 'root_cause', 'remediation_actions', 'lessons_learned', 'cross_border_impact', 'malicious_action', 'root_cause', 'remediation_actions',
'assigned_to', 'lessons_learned', 'assigned_to',
]; ];
foreach ($allowedFields as $field) { foreach ($allowedFields as $field) {
@ -184,9 +197,24 @@ class IncidentController extends BaseController
} }
} }
// Se chiuso, registra data // Timbra automaticamente i timestamp di fase al primo ingresso nello stato
if (isset($updates['status']) && $updates['status'] === 'closed') { // (per il calcolo metriche TTD/TTC/TTR). Non sovrascrive valori gia' presenti.
$updates['closed_at'] = date('Y-m-d H:i:s'); if (isset($updates['status'])) {
$now = date('Y-m-d H:i:s');
$stamp = [
'analyzing' => 'triaged_at',
'containing' => 'contained_at',
'eradicating'=> 'eradicated_at',
'recovering' => 'recovered_at',
];
$col = $stamp[$updates['status']] ?? null;
if ($col !== null && empty($incident[$col])) {
$updates[$col] = $now;
}
if ($updates['status'] === 'closed') {
$updates['closed_at'] = $now;
if (empty($incident['recovered_at'])) $updates['recovered_at'] = $now;
}
} }
// Se diventa significativo, calcola scadenze // Se diventa significativo, calcola scadenze
@ -392,4 +420,127 @@ class IncidentController extends BaseController
$this->jsonError('Errore AI: ' . $e->getMessage(), 500, 'AI_ERROR'); $this->jsonError('Errore AI: ' . $e->getMessage(), 500, 'AI_ERROR');
} }
} }
/**
* GET /api/incidents/{id}/metrics
* Calcola TTD/TTC/TTR e downtime dai timestamp di fase (in minuti).
*/
public function metrics(int $id): void
{
$this->requireOrgAccess();
$inc = Database::fetchOne(
'SELECT detected_at, triaged_at, contained_at, eradicated_at, recovered_at, closed_at, affected_users_count
FROM incidents WHERE id = ? AND organization_id = ?',
[$id, $this->getCurrentOrgId()]
);
if (!$inc) {
$this->jsonError('Incidente non trovato', 404, 'INCIDENT_NOT_FOUND');
}
$this->jsonSuccess($this->computeMetrics($inc));
}
/** Differenza in minuti tra due datetime, null se mancante. */
private function minutesBetween(?string $from, ?string $to): ?int
{
if (empty($from) || empty($to)) return null;
$a = strtotime($from); $b = strtotime($to);
if ($a === false || $b === false) return null;
return (int) round(($b - $a) / 60);
}
private function computeMetrics(array $inc): array
{
$det = $inc['detected_at'] ?? null;
return [
'ttd_minutes' => $this->minutesBetween($det, $inc['triaged_at'] ?? null),
'ttc_minutes' => $this->minutesBetween($det, $inc['contained_at'] ?? null),
'ttr_minutes' => $this->minutesBetween($det, $inc['recovered_at'] ?? null),
'downtime_minutes' => $this->minutesBetween($det, $inc['recovered_at'] ?? $inc['closed_at'] ?? null),
'affected_users' => isset($inc['affected_users_count']) ? (int) $inc['affected_users_count'] : null,
];
}
/**
* GET /api/incidents/{id}/pir
* Ritorna la Post-Incident Review (RC.CO-03) con le metriche calcolate.
*/
public function getPir(int $id): void
{
$this->requireOrgAccess();
$inc = Database::fetchOne(
'SELECT * FROM incidents WHERE id = ? AND organization_id = ?',
[$id, $this->getCurrentOrgId()]
);
if (!$inc) {
$this->jsonError('Incidente non trovato', 404, 'INCIDENT_NOT_FOUND');
}
$pir = Database::fetchOne('SELECT * FROM incident_pir WHERE incident_id = ?', [$id]);
if ($pir && !empty($pir['improvement_actions'])) {
$pir['improvement_actions'] = json_decode($pir['improvement_actions'], true);
}
$this->jsonSuccess([
'pir' => $pir,
'metrics' => $this->computeMetrics($inc),
'reference' => 'RC.CO-03 (NIST CSF) - PIR da completare entro 2 settimane dalla chiusura per incidenti critici',
]);
}
/**
* POST /api/incidents/{id}/pir
* Crea o aggiorna la Post-Incident Review (upsert).
*/
public function savePir(int $id): void
{
$this->requireOrgRole(['org_admin', 'compliance_manager']);
$inc = Database::fetchOne(
'SELECT * FROM incidents WHERE id = ? AND organization_id = ?',
[$id, $this->getCurrentOrgId()]
);
if (!$inc) {
$this->jsonError('Incidente non trovato', 404, 'INCIDENT_NOT_FOUND');
}
$m = $this->computeMetrics($inc);
$actions = $this->getParam('improvement_actions');
$fields = [
'organization_id' => $this->getCurrentOrgId(),
'problem_statement' => $this->getParam('problem_statement'),
'why_1' => $this->getParam('why_1'), 'why_2' => $this->getParam('why_2'),
'why_3' => $this->getParam('why_3'), 'why_4' => $this->getParam('why_4'),
'why_5' => $this->getParam('why_5'),
'root_cause' => $this->getParam('root_cause'),
'ttd_minutes' => $m['ttd_minutes'],
'ttc_minutes' => $m['ttc_minutes'],
'ttr_minutes' => $m['ttr_minutes'],
'downtime_minutes' => $m['downtime_minutes'],
'affected_users' => $m['affected_users'],
'estimated_cost_eur' => $this->getParam('estimated_cost_eur'),
'notification_compliance' => $this->getParam('notification_compliance') !== null ? (int)(bool)$this->getParam('notification_compliance') : null,
'what_went_well' => $this->getParam('what_went_well'),
'what_to_improve' => $this->getParam('what_to_improve'),
'improvement_actions' => is_array($actions) ? json_encode($actions, JSON_UNESCAPED_UNICODE) : null,
'participants' => $this->getParam('participants'),
'reviewed_by' => $this->getCurrentUserId(),
'reviewed_at' => date('Y-m-d H:i:s'),
'status' => $this->getParam('status', 'draft'),
];
$existing = Database::fetchOne('SELECT id FROM incident_pir WHERE incident_id = ?', [$id]);
if ($existing) {
Database::update('incident_pir', $fields, 'incident_id = ?', [$id]);
$pirId = (int) $existing['id'];
} else {
$fields['incident_id'] = $id;
$pirId = Database::insert('incident_pir', $fields);
}
// Se la root cause e' definita, allineala anche all'incidente (campo legacy)
if (!empty($fields['root_cause'])) {
Database::update('incidents', ['root_cause' => $fields['root_cause']], 'id = ?', [$id]);
}
$this->logAudit('incident_pir_saved', 'incident', $id, ['pir_id' => $pirId, 'status' => $fields['status']]);
$this->jsonSuccess(['pir_id' => $pirId, 'metrics' => $m], 'Post-Incident Review salvata');
}
} }

View File

@ -28,6 +28,32 @@ class AIService
} }
} }
/**
* Blocco "fonti certe" da iniettare nei system prompt.
* Elenca le fonti normative autoritative e impone di citarle, vietando
* riferimenti inventati. (Richiesta utente 2026-05-29 - grounding su fonti certe.)
*/
private function authoritativeSourcesBlock(): string
{
static $sources = null;
if ($sources === null) {
$sources = @include __DIR__ . '/../config/nis2_sources.php';
if (!is_array($sources)) $sources = [];
}
if (empty($sources)) return '';
$lines = [];
foreach ($sources as $s) {
$lines[] = '- ' . $s['citation'] . ' — ' . $s['authority'];
}
return "\n## FONTI NORMATIVE CERTE (cita SEMPRE quella pertinente)\n"
. implode("\n", $lines)
. "\n\nREGOLE SULLE FONTI (vincolanti):\n"
. "1. Ogni affermazione normativa DEVE essere ancorata a una di queste fonti, citata esplicitamente (es. \"ai sensi dell'art. 23 della Direttiva (UE) 2022/2555\" o \"Determinazione ACN n. 164179/2025, Allegato 3\").\n"
. "2. NON inventare numeri di articolo, determine, allegati o date: se non sei certo, dichiaralo e invita a verificare la fonte ufficiale.\n"
. "3. Preferisci sempre il riferimento normativo italiano (D.Lgs. 138/2024 + Determine ACN) per gli obblighi operativi, e la Direttiva UE per i principi.\n";
}
/** /**
* Analizza risultati gap analysis e genera raccomandazioni * Analizza risultati gap analysis e genera raccomandazioni
*/ */
@ -162,8 +188,23 @@ PROMPT;
*/ */
public function classifyIncident(string $title, string $description, array $organization): array public function classifyIncident(string $title, string $description, array $organization): array
{ {
$entityType = $organization['entity_type'] ?? 'important';
// Allegato 3 (soggetti essenziali) vs Allegato 4 (soggetti importanti):
// gli importanti NON hanno l'obbligo sugli incidenti ricorrenti (IS-4).
$isEssential = ($entityType === 'essential');
$allowedIs = $isEssential ? 'IS-1|IS-2|IS-3|IS-4' : 'IS-1|IS-2|IS-3';
$allegato = $isEssential ? 'Allegato 3 (soggetti essenziali)' : 'Allegato 4 (soggetti importanti)';
$sourcesBlock = $this->authoritativeSourcesBlock();
$prompt = <<<PROMPT $prompt = <<<PROMPT
Sei un analista di incident response. Classifica il seguente incidente di sicurezza secondo i criteri NIS2. Sei un analista di incident response NIS2. Classifica il seguente incidente secondo il quadro normativo italiano.
## Quadro normativo di riferimento (cita la fonte pertinente in ogni campo motivazionale)
- Obbligo di notifica: art. 23 D.Lgs. 138/2024 e Direttiva (UE) 2022/2555.
- Classificazione incidenti significativi e tempistiche: Determinazione ACN n. 164179/2025, {$allegato}.
- Tempistiche: preallarme entro 24h, notifica completa entro 72h, relazione finale entro 1 mese dalla conoscenza dell'incidente significativo.
- Tipologie significative applicabili a questo soggetto ({$entityType}): {$allowedIs}.
{$sourcesBlock}
## Incidente ## Incidente
- Titolo: {$title} - Titolo: {$title}
@ -171,21 +212,23 @@ Sei un analista di incident response. Classifica il seguente incidente di sicure
## Organizzazione ## Organizzazione
- Settore: {$organization['sector']} - Settore: {$organization['sector']}
- Tipo entità: {$organization['entity_type']} - Tipo entità: {$entityType}
Rispondi in formato JSON: Rispondi in formato JSON:
{ {
"classification": "cyber_attack|data_breach|system_failure|human_error|natural_disaster|supply_chain|other", "classification": "cyber_attack|data_breach|system_failure|human_error|natural_disaster|supply_chain|other",
"nis2_incident_type": "{$allowedIs}|none",
"severity": "low|medium|high|critical", "severity": "low|medium|high|critical",
"is_significant": true/false, "is_significant": true/false,
"significance_reason": "Motivo se significativo secondo NIS2", "significance_reason": "Motivo con citazione esplicita della fonte (es. Determina ACN 164179/2025, {$allegato})",
"requires_csirt_notification": true/false, "requires_csirt_notification": true/false,
"notification_basis": "Riferimento normativo dell'obbligo (es. art. 23 D.Lgs. 138/2024)",
"suggested_actions": ["Azione immediata 1", "Azione immediata 2"], "suggested_actions": ["Azione immediata 1", "Azione immediata 2"],
"potential_impact": "Descrizione impatto potenziale", "potential_impact": "Descrizione impatto potenziale",
"iocs_to_check": ["Indicatore 1", "Indicatore 2"] "iocs_to_check": ["Indicatore 1", "Indicatore 2"]
} }
Rispondi SOLO con il JSON. Non inventare riferimenti: usa solo le fonti elencate sopra. Rispondi SOLO con il JSON.
PROMPT; PROMPT;
$response = $this->callAPI($prompt); $response = $this->callAPI($prompt);
@ -219,6 +262,12 @@ Rispondi sempre in italiano, in modo professionale, preciso e conciso.
Non includere dati identificativi dell'organizzazione nelle risposte. Non includere dati identificativi dell'organizzazione nelle risposte.
SYSTEM; SYSTEM;
// Grounding su fonti certe: applicato solo al prompt di default
// (i system prompt espliciti gestiscono le fonti per conto proprio).
if ($systemPrompt === null) {
$system .= "\n" . $this->authoritativeSourcesBlock();
}
$body = [ $body = [
'model' => $this->model, 'model' => $this->model,
'max_tokens' => $this->maxTokens, 'max_tokens' => $this->maxTokens,
@ -602,7 +651,8 @@ PROMPT;
} }
$systemPrompt = "Sei un esperto consulente di cybersecurity NIS2 (EU 2022/2555) e D.Lgs. 138/2024.\n" $systemPrompt = "Sei un esperto consulente di cybersecurity NIS2 (EU 2022/2555) e D.Lgs. 138/2024.\n"
. "Rispondi in modo preciso e cita le fonti del contesto quando rilevanti.\n"; . "Rispondi in modo preciso e cita le fonti del contesto quando rilevanti.\n"
. $this->authoritativeSourcesBlock();
if (!empty($contextBlock)) { if (!empty($contextBlock)) {
$systemPrompt .= "\n## Contesto documentale (knowledge base)\n" . $contextBlock $systemPrompt .= "\n## Contesto documentale (knowledge base)\n" . $contextBlock
. "\n\nQuando rispondi, cita esplicitamente i numeri tra parentesi quadre [1], [2], ... che corrispondono ai documenti del contesto."; . "\n\nQuando rispondi, cita esplicitamente i numeri tra parentesi quadre [1], [2], ... che corrispondono ai documenti del contesto.";

View File

@ -0,0 +1,202 @@
<?php
/**
* NIS2 Agile - Asset Scoring Service
*
* Metodologia di scoring rilevanza NIS2 (requisito GV.OC-04).
* Adattata dai mockup docs/nis2/assets.html + doc-relevant-systems.html.
*
* 6 criteri pesati, punteggio 0-100:
* C1 Criticita Operativa 0-25
* C2 Impatto Interruzione 0-25
* C3 Dati Trattati 0-20
* C4 Dipendenze 0-15
* C5 Esposizione 0-10
* C6 Obblighi Normativi 0-5
*
* Soglia rilevanza NIS2: score >= 40.
* Classi: >=80 critico | 60-79 alto | 40-59 medio | 20-39 basso | <20 trascurabile.
*
* La logica e' PURA (nessun side effect / DB): si presta a unit test e riuso.
*/
class AssetScoringService
{
public const RELEVANCE_THRESHOLD = 40;
/**
* Griglia ufficiale: per ogni criterio, la lista di opzioni selezionabili
* (value => punti) con label per la UI. value e' una chiave stabile usata
* dal frontend e salvata in relevance_criteria JSON.
*/
public const GRID = [
'c1_operational_criticality' => [
'label' => 'Criticita Operativa',
'max' => 25,
'help' => 'Quanto il sistema e essenziale per l\'erogazione dei servizi core business.',
'options' => [
'critical' => ['label' => 'Critico', 'points' => 25],
'very_high' => ['label' => 'Molto Alto', 'points' => 20],
'high' => ['label' => 'Alto', 'points' => 15],
'medium' => ['label' => 'Medio', 'points' => 10],
'low' => ['label' => 'Basso', 'points' => 5],
'negligible' => ['label' => 'Trascurabile', 'points' => 0],
],
],
'c2_disruption_impact' => [
'label' => 'Impatto Interruzione',
'max' => 25,
'help' => 'Conseguenze di un\'interruzione in termini di durata e utenti impattati.',
'options' => [
'gt24h_gt70' => ['label' => '>24h + >70% utenti', 'points' => 25],
'h8_24_50_70' => ['label' => '8-24h + 50-70% utenti', 'points' => 20],
'h4_8_30_50' => ['label' => '4-8h + 30-50% utenti', 'points' => 15],
'h1_4_10_30' => ['label' => '1-4h + 10-30% utenti', 'points' => 10],
'lt1h_lt10' => ['label' => '<1h + <10% utenti', 'points' => 5],
'none' => ['label' => 'Nessun impatto', 'points' => 0],
],
],
'c3_data_processed' => [
'label' => 'Dati Trattati',
'max' => 20,
'help' => 'Sensibilita e criticita dei dati gestiti dal sistema.',
'options' => [
'gdpr_art9' => ['label' => 'Dati Sensibili Art.9 GDPR', 'points' => 20],
'personal_large' => ['label' => 'Dati Personali larga scala', 'points' => 15],
'personal_fin' => ['label' => 'Dati Personali + Finanziari', 'points' => 10],
'confidential' => ['label' => 'Dati Aziendali Riservati', 'points' => 5],
'public' => ['label' => 'Dati Pubblici', 'points' => 0],
],
],
'c4_dependencies' => [
'label' => 'Dipendenze',
'max' => 15,
'help' => 'Quanti altri sistemi critici dipendono da questo sistema.',
'options' => [
'ge5_critical' => ['label' => '>=5 sistemi critici', 'points' => 15],
'n3_4_critical' => ['label' => '3-4 sistemi critici', 'points' => 12],
'n2_critical' => ['label' => '2 sistemi critici', 'points' => 9],
'n1_critical' => ['label' => '1 sistema critico', 'points' => 6],
'noncritical' => ['label' => '1-2 sistemi non critici', 'points' => 3],
'none' => ['label' => 'Nessuna dipendenza', 'points' => 0],
],
],
'c5_exposure' => [
'label' => 'Esposizione',
'max' => 10,
'help' => 'Superficie di attacco ed esposizione del sistema.',
'options' => [
'internet_no_mfa' => ['label' => 'Internet pubblico senza MFA', 'points' => 10],
'internet_mfa' => ['label' => 'Internet con MFA', 'points' => 8],
'partner_net' => ['label' => 'Reti partner/fornitori', 'points' => 6],
'intranet' => ['label' => 'Rete aziendale intranet', 'points' => 4],
'mgmt_isolated' => ['label' => 'Rete gestione isolata', 'points' => 2],
'air_gapped' => ['label' => 'Completamente isolato', 'points' => 0],
],
],
'c6_regulatory' => [
'label' => 'Obblighi Normativi',
'max' => 5,
'help' => 'Se il sistema e soggetto a obblighi normativi o contrattuali specifici.',
'options' => [
'nis2_required' => ['label' => 'Richiesto da NIS2', 'points' => 5],
'mandatory_cert' => ['label' => 'Certificazioni obbligatorie', 'points' => 4],
'strict_sla' => ['label' => 'Obblighi SLA stringenti', 'points' => 3],
'external_audit' => ['label' => 'Audit esterni regolari', 'points' => 2],
'none' => ['label' => 'Nessun obbligo', 'points' => 0],
],
],
];
/**
* Calcola lo score a partire dalle selezioni dell'utente.
*
* @param array $criteria mappa criterioKey => optionValue
* es. ['c1_operational_criticality' => 'critical', ...]
* @return array{score:int, class:string, is_relevant:bool, breakdown:array, criticality:string}
* @throws InvalidArgumentException se un criterio/opzione non e valido
*/
public static function calculate(array $criteria): array
{
$score = 0;
$breakdown = [];
foreach (self::GRID as $key => $def) {
if (!array_key_exists($key, $criteria)) {
throw new InvalidArgumentException("Criterio mancante: {$key}");
}
$optVal = $criteria[$key];
if (!isset($def['options'][$optVal])) {
throw new InvalidArgumentException("Opzione non valida '{$optVal}' per criterio {$key}");
}
$pts = $def['options'][$optVal]['points'];
$score += $pts;
$breakdown[$key] = [
'value' => $optVal,
'label' => $def['options'][$optVal]['label'],
'points' => $pts,
'max' => $def['max'],
];
}
$class = self::classify($score);
return [
'score' => $score,
'class' => $class,
'is_relevant' => $score >= self::RELEVANCE_THRESHOLD,
'breakdown' => $breakdown,
// mapping verso l'enum legacy assets.criticality per coerenza UI esistente
'criticality' => self::toLegacyCriticality($score),
];
}
/** Classe testuale secondo le soglie ufficiali. */
public static function classify(int $score): string
{
if ($score >= 80) return 'critico';
if ($score >= 60) return 'alto';
if ($score >= 40) return 'medio';
if ($score >= 20) return 'basso';
return 'trascurabile';
}
/** Allinea lo score all'enum assets.criticality preesistente (low/medium/high/critical). */
public static function toLegacyCriticality(int $score): string
{
if ($score >= 80) return 'critical';
if ($score >= 60) return 'high';
if ($score >= 40) return 'medium';
return 'low';
}
/** Misure obbligatorie associate alla classe (per report GV.OC-04 / UI). */
public static function requiredMeasures(string $class): array
{
return [
'critico' => [
'Monitoraggio continuo 24/7 (SIEM/SOC)',
'Backup immutabile con test ripristino periodico',
'MFA obbligatoria + accessi privilegiati controllati (PAM)',
'Inclusione obbligatoria in BIA e piano di continuita',
'Test di vulnerabilita/penetration test almeno annuali',
],
'alto' => [
'Monitoraggio in orario lavorativo + alerting',
'Backup regolari con verifica integrita',
'MFA per accessi remoti',
'Inclusione in risk assessment ciclico',
],
'medio' => [
'Logging centralizzato',
'Backup periodici',
'Patch management documentato',
],
'basso' => [
'Inventario aggiornato',
'Patch management standard',
],
'trascurabile' => [
'Censimento in inventario asset',
],
][$class] ?? [];
}
}

View File

@ -255,6 +255,118 @@ class ReportService
* @param int $orgId ID dell'organizzazione * @param int $orgId ID dell'organizzazione
* @return string Documento HTML completo * @return string Documento HTML completo
*/ */
/**
* Registro formale "Elenco Sistemi Rilevanti NIS2" (requisito GV.OC-04).
* HTML stampabile (PDF via stampa browser). Cita le fonti normative certe.
* Adattato dal mockup docs/nis2/doc-relevant-systems.html.
*/
public function generateRelevantSystemsRegister(int $orgId): string
{
$org = Database::fetchOne('SELECT name, sector, entity_type FROM organizations WHERE id = ?', [$orgId]);
if (!$org) {
return $this->buildErrorHtml('Organizzazione non trovata');
}
$systems = Database::fetchAll(
"SELECT a.name, a.asset_type, a.category, a.ip_address, a.location,
a.relevance_score, a.relevance_class, a.relevance_assessed_at,
u.full_name AS owner_name
FROM assets a
LEFT JOIN users u ON u.id = a.owner_user_id
WHERE a.organization_id = ? AND a.is_nis2_relevant = 1
ORDER BY a.relevance_score DESC, a.name",
[$orgId]
);
$orgName = htmlspecialchars($org['name'] ?? '', ENT_QUOTES, 'UTF-8');
$entity = htmlspecialchars($org['entity_type'] ?? '', ENT_QUOTES, 'UTF-8');
$sector = htmlspecialchars($org['sector'] ?? '', ENT_QUOTES, 'UTF-8');
$date = date('d/m/Y');
$appName = defined('APP_NAME') ? APP_NAME : 'NIS2 Agile';
$appVer = defined('APP_VERSION') ? APP_VERSION : '1.0.0';
$classColors = ['critico' => '#dc2626', 'alto' => '#ea580c', 'medio' => '#ca8a04'];
$counts = ['critico' => 0, 'alto' => 0, 'medio' => 0];
$rowsHtml = '';
if (empty($systems)) {
$rowsHtml = '<tr><td colspan="7" style="text-align:center;color:#888;padding:24px;">Nessun sistema classificato come rilevante (score &ge; 40). Eseguire la valutazione di rilevanza dal modulo Inventario Asset.</td></tr>';
} else {
$n = 0;
foreach ($systems as $s) {
$n++;
$cls = $s['relevance_class'] ?? 'medio';
if (isset($counts[$cls])) $counts[$cls]++;
$color = $classColors[$cls] ?? '#6b7280';
$rowsHtml .= '<tr>'
. '<td>' . $n . '</td>'
. '<td><strong>' . htmlspecialchars($s['name'], ENT_QUOTES, 'UTF-8') . '</strong></td>'
. '<td>' . htmlspecialchars(($s['asset_type'] ?? '') . ($s['category'] ? ' / ' . $s['category'] : ''), ENT_QUOTES, 'UTF-8') . '</td>'
. '<td>' . htmlspecialchars($s['ip_address'] ?: '-', ENT_QUOTES, 'UTF-8') . '</td>'
. '<td>' . htmlspecialchars($s['owner_name'] ?: '-', ENT_QUOTES, 'UTF-8') . '</td>'
. '<td style="text-align:center;font-weight:700;">' . (int) $s['relevance_score'] . '/100</td>'
. '<td style="color:' . $color . ';font-weight:700;text-transform:uppercase;">' . htmlspecialchars($cls, ENT_QUOTES, 'UTF-8') . '</td>'
. '</tr>';
}
}
$total = count($systems);
return <<<HTML
<!DOCTYPE html>
<html lang="it"><head><meta charset="utf-8">
<title>Elenco Sistemi Rilevanti NIS2 - {$orgName}</title>
<style>
body{font-family:'Segoe UI',Arial,sans-serif;color:#1f2937;margin:40px;font-size:13px;}
.conf{background:#7f1d1d;color:#fff;text-align:center;padding:8px;font-weight:700;letter-spacing:1px;border-radius:6px;margin-bottom:18px;}
h1{font-size:22px;margin:0 0 4px;} h2{font-size:15px;border-bottom:2px solid #06b6d4;padding-bottom:4px;margin-top:28px;}
.meta{color:#6b7280;font-size:12px;margin-bottom:18px;}
table{width:100%;border-collapse:collapse;margin-top:10px;} th,td{border:1px solid #e5e7eb;padding:7px 9px;text-align:left;}
th{background:#f0f9ff;font-size:12px;} .stats{display:flex;gap:14px;margin:14px 0;}
.stat{flex:1;border:1px solid #e5e7eb;border-radius:8px;padding:10px;text-align:center;}
.stat .v{font-size:22px;font-weight:800;} .src{background:#f9fafb;border-left:4px solid #06b6d4;padding:10px 14px;font-size:11.5px;color:#374151;margin-top:24px;border-radius:0 6px 6px 0;}
.sign{margin-top:36px;display:flex;justify-content:space-between;} .sign div{width:45%;border-top:1px solid #9ca3af;padding-top:6px;font-size:12px;color:#4b5563;}
@media print{body{margin:12mm;} .noprint{display:none;}}
</style></head><body>
<div class="conf">&#9888; DOCUMENTO RISERVATO - DISTRIBUZIONE LIMITATA &#9888;</div>
<h1>{$orgName}</h1>
<h1 style="font-size:18px;color:#0e7490;">Elenco Formale dei Sistemi Rilevanti NIS2</h1>
<div class="meta">Documento ai sensi della Direttiva (UE) 2022/2555 e del D.Lgs. 138/2024 &mdash; Requisito GV.OC-04 (NIST CSF 2.0)<br>
Settore: {$sector} &nbsp;|&nbsp; Tipo soggetto: {$entity} &nbsp;|&nbsp; Data emissione: {$date} &nbsp;|&nbsp; Generato da {$appName} v{$appVer}</div>
<h2>1. Premessa e metodologia</h2>
<p>Il presente documento costituisce l'elenco formale dei sistemi informativi e di rete classificati come <strong>rilevanti</strong> ai fini della conformita alla Direttiva NIS2. La classificazione adotta una metodologia di scoring 0-100 su sei criteri (Criticita Operativa, Impatto Interruzione, Dati Trattati, Dipendenze, Esposizione, Obblighi Normativi). <strong>Soglia di rilevanza: punteggio &ge; 40.</strong> I sistemi con punteggio &ge; 80 sono considerati critici e richiedono misure di sicurezza massime e monitoraggio continuo.</p>
<div class="stats">
<div class="stat"><div class="v">{$total}</div>Sistemi rilevanti</div>
<div class="stat"><div class="v" style="color:#dc2626;">{$counts['critico']}</div>Critici (&ge;80)</div>
<div class="stat"><div class="v" style="color:#ea580c;">{$counts['alto']}</div>Alti (60-79)</div>
<div class="stat"><div class="v" style="color:#ca8a04;">{$counts['medio']}</div>Medi (40-59)</div>
</div>
<h2>2. Elenco sistemi rilevanti</h2>
<table>
<thead><tr><th>#</th><th>Sistema</th><th>Tipo/Categoria</th><th>IP</th><th>Responsabile</th><th>Punteggio</th><th>Classe</th></tr></thead>
<tbody>{$rowsHtml}</tbody>
</table>
<div class="src">
<strong>Fonti normative certe:</strong><br>
&bull; Direttiva (UE) 2022/2555 (NIS2) &mdash; Parlamento europeo e Consiglio UE<br>
&bull; D.Lgs. 4 settembre 2024, n. 138 &mdash; recepimento NIS2 (artt. 23, 24)<br>
&bull; NIST Cybersecurity Framework 2.0 &mdash; controllo GV.OC-04 (elenco sistemi rilevanti)
</div>
<div class="sign">
<div>Redatto da (CISO / Responsabile Compliance)</div>
<div>Approvato da (Organo di gestione / Direzione)</div>
</div>
<p class="noprint" style="margin-top:24px;"><button onclick="window.print()">Stampa / Salva PDF</button></p>
</body></html>
HTML;
}
public function generateExecutiveReport(int $orgId): string public function generateExecutiveReport(int $orgId): string
{ {
$data = $this->generateComplianceReport($orgId); $data = $this->generateComplianceReport($orgId);

View File

@ -19,7 +19,7 @@ class VectorService
$url = getenv('QDRANT_URL') $url = getenv('QDRANT_URL')
?: ($_SERVER['QDRANT_URL'] ?? null) ?: ($_SERVER['QDRANT_URL'] ?? null)
?: ($_ENV['QDRANT_URL'] ?? null) ?: ($_ENV['QDRANT_URL'] ?? null)
?: 'http://172.21.0.5:6333'; // IP nis2-qdrant nella docker_nis2-network ?: 'http://172.21.0.3:6333'; // IP nis2-qdrant (agg. 2026-05-29: era .5, container con IP dinamico driftato). TODO: assegnare ipv4_address statico in docker-compose per evitare ricorrenze.
$this->qdrantUrl = rtrim($url, '/'); $this->qdrantUrl = rtrim($url, '/');
$this->collection = $collection; $this->collection = $collection;
} }

View File

@ -0,0 +1,263 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Evix Suite — Analisi Concorrenza & Best-of-Breed (Strategico/Interno)</title>
<style>
:root{
--cy:#06B6D4; --cy-d:#0e7490; --ink:#0f172a; --mut:#64748b; --line:#e2e8f0;
--ok:#16a34a; --warn:#ea580c; --bad:#dc2626; --bg:#f8fafc; --card:#fff;
--partial:#ca8a04;
}
*{box-sizing:border-box}
body{font-family:'Segoe UI',system-ui,Arial,sans-serif;color:var(--ink);background:var(--bg);margin:0;line-height:1.55;font-size:15px}
.wrap{max-width:1180px;margin:0 auto;padding:32px 24px 80px}
header.hero{background:linear-gradient(135deg,#0f172a,#0e7490);color:#fff;border-radius:18px;padding:36px 40px;margin-bottom:14px}
header.hero .tag{display:inline-block;background:rgba(255,255,255,.15);padding:4px 12px;border-radius:999px;font-size:12px;letter-spacing:1px;text-transform:uppercase}
header.hero h1{margin:14px 0 6px;font-size:30px}
header.hero p{margin:0;opacity:.9;max-width:760px}
.disclaimer{background:#fff7ed;border:1px solid #fed7aa;color:#9a3412;border-radius:12px;padding:14px 18px;font-size:13.5px;margin:14px 0 30px}
h2{font-size:22px;margin:40px 0 6px;border-left:5px solid var(--cy);padding-left:12px}
h2 .sub{display:block;font-size:13px;color:var(--mut);font-weight:400;margin-top:3px;padding-left:1px}
h3{font-size:17px;margin:26px 0 8px;color:var(--cy-d)}
p.lead{color:var(--mut)}
.grid{display:grid;gap:16px}
.modules{grid-template-columns:repeat(auto-fill,minmax(250px,1fr))}
.card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:18px}
.card h4{margin:0 0 4px;font-size:16px}
.badge{display:inline-block;font-size:11px;font-weight:700;padding:3px 9px;border-radius:999px;text-transform:uppercase;letter-spacing:.5px}
.b-ok{background:#dcfce7;color:#166534} .b-near{background:#fef9c3;color:#854d0e}
.b-gap{background:#fee2e2;color:#991b1b}
.card ul{margin:8px 0 0;padding-left:18px;font-size:13.5px;color:#334155}
.card ul.gap li{color:#9a3412}
table{width:100%;border-collapse:collapse;background:#fff;border:1px solid var(--line);border-radius:12px;overflow:hidden;font-size:13.5px;margin-top:10px}
th,td{padding:10px 12px;text-align:left;border-bottom:1px solid var(--line);vertical-align:top}
thead th{background:#ecfeff;color:var(--cy-d);font-size:12.5px;position:sticky;top:0}
tbody tr:last-child td{border-bottom:none}
.col-evix{background:#f0fdff}
.y{color:var(--ok);font-weight:700}.n{color:var(--bad);font-weight:700}.pp{color:var(--partial);font-weight:700}
.v{font-size:11px;color:var(--mut);font-style:italic}
.legend{display:flex;gap:18px;flex-wrap:wrap;font-size:13px;color:var(--mut);margin:10px 0 0}
.legend span{display:inline-flex;align-items:center;gap:6px}
.dot{width:11px;height:11px;border-radius:50%;display:inline-block}
.roadmap{border-left:3px solid var(--cy);padding-left:18px;margin-top:10px}
.roadmap .item{margin-bottom:14px}
.roadmap .when{font-size:12px;font-weight:700;color:var(--cy-d);text-transform:uppercase;letter-spacing:.5px}
footer{margin-top:50px;border-top:1px solid var(--line);padding-top:18px;font-size:12.5px;color:var(--mut)}
.src{background:#f1f5f9;border-radius:10px;padding:14px 18px;font-size:12.5px;color:#475569;margin-top:14px}
@media(max-width:680px){header.hero{padding:24px}.wrap{padding:18px}}
</style>
</head>
<body>
<div class="wrap">
<header class="hero">
<span class="tag">Documento strategico interno · gap-driven</span>
<h1>Evix Suite — Analisi Concorrenza & posizionamento Best-of-Breed</h1>
<p>Obiettivo strategico: ogni modulo della suite Evix (i prodotti Agile) deve essere <strong>best-of-breed</strong> nel proprio dominio. Questo documento valuta onestamente dove lo siamo già e dove mancano feature per diventarlo, rispetto a piattaforme GRC internazionali e soluzioni NIS2/compliance italiane.</p>
</header>
<div class="disclaimer">
<strong>Nota metodologica (fonti certe).</strong> Le capacità di <strong>Evix/NIS2 Agile</strong> riportate sono verificate sul codice sorgente del prodotto (feature realmente implementate al 2026-05-29, v1.7.0). Le capacità dei <strong>concorrenti</strong> riflettono posizionamenti generali di pubblico dominio e <em>vanno verificate</em> prima di qualsiasi uso commerciale: le celle marcate <span class="v">[da verificare]</span> richiedono una fonte primaria (sito vendor, demo, analyst report). Questo è un documento <strong>interno</strong>, non un materiale di marketing.
</div>
<h2>1. La suite Evix come insieme di moduli
<span class="sub">Evix = brand-ombrello; ciascun prodotto Agile è un "modulo" della suite, integrabile via API condivise (agile-services) e SSO centralizzato.</span>
</h2>
<div class="grid modules">
<div class="card">
<h4>NIS2 Agile</h4>
<span class="badge b-ok">Maturo</span>
<ul><li>Compliance Direttiva (UE) 2022/2555 + D.Lgs. 138/2024</li><li>Oggetto principale di questa analisi</li></ul>
</div>
<div class="card">
<h4>231 Agile</h4>
<span class="badge b-near">In suite</span>
<ul><li>Modello Organizzativo 231</li><li>Integrato con NIS2 via Services API (mapping NIS2→MOG)</li></ul>
</div>
<div class="card">
<h4>SustainAI Agile</h4>
<span class="badge b-near">In suite</span>
<ul><li>Sostenibilità / ESG / CSRD</li><li>Stesso stack UI/RAG</li></ul>
</div>
<div class="card">
<h4>TRPG Agile</h4>
<span class="badge b-near">In suite</span>
<ul><li>Risk &amp; privacy / governance</li><li>Allineamento Auth/SSO/Sessions in corso</li></ul>
</div>
</div>
<p class="lead" style="margin-top:12px">Il vantaggio competitivo di suite (non del singolo modulo) è l'<strong>integrazione cross-prodotto</strong> + <strong>motore AI/RAG condiviso</strong> + <strong>multi-tenancy e white-label per studi di consulenza</strong>. Sotto, il dettaglio modulo-per-modulo del prodotto NIS2 (il più maturo); gli altri prodotti richiedono un audit analogo dedicato.</p>
<h2>2. Scorecard Best-of-Breed — moduli di NIS2 Agile
<span class="sub">Verde = già best-of-breed nel segmento NIS2 Italia · Giallo = competitivo, gap colmabili · Rosso = gap rilevante vs leader di categoria</span>
</h2>
<div class="grid modules">
<div class="card"><h4>Gap Analysis Art.21</h4><span class="badge b-ok">Best-of-breed (IT)</span>
<ul><li>80 domande su 10 categorie Art.21, scoring + analisi AI</li><li>Mapping ISO 27001 + ora NIST CSF 2.0 (43 controlli)</li></ul>
<ul class="gap gap"><li>Gap: benchmark settoriale anonimizzato assente</li></ul>
</div>
<div class="card"><h4>Asset &amp; Sistemi Rilevanti</h4><span class="badge b-ok">Best-of-breed (IT)</span>
<ul><li>Scoring rilevanza 0-100 a 6 criteri (GV.OC-04)</li><li>Registro formale stampabile + classi critico/alto/medio</li></ul>
<ul class="gap"><li>Gap: auto-discovery asset e integrazione CMDB/cloud assenti</li></ul>
</div>
<div class="card"><h4>Gestione Incidenti</h4><span class="badge b-ok">Best-of-breed (IT)</span>
<ul><li>Art.23 24h/72h/30g + tassonomia IS-1..4 (Determina ACN 164179/2025)</li><li>PIR 5-Whys + metriche TTD/TTC/TTR; regime essenziale/importante</li></ul>
<ul class="gap"><li>Gap: ingestion automatica da SIEM/SOC/EDR (oggi manuale)</li></ul>
</div>
<div class="card"><h4>Risk Management</h4><span class="badge b-near">Competitivo</span>
<ul><li>Registro rischi, matrice 5×5 ISO 27005, AI suggest</li></ul>
<ul class="gap"><li>Gap vs leader: scenari quantitativi (FAIR), monte-carlo, KRI dashboard</li></ul>
</div>
<div class="card"><h4>Audit &amp; Evidence</h4><span class="badge b-ok">Differenziante</span>
<ul><li>Hash-chain SHA-256 immutabile (integrità forense) + export certificato</li><li>Mapping ISO27001 e NIST CSF 2.0</li></ul>
<ul class="gap"><li>Gap: raccolta evidenze automatica (connettori) assente</li></ul>
</div>
<div class="card"><h4>Policy Management</h4><span class="badge b-near">Competitivo</span>
<ul><li>Generazione bozze AI + workflow approvazione</li></ul>
<ul class="gap"><li>Gap: versioning/diff avanzato, attestation dipendenti</li></ul>
</div>
<div class="card"><h4>Supply Chain</h4><span class="badge b-near">Competitivo</span>
<ul><li>Valutazione fornitori + risk scoring + Art.21.2(d)</li></ul>
<ul class="gap"><li>Gap: questionari self-assessment al fornitore, rating esterni</li></ul>
</div>
<div class="card"><h4>AI / Knowledge Base</h4><span class="badge b-ok">Differenziante</span>
<ul><li>RAG multi-livello (SYSTEM/FIRM/ORG) su Qdrant + Voyage</li><li>Grounding su testi normativi ufficiali con citazioni (fonti certe)</li></ul>
<ul class="gap"><li>Gap: nessun gap critico; estendere copertura KB normativa</li></ul>
</div>
<div class="card"><h4>Continuous Monitoring</h4><span class="badge b-gap">Gap rilevante</span>
<ul class="gap"><li>Assente: monitoraggio continuo dei controlli + evidence automation (core di Vanta/Drata)</li><li>Oggi: assessment puntuale + evidenze manuali</li></ul>
</div>
<div class="card"><h4>Integrazioni / Connettori</h4><span class="badge b-gap">Gap rilevante</span>
<ul class="gap"><li>Assente catalogo connettori (M365, Google, AWS/Azure, Jira, IdP, EDR)</li><li>Presente: Services API + Webhook HMAC (base solida per costruirli)</li></ul>
</div>
</div>
<h2>3. Matrice comparativa
<span class="sub">NIS2 Agile (Evix) vs categorie concorrenti. Confronto sul caso d'uso "compliance NIS2 per PMI/Enterprise e studi di consulenza in Italia".</span>
</h2>
<div class="legend">
<span><span class="dot" style="background:var(--ok)"></span> Sì / forte</span>
<span><span class="dot" style="background:var(--partial)"></span> Parziale</span>
<span><span class="dot" style="background:var(--bad)"></span> No / debole</span>
<span class="v">[dv] = dato concorrente da verificare</span>
</div>
<table>
<thead><tr>
<th>Capacità</th>
<th class="col-evix">NIS2 Agile (Evix)</th>
<th>GRC internazionali<br><span class="v">(ServiceNow, OneTrust, Archer)</span></th>
<th>Compliance automation<br><span class="v">(Vanta, Drata)</span></th>
<th>Soluzioni NIS2 IT<br><span class="v">[da verificare]</span></th>
<th>Consulenza + Excel</th>
</tr></thead>
<tbody>
<tr><td>NIS2 / D.Lgs.138 nativo + Determine ACN 2025</td>
<td class="col-evix y">Sì — aggiornato Determine 2025</td>
<td class="pp">Parziale, framework generici <span class="v">[dv]</span></td>
<td class="n">Debole (focus SOC2/ISO) <span class="v">[dv]</span></td>
<td class="pp">Variabile <span class="v">[dv]</span></td>
<td class="pp">Dipende dal consulente</td></tr>
<tr><td>AI nativa (gap, policy, classificazione incidenti) con grounding su fonti certe</td>
<td class="col-evix y">Sì — RAG citante testi ufficiali</td>
<td class="pp">In crescita <span class="v">[dv]</span></td>
<td class="pp">In crescita <span class="v">[dv]</span></td>
<td class="n">Raro <span class="v">[dv]</span></td>
<td class="n">No</td></tr>
<tr><td>Audit trail immutabile (hash-chain)</td>
<td class="col-evix y">Sì — SHA-256 chain</td>
<td class="y"><span class="v">[dv]</span></td>
<td class="pp">Parziale <span class="v">[dv]</span></td>
<td class="n">Raro <span class="v">[dv]</span></td>
<td class="n">No</td></tr>
<tr><td>Continuous control monitoring + evidence automation</td>
<td class="col-evix n">No (gap)</td>
<td class="y"><span class="v">[dv]</span></td>
<td class="y">Sì — core <span class="v">[dv]</span></td>
<td class="n">No <span class="v">[dv]</span></td>
<td class="n">No</td></tr>
<tr><td>Catalogo connettori/integrazioni (cloud, IdP, EDR, ticketing)</td>
<td class="col-evix pp">Parziale — API/webhook, no connettori pronti</td>
<td class="y"><span class="v">[dv]</span></td>
<td class="y">Sì — molti <span class="v">[dv]</span></td>
<td class="n">Raro <span class="v">[dv]</span></td>
<td class="n">No</td></tr>
<tr><td>Multi-tenant + white-label per studi di consulenza</td>
<td class="col-evix y">Sì — firm + branding + KB FIRM</td>
<td class="pp">Parziale/costoso <span class="v">[dv]</span></td>
<td class="pp">Programmi partner <span class="v">[dv]</span></td>
<td class="pp">Variabile <span class="v">[dv]</span></td>
<td class="n">No</td></tr>
<tr><td>Integrazione cross-compliance (NIS2 ↔ 231 ↔ ESG)</td>
<td class="col-evix y">Sì — suite Evix + Services API</td>
<td class="pp">Moduli separati <span class="v">[dv]</span></td>
<td class="n">No <span class="v">[dv]</span></td>
<td class="n">No <span class="v">[dv]</span></td>
<td class="n">No</td></tr>
<tr><td>Costo / time-to-value per PMI italiana</td>
<td class="col-evix y">Basso — onboarding guidato + visura</td>
<td class="n">Alto / enterprise <span class="v">[dv]</span></td>
<td class="pp">Medio <span class="v">[dv]</span></td>
<td class="pp">Variabile <span class="v">[dv]</span></td>
<td class="pp">Alto in ore uomo</td></tr>
<tr><td>Reporting/dashboard enterprise &amp; analytics</td>
<td class="col-evix pp">Parziale — report esecutivo + CSV</td>
<td class="y">Forte <span class="v">[dv]</span></td>
<td class="y">Forte <span class="v">[dv]</span></td>
<td class="pp">Variabile <span class="v">[dv]</span></td>
<td class="n">No</td></tr>
</tbody>
</table>
<h2>4. Dove siamo già Best-of-Breed
<span class="sub">Vantaggi difendibili, radicati su feature reali del prodotto.</span>
</h2>
<div class="grid modules">
<div class="card"><h4>🇮🇹 Aderenza normativa italiana</h4><ul><li>Unico a coprire Determina ACN 164179/2025 (IS-1..4, Allegati 3/4) e 333017/2025 a livello di workflow, non solo testo.</li></ul></div>
<div class="card"><h4>🤖 AI con fonti certe</h4><ul><li>Risposte AI ancorate ai testi normativi ufficiali con citazione esplicita e divieto di riferimenti inventati.</li></ul></div>
<div class="card"><h4>🔗 Integrità audit</h4><ul><li>Hash-chain SHA-256 + export certificato: integrità forense delle evidenze.</li></ul></div>
<div class="card"><h4>🏢 Modello consulenza/white-label</h4><ul><li>Multi-tenant, KB a 3 livelli, branding per studio: pensato per i consulenti, non solo l'azienda finale.</li></ul></div>
<div class="card"><h4>🧩 Suite integrata</h4><ul><li>NIS2 ↔ 231 ↔ ESG via Services API condivise: i competitor sono single-domain.</li></ul></div>
</div>
<h2>5. Gap → Roadmap per chiudere il "best-of-breed" su ogni modulo
<span class="sub">Priorità per impatto competitivo. Le voci P1 sono quelle che oggi ci fanno perdere confronti vs Vanta/Drata e GRC enterprise.</span>
</h2>
<div class="roadmap">
<div class="item"><div class="when">P1 · Colmare il gap "compliance automation"</div>
<strong>Continuous Control Monitoring + Evidence Automation.</strong> Connettori per raccolta automatica evidenze (M365, Google Workspace, AWS/Azure, IdP, EDR). È il core di Vanta/Drata e oggi è il nostro gap più visibile. Base esistente: Services API + Webhook HMAC.</div>
<div class="item"><div class="when">P1 · Ingestion incidenti</div>
<strong>Integrazione SIEM/SOC/EDR</strong> per apertura automatica incidenti (i mockup analizzati già prevedevano "Alert SIEM/SOC" come fonte). Trasforma il modulo incidenti da reattivo a proattivo.</div>
<div class="item"><div class="when">P2 · Asset</div>
<strong>Auto-discovery asset + import CMDB/cloud</strong> per alimentare automaticamente lo scoring di rilevanza GV.OC-04 appena introdotto.</div>
<div class="item"><div class="when">P2 · Risk quantitativo</div>
<strong>Risk analysis quantitativa (FAIR)</strong> + dashboard KRI, per competere con i GRC enterprise sul risk management.</div>
<div class="item"><div class="when">P2 · Reporting</div>
<strong>Dashboard analytics e benchmark settoriale anonimizzato</strong> (già nei TODO di progetto) — chiude il gap su reporting e aggiunge un dato che i competitor non hanno (rete multi-tenant).</div>
<div class="item"><div class="when">P3 · Supply chain &amp; policy</div>
<strong>Portale self-assessment fornitori</strong> e <strong>attestation/versioning policy</strong> per completare i due moduli "competitivi" verso il best-of-breed.</div>
</div>
<div class="src">
<strong>Fonti &amp; verificabilità.</strong> Capacità Evix/NIS2 Agile: codice sorgente del prodotto (v1.7.0, 2026-05-29) e documentazione di progetto. Riferimenti normativi: Direttiva (UE) 2022/2555, D.Lgs. 138/2024, Determine ACN 164179/2025 e 333017/2025 (registro <code>application/config/nis2_sources.php</code>). <strong>Dati concorrenti marcati [da verificare]</strong>: posizionamenti generali di pubblico dominio, da confermare con fonte primaria prima di ogni uso esterno. I nomi dei vendor sono citati a scopo di benchmarking interno.
</div>
<footer>
Evix Suite — Analisi Concorrenza (strategico/interno) · generato 2026-05-29 (CEST) · NIS2 Agile v1.7.0 ·
Documento di lavoro: <strong>non distribuire esternamente senza validazione dei dati concorrenti</strong>.
</footer>
</div>
</body>
</html>

Binary file not shown.

View File

@ -0,0 +1,233 @@
Agenda di Ricerca e Innovazione
per la Cybersicurezza
Settore
Sottosettore o tipologia di soggetto
Sottosettore o tipologia di Allegato
soggetto I: Settori ad alta criticità
Grandi imprese
(occupano almeno 250 dipendenti oppure hanno un fatturato
di almeno 50M€ oppure hanno un bilancio di almeno 43M€)
Medie imprese
(occupano almeno 50 dipendenti oppure hanno un fatturato
di almeno 10M€ oppure hanno un bilancio di almeno 10M€)
Piccole e micro imprese
Autorità di Settore
1. Energia elettrica
2. Teleriscaldamento e teleraffrescamento
1. Energia
3. Petrolio
4. Gas
5. Idrogeno
1. Trasporto aereo
2. Trasporti
2. Trasporto ferroviario
3. Trasporto per vie d'acqua
4. Trasporto su strada
3. Settore bancario
1. Enti creditizi quali definiti all'articolo 4, punto 1), del regolamento (UE) n. 575/2013 del Parlamento europeo e del Consiglio
(DORA lex specialis)
1. Gestori delle sedi di negoziazione quali definiti all'articolo 4, punto 24), della direttiva 2014/65/UE del Parlamento europeo e del Consiglio
4. Infrastrutture dei mercati finanziari
2. Controparti centrali (CCP) quali definite all'articolo 2, punto 1), del regolamento (UE) n. 648/2012 del Parlamento europeo e del Consiglio
(DORA lex specialis)
Essenziali
Importanti1
Non in ambito2
Essenziali
Importanti1
Non in ambito2
1. Prestatori di assistenza sanitaria quali definiti all'articolo 3, lettera g), della direttiva 2011/24/UE del Parlamento europeo e del Consiglio
2. Laboratori di riferimento dell'UE quali definiti all'articolo 15 del regolamento (UE) 2022/2371 del Parlamento europeo e del Consiglio
5. Settore sanitario
3. Soggetti che svolgono attività di ricerca e sviluppo relative ai medicinali quali definiti all'articolo 1, punto 2), della direttiva 2001/83/CE
del Parlamento europeo e del Consiglio
4. Soggetti che fabbricano prodotti farmaceutici di base e preparati farmaceutici di cui alla sezione C, divisione 21, della NACE Rev. 2
5. Soggetti che fabbricano dispositivi medici considerati critici durante un'emergenza di sanità pubblica (elenco dei dispositivi critici per
l'emergenza di sanità pubblica) di cui all'articolo 22 del regolamento (UE) 2022/123 del Parlamento europeo e del Consiglio
6. Acqua potabile
1. Fornitori e distributori di acque destinate al consumo umano, quali definiti all'articolo 2, punto 1, lettera a), della direttiva (UE) 2020/2184
del Parlamento europeo e del Consiglio, ma esclusi i distributori per i quali la distribuzione di acque destinate al consumo umano è una parte
non essenziale dell'attività generale di distribuzione di altri prodotti e beni
7. Acque reflue
1. Imprese che raccolgono, smaltiscono o trattano acque reflue urbane, domestiche o industriali quali definite all'articolo 2, punti da 1), 2) e 3),
della direttiva 91/271/CEE del Consiglio, escluse le imprese per cui la raccolta, lo smaltimento o il trattamento di acque reflue urbane,
domestiche o industriali è una parte non essenziale della loro attività generale
1. Fornitori di punti di interscambio Internet (Internet exchange point IXP)
2. Fornitori di servizi di sistema dei nomi di dominio (domain name system DNS), esclusi gli operatori dei server dei nomi radice
Essenziali
3. Gestori di registri dei nomi di dominio di primo livello (top level domain TLD)
4. Fornitori di servizi di cloud computing
8. Infrastrutture digitali
Essenziali
5. Fornitori di servizi di data center
Importanti1
Non in ambito2
6. Fornitori di reti di distribuzione dei contenuti (content delivery network CDN)
Essenziali
7. Prestatori di servizi fiduciari qualificati e non qualificati
8. Fornitori di reti pubbliche di comunicazione elettronica
10. Spazio
Importanti1
Essenziali
9. Fornitori di servizi di comunicazione elettronica accessibili al pubblico
9. Gestione dei servizi TIC
(business-to-business)
Essenziali i servizi fiduciari qualificati/Importanti1 quelli non qualificati
1. Fornitori di servizi gestiti
2. Fornitori di servizi di sicurezza gestiti
1. Operatori di infrastrutture terrestri possedute, gestite e operate dagli Stati membri o da privati, che sostengono la fornitura di servizi
spaziali, esclusi i fornitori di reti pubbliche di comunicazione elettronica
Essenziali
Importanti1
Non in ambito2
Essenziali
Importanti1
Non in ambito2
Allegato II: altri settori critici
1. Servizi postali e di corriere
1. Fornitori di servizi postali quali definiti all'articolo 2, punto 1 bis), della direttiva 97/67/CE, tra cui i fornitori di servizi di corriere
2. Gestione dei rifiuti
1. Imprese che si occupano della gestione dei rifiuti quali definite all'articolo 3, punto 9), della direttiva 2008/98/CE del Parlamento europeo
e del Consiglio, escluse quelle per cui la gestione dei rifiuti non è la principale attività economica
3. Fabbricazione, produzione e distribuzione
di sostanze chimiche
1. Imprese che si occupano della fabbricazione di sostanze e della distribuzione di sostanze o miscele di cui all'articolo 3, punti 9) e 14),
del regolamento (CE) n. 1907/2006 del Parlamento europeo e del Consiglio e imprese che si occupano della produzione di articoli quali definite
all'articolo 3, punto 3), del medesimo regolamento, da sostanze o miscele
4. Produzione, trasformazione e distribuzione
di alimenti
1. Imprese alimentari quali definite all'articolo 3, punto 2), del regolamento (CE) n. 178/2002 del Parlamento europeo e del Consiglio che
si occupano della distribuzione all'ingrosso e della produzione industriale e trasformazione
1. Fabbricazione di dispositivi medici e di dispositivi medico-diagnostici in vitro
Importanti1
Non in ambito2
2. Fabbricazione di computer e prodotti di elettronica e ottica
5. Fabbricazione
3. Fabbricazione di apparecchiature elettriche
4. Fabbricazione di macchinari e apparecchiature n.c.a.
5. Fabbricazione di autoveicoli, rimorchi e semirimorchi
6. Fabbricazione di altri mezzi di trasporto
1. Fornitori di mercati online
6. Fornitori di servizi digitali
2. Fornitori di motori di ricerca online
3. Fornitori di piattaforme di social network
Importanti1
4. Fornitori di servizi di registrazione dei nomi di dominio
7. Ricerca
Importanti1
1. Organizzazioni di ricerca
Non in ambito2
Allegato III: Amministrazioni centrali, regionali, locali e di altro tipo
Amministrazioni centrali:
1. Gli Organi costituzionali e di rilievo costituzionale
2. La Presidenza del Consiglio dei ministri e i Ministeri
3. Le Agenzie fiscali
4. Le Autorità amministrative indipendenti
Essenziali
Amministrazioni regionali:
1. Le Regioni e le Province autonome
Pubbliche Amministrazioni
Amministrazioni locali:
1. Le Città metropolitane
2. I Comuni con popolazione superiore a 100.000 abitanti
3. I Comuni capoluoghi di regione
4. Le Aziende sanitarie locali
Importanti1
Altri soggetti pubblici:
1. Gli Enti di regolazione dell'attività economica
2. Gli Enti produttori di servizi economici
3. Gli Enti a struttura associativa
4. Gli Enti produttori di servizi assistenziali, ricreativi e culturali
5. Gli Enti e le Istituzioni di ricerca
6. Gli Istituti zooprofilattici sperimentali
Allegato IV: Ulteriori tipologie di soggetti
1. Soggetti che forniscono servizi di trasporto pubblico locale
2. Istituti di istruzione che svolgono attività di ricerca
Soggetti a eventuale individuazione dellAutorità
Ulteriori tipologie di soggetti
3. Soggetti che svolgono attività di interesse culturale
4. Società in house, società partecipate e società a controllo pubblico, come definite nel decreto legislativo 19 agosto 2016, n.175
Possibile individuazione dellAutorità come essenziali
2
Possibile individuazione dellAutorità come importanti o essenziali
1

Binary file not shown.

View File

@ -0,0 +1,241 @@
Agenzia per la Cybersicurezza Nazionale
Determinazione del Direttore Generale dellAgenzia per la
cybersicurezza nazionale
di cui allarticolo 31, commi 1 e 2, del decreto legislativo 4 settembre 2024, n. 138, adottata
secondo le modalità di cui allarticolo 40, comma 5, lettera l), che, ai sensi dellarticolo 42,
comma 1, lettera c), in fase di prima applicazione, stabilisce le modalità e le specifiche di base
per ladempimento agli obblighi di cui agli articoli 23, 24, 25, 29 e 32 del decreto medesimo.
IL DIRETTORE GENERALE
VISTO il decreto-legge 14 giugno 2021, n. 82, come convertito con modificazioni nella legge 4
agosto 2021, n. 109, recante “Disposizioni urgenti in materia di cybersicurezza, definizione
dellarchitettura nazionale di cybersicurezza e istituzione dellAgenzia per la cybersicurezza
nazionale”;
VISTO il decreto legislativo 4 settembre 2024, n. 138, recante “il recepimento della direttiva (UE)
2022/2555, relativa a misure per un livello comune elevato di cibersicurezza nellUnione, recante
modifica del regolamento (UE) n. 910/2014 e della direttiva (UE) 2018/1972 e che abroga la
direttiva (UE) 2016/1148”, c.d. decreto NIS, ed in particolare larticolo 31, commi 1 e 2, che
prevede che, ai fini di cui agli articoli 23, 24, 25, 27, 28 e 29, l'Autorità nazionale competente NIS
stabilisce obblighi proporzionati tenuto debitamente conto del grado di esposizione dei soggetti ai
rischi, delle dimensioni dei soggetti e della probabilità che si verifichino incidenti, nonché della
loro gravità, compreso il loro impatto sociale ed economico, nonché termini, modalità, specifiche
e tempi graduali di implementazione di tali obblighi;
VISTO larticolo 40, comma 5, lettera l), del decreto NIS che prevede che tali obblighi sono stabiliti
con una o più Determinazioni dellAgenzia per la cybersicurezza nazionale, sentito il Tavolo per
lattuazione della disciplina NIS;
VISTO altresì larticolo 42, comma 1, lettera c), del decreto NIS, che prevede, in fase di prima
applicazione, che lAutorità nazionale competente NIS stabilisce le modalità e le specifiche di base
per ladempimento ai predetti obblighi;
VISTO il decreto del Presidente del Consiglio dei ministri del 10 marzo 2023, recante la nomina
del Prefetto Bruno Frattasi a Direttore generale dellAgenzia per la cybersicurezza nazionale;
VISTO il “Framework Nazionale per la Cybersecurity e la Data Protection”, edizione 2025
(Framework nazionale), realizzato dal Centro di ricerca di cyber intelligence and information
security (CIS) della Sapienza Università di Roma e dal Cybersecurity national lab del Consorzio
interuniversitario nazionale per l'informatica (CINI), in collaborazione con lAgenzia per la
cybersicurezza nazionale (ACN), quale strumento di supporto per le organizzazioni pubbliche e
private in materia di strategie e processi volti alla sicurezza informatica;
CONSIDERATO che gli allegati tecnici recanti le predette specifiche di base, illustrate nella
seconda riunione plenaria del Tavolo per lattuazione della disciplina NIS, tenutasi il 28 gennaio
1 di 6
Agenzia per la Cybersicurezza Nazionale
2025, sono stati condivisi con le Autorità di settore e con le associazioni di categoria anche per
mezzo dei tavoli settoriali di cui allarticolo 11, comma 4, lettera f), del decreto NIS;
PRESSO ATTO dei riscontri pervenuti;
SENTITO il Tavolo per lattuazione della disciplina NIS nella riunione del 10 aprile 2025;
RITENUTO di avviare la procedura di informazione ai sensi della Direttiva (UE) n. 2015/1535 del
Parlamento europeo e del Consiglio del 9 settembre 2015;
CONSIDERATO il grado di esposizione dei soggetti ai rischi, le dimensioni dei soggetti e la
probabilità che si verifichino incidenti, nonché la loro gravità, compreso il loro impatto sociale ed
economico;
ADOTTA LA PRESENTE DETERMINAZIONE
Articolo 1
(Definizioni)
1. Ai fini della presente determinazione si intende per:
a) “decreto NIS”, il decreto legislativo 4 settembre 2024, n. 138;
b) “Agenzia per la cybersicurezza nazionale”, lAgenzia per la cybersicurezza nazionale
di cui allarticolo 5, comma 1, del decreto-legge 14 giugno 2021, n. 82;
c) “Autorità nazionale competente NIS”, lAutorità nazionale competente di cui
allarticolo 10, comma 1, del decreto NIS;
d) “Autorità di settore NIS”, le Amministrazioni di cui allarticolo 11, commi 1 e 2, del
decreto NIS;
e) “soggetto NIS”, un soggetto, di cui allarticolo 2, comma 1, lettera hhh), del decreto
NIS, di natura giuridica pubblica o privata che rientra nellambito di applicazione del
decreto NIS;
f) “soggetti essenziali”, i soggetti NIS considerati essenziali ai sensi del decreto NIS;
g) “soggetti importanti”, i soggetti NIS considerati importanti ai sensi del decreto NIS;
h) “comunicazione di inserimento nellelenco dei soggetti NIS”, la comunicazione di cui
allarticolo 7, comma 3, lettera a), del decreto NIS;
i) “organi di amministrazione e direttivi”, gli organi di amministrazione e direttivi di cui
allarticolo 23 del decreto NIS, ivi incluso, laddove presente, il consiglio di
amministrazione dei soggetti NIS;
j) “misure di sicurezza di base”, specifiche di base per gli obblighi di cui agli articoli 23
e 24 del decreto NIS, sviluppate in accordo al Framework nazionale e organizzate in
funzioni, categorie, sottocategorie e requisiti;
k) “incidenti significativi di base”, le specifiche di base che descrivono gli incidenti
significativi di cui allarticolo 25 del decreto NIS;
2 di 6
Agenzia per la Cybersicurezza Nazionale
l) “sistemi informativi e di rete rilevanti”, sistemi informativi e di rete la cui
compromissione comporterebbe un impatto significativo sulla confidenzialità, integrità
e disponibilità delle attività e servizi per i quali il soggetto NIS rientra nellambito di
applicazione del decreto NIS;
m) “fornitori di servizi di registrazione dei nomi di dominio”, i fornitori di cui allarticolo
2, comma 1, lettera oo), del decreto NIS;
n) “gestori di registri dei nomi di dominio di primo livello”, i gestori di cui allarticolo 2,
comma 1, lettera pp), del decreto NIS;
o) “soggetti PSNC-NIS”, i soggetti di cui all'articolo 1, comma 2-bis, del decreto-legge n.
105 del 2019 che sono soggetti NIS;
p) “sistemi informativi e di rete PSNC”, sistemi informativi e di rete che sono inseriti
nell'elenco di cui all'articolo 1, comma 2, lettera b), del decreto-legge n. 105 del 2019
q) “operatori di servizi essenziali”, c.d. OSE, i soggetti NIS identificati prima della data di
entrata in vigore del decreto NIS come operatori di servizi essenziali ai sensi del decreto
legislativo 18 maggio 2018, n. 65;
r) “sistemi informativi e di rete OSE”, sistemi informativi e di rete delloperatore di servizi
essenziali che abilitano i servizi essenziali per i quali loperatore stesso è stato
identificato ai sensi del decreto legislativo 18 maggio 2018, n. 65;
s) “operatori telco”, i soggetti NIS che forniscono reti pubbliche di comunicazione
elettronica o servizi di comunicazioni elettroniche accessibili al pubblico ai sensi del
decreto legislativo 1° agosto 2003, n. 259, ad un numero di utenti pari o superiore, anche
alternativamente:
1) all1% della base di utenti nazionale, calcolato sulla base dei dati pubblicati
dallOsservatorio trimestrale delle comunicazioni a cura dellAutorità per le
garanzie nelle comunicazioni;
2) a un milione;
t) “sistemi informativi e di rete telco”, sistemi informativi e di rete per laccesso alla rete
fissa o mobile, da postazione o da terminale mobile, individuati come critici
dalloperatore telco in quanto potenzialmente in grado di servire, per ciascun servizio
indicato:
1) una percentuale dellutenza pari o superiore all1% della base di utenti nazionale
per quel servizio, sulla base dei dati pubblicati dallOsservatorio trimestrale delle
comunicazioni a cura dellAutorità per le garanzie nelle comunicazioni;
2) unutenza pari o superiore a un milione.
Articolo 2
(Adozione delle specifiche di base)
1. In fase di prima applicazione del decreto NIS sono adottate le specifiche di base di cui agli
allegati 1, 2, 3 e 4, facenti parte integrante della presente determinazione.
2. Le misure di sicurezza di base, a carico degli organi di amministrazione e direttivi e in
materia di misure di gestione dei rischi per la sicurezza informatica, sono stabiliti:
a) per i soggetti importanti, nellallegato 1;
b) per i soggetti essenziali, nellallegato 2.
3 di 6
Agenzia per la Cybersicurezza Nazionale
3. Gli incidenti significativi di base sono stabiliti:
a) per i soggetti importanti, nellallegato 3;
b) per i soggetti essenziali, nellallegato 4.
Articolo 3
(Termini per ladozione delle specifiche di base)
1. Il termine per ladozione delle misure di sicurezza di base di cui agli allegati 1 e 2 è fissato
in diciotto mesi dalla ricezione, da parte del soggetto NIS della comunicazione di
inserimento nellelenco dei soggetti NIS.
2. Il termine per ladempimento dellobbligo di notifica degli incidenti significativi di base
descritti negli allegati 3 e 4 è fissato in nove mesi dalla ricezione, da parte del soggetto NIS,
della comunicazione di inserimento nellelenco dei soggetti NIS.
Articolo 4
(Sicurezza, stabilità e resilienza dei sistemi di nomi di dominio)
1. Fermo restando quanto previsto dallarticolo 29 del decreto NIS, entro diciotto mesi dalla
ricezione della comunicazione di inserimento nellelenco dei soggetti NIS, i gestori di
registri dei nomi di dominio di primo livello e i fornitori di servizi di registrazione dei nomi
di dominio si adeguano alle previsioni di cui al predetto articolo, commi 1 e 2, nonché
adottano e rendono pubbliche le politiche e le procedure di cui al comma 3 del medesimo
articolo.
2. Le modalità di adeguamento alle previsioni di cui allarticolo 29, commi 1 e 2, nonché le
politiche e le procedure di cui al comma 3 del medesimo articolo sono approvate dagli organi
di amministrazione e direttivi.
3. Ai sensi dellarticolo 32, comma 3, i gestori di registri dei nomi di dominio di primo livello
e i fornitori di servizi di registrazione dei nomi di dominio adottano politiche al fine di
assicurare un livello di sicurezza informatica coerente con le specifiche di cui allallegato 1.
4. Le politiche di sicurezza informatica di cui al comma 3 sono approvate dagli organi di
amministrazione e direttivi.
Articolo 5
(Obblighi di notifica per i soggetti PSNC-NIS)
1. Fermo restando quanto previsto dallarticolo 33 del decreto NIS, i soggetti PSNC-NIS
notificano gli incidenti significativi di base di cui allallegato 4, ai sensi dellarticolo 25 del
decreto NIS, limitatamente ai sistemi informativi e di rete diversi da quelli PSNC.
2. Il termine per lobbligo di cui al comma 1 decorre dalla data di entrata in vigore della
presente determinazione.
4 di 6
Agenzia per la Cybersicurezza Nazionale
Articolo 6
(Regime transitorio per gli operatori di servizi essenziali)
1. Fermo restando quanto previsto dallarticolo 2, comma 2, e dallarticolo 3, comma 1, gli
operatori di servizi essenziali, limitatamente ai sistemi informativi e di rete OSE, per quanto
non in contrasto con la legge e il decreto NIS, assicurano il mantenimento delle misure
tecniche e organizzative già adottate prima dellentrata in vigore del decreto NIS ai sensi del
decreto legislativo 18 maggio 2018, n. 65.
2. Al fine di assicurare la continuità dellobbligo di notifica di incidente di cui allarticolo 12,
comma 5, del decreto legislativo 18 maggio 2018, n. 65, dallentrata in vigore della presente
determinazione, ai sensi dellarticolo 25 del decreto NIS, gli operatori di servizi essenziali,
limitatamente ai sistemi informativi e di rete OSE, notificano gli incidenti significativi di
base di cui:
a) allallegato 3, qualora siano soggetti importanti;
b) allallegato 4, qualora siano soggetti essenziali.
3. Il termine per gli adempimenti di cui al presente articolo decorre dalla data di entrata in
vigore della presente determinazione.
Articolo 7
(Regime transitorio per gli operatori telco)
1. Fermo restando quanto previsto dallarticolo 2, comma 2, e dallarticolo 3, comma 1, gli
operatori telco, limitatamente ai sistemi informativi e di rete telco, per quanto non in
contrasto con la legge e il decreto NIS, assicurano il mantenimento delle misure di sicurezza
e di integrità delle reti e dei servizi già adottate prima dellentrata in vigore del decreto NIS
ai sensi del decreto del Ministro dello sviluppo economico del 12 dicembre 2018.
2. Al fine di assicurare la continuità dellobbligo di notifica di incidente di cui allarticolo 40,
comma 3, lettera b), del decreto legislativo 1° agosto 2003, n. 259, ai sensi dellarticolo 25
del decreto NIS, dallentrata in vigore della presente determinazione, gli operatori telco,
limitatamente ai sistemi informativi e di rete telco, notificano gli incidenti significativi di
base:
a) di cui allallegato 3, qualora siano soggetti importanti;
b) di cui allallegato 4, qualora siano soggetti essenziali.
3. Ai fini del comma 2, nella definizione del livello di servizio atteso di cui agli allegati 3 e 4,
gli operatori telco considerano come incidenti significativi di base i seguenti casi:
a) durata superiore ad unora e percentuale degli utenti colpiti superiore al quindici per
cento del totale degli utenti nazionali del servizio interessato;
b) durata superiore a due ore e percentuale degli utenti colpiti superiore al dieci per
cento del totale degli utenti nazionali del servizio interessato;
c) durata superiore a quattro ore e percentuale degli utenti colpiti superiore al cinque
per cento del totale degli utenti nazionali del servizio interessato;
5 di 6
Agenzia per la Cybersicurezza Nazionale
d) durata superiore a sei ore e percentuale degli utenti colpiti superiore al due per cento
del totale degli utenti nazionali del servizio interessato;
e) durata superiore ad otto ore e percentuale degli utenti colpiti superiore alluno per
cento del totale degli utenti nazionali del servizio interessato.
4.
Il termine per gli adempimenti di cui al presente articolo decorre dalla data di entrata in
vigore della presente determinazione.
Articolo 8
(Disposizioni finanziarie)
1. Dalla presente determinazione non derivano nuovi o maggiori oneri a carico della finanza
pubblica, anche ai sensi dellarticolo 12, comma 6, del decreto NIS.
Articolo 9
(Pubblicità)
1. La presente determinazione è pubblicata sui siti web istituzionali dellAgenzia per la
cybersicurezza nazionale e delle Autorità di settore NIS. Ne sarà data, altresì,
comunicazione tramite pubblicazione nella Gazzetta Ufficiale della Repubblica italiana.
Articolo 10
(Entrata in vigore e disposizioni transitorie)
1. Per quanto non previsto dalla presente determinazione, si applicano le disposizioni del
decreto NIS.
2. La presente determinazione entra in vigore a decorrere dal 30 aprile 2025.
3. Larticolo 2, commi 2 e 3, e larticolo 3 entrano in vigore il giorno successivo
allesperimento della procedura di informazione ai sensi della Direttiva (UE) n. 2015/1535
del Parlamento europeo e del Consiglio del 9 settembre 2015.
Roma, data del protocollo
IL DIRETTORE GENERALE
Bruno Frattasi
Firmato digitalmente da: Bruno
Frattasi
Data: 14/04/2025 10:52:39
6 di 6

Binary file not shown.

View File

@ -0,0 +1,678 @@
Agenzia per la Cybersicurezza Nazionale
Determinazione del Direttore generale dellAgenzia per la
cybersicurezza nazionale
di cui allarticolo 7, comma 6, del decreto legislativo 4 settembre 2024, n. 138, adottata
secondo le modalità di cui allarticolo 40, comma 5, recante termini, modalità e procedimenti
di utilizzo e accesso alla piattaforma digitale nonché ulteriori informazioni che i soggetti
devono fornire allAutorità nazionale competente NIS e termini, modalità e procedimento di
designazione dei rappresentanti NIS sul territorio nazionale.
IL DIRETTORE GENERALE
VISTO il decreto legislativo 12 gennaio 2019, n. 14, recante “Codice della crisi d'impresa e
dell'insolvenza in attuazione della legge 19 ottobre 2017, n. 155”;
VISTO il decreto-legge 14 giugno 2021, n. 82, convertito, con modificazioni, dalla legge 4 agosto
2021, n. 109, recante “Disposizioni urgenti in materia di cybersicurezza, definizione
dellarchitettura nazionale di cybersicurezza e istituzione dellAgenzia per la cybersicurezza
nazionale”;
VISTO il decreto legislativo 4 settembre 2024, n. 138, recante “Recepimento della direttiva (UE)
2022/2555, relativa a misure per un livello comune elevato di cibersicurezza nellUnione, recante
modifica del regolamento (UE) n. 910/2014 e della direttiva (UE) 2018/1972 e che abroga la
direttiva (UE) 2016/1148” e, in particolare, larticolo 7 e larticolo 40, comma 5, lettera b);
VISTO il Regolamento (CEE) n. 2137/85 del Consiglio del 25 luglio 1985 relativo all'istituzione di
un gruppo europeo di interesse economico (GEIE);
VISTO il decreto legislativo 23 luglio 1991, n. 240, recante “Norme per lapplicazione del
regolamento n. 85/2137/CEE relativo allistituzione di un Gruppo europeo di interesse economico GEIE, ai sensi dellart. 17 della legge 29 dicembre 1990, n. 428”;
VISTA la raccomandazione 2003/361/CE della Commissione, del 6 maggio 2003, relativa alla
definizione delle microimprese, piccole e medie imprese;
VISTA la legge 28 giugno 2024, n. 90, recante “Disposizioni in materia di rafforzamento della
cybersicurezza nazionale e di reati informatici”;
VISTO il decreto del Presidente della Repubblica 28 dicembre 2000, n. 445, recante “Testo unico
delle disposizioni legislative e regolamentari in materia di documentazione amministrativa”, e, in
particolare, larticolo 76;
CONSIDERATO che, ai sensi del richiamato articolo 7, comma 1, del decreto legislativo 4
settembre 2024, n. 138, i soggetti di cui all'articolo 3 del medesimo decreto si registrano o
1 di 16
Agenzia per la Cybersicurezza Nazionale
aggiornano la propria registrazione sulla piattaforma digitale resa disponibile dall'Autorità
nazionale competente NIS;
CONSIDERATO, altresì, che, ai sensi dellarticolo 7, comma 6, e dellarticolo 40, comma 5,
lettera b), del decreto legislativo 4 settembre 2024, n. 138, con determinazione dellAgenzia per la
cybersicurezza nazionale, sentito il Tavolo per lattuazione della disciplina NIS, sono stabiliti i
termini, le modalità nonché i procedimenti di utilizzo e accesso alla piattaforma digitale di cui
allarticolo 7 e le eventuali ulteriori informazioni che i soggetti devono fornire ai sensi dello stesso
articolo, nonché i termini, le modalità e i procedimenti di designazione dei rappresentanti di cui
allarticolo 5, comma 3, del medesimo decreto legislativo;
RICHIAMATA la propria determinazione n. 333017 del 22 settembre 2025 che ha aggiornato la
precedente determinazione n.38565 del 26 novembre 2024 integrandovi le modalità di designazione
del referente CSIRT e di aggiornamento annuale delle informazioni, tramite il servizio dedicato
NIS/Aggiornamento annuale disponibile sul Portale ACN;
RITENUTO di aggiornare e sostituire la predetta determinazione in vista della registrazione 2026
dei soggetti NIS sul portale ACN tramite il servizio dedicato NIS/Dichiarazione;
SENTITO il Tavolo per lattuazione della disciplina NIS di cui allarticolo 12 del decreto
legislativo 4 settembre 2024, n. 138, nella riunione del 18 dicembre 2025;
ADOTTA LA PRESENTE DETERMINAZIONE
Capo I
Disposizioni di carattere generale
Articolo 1
(Definizioni)
1. Ai fini della presente determinazione si intende per:
a) “decreto NIS”, il decreto legislativo 4 settembre 2024, n. 138;
b) “Agenzia per la cybersicurezza nazionale”, lAgenzia per la cybersicurezza nazionale
di cui allarticolo 5, comma 1, del decreto-legge 14 giugno 2021, n. 82;
c) “Autorità nazionale competente NIS”, lAutorità nazionale competente di cui
allarticolo 10, comma 1, del decreto NIS;
d) “Autorità di settore NIS”, le Amministrazioni di cui allarticolo 11, commi 1 e 2, del
decreto NIS;
e) “organi di amministrazione e direttivi”, gli organi di amministrazione e direttivi di cui
allarticolo 23 del decreto NIS, ivi incluso, laddove presente, il consiglio di
amministrazione dei soggetti NIS;
f) “Portale ACN”, il Portale dei servizi tramite il quale sono accessibili i servizi che
lAgenzia per la cybersicurezza nazionale mette a disposizione dei suoi interlocutori e
dei soggetti, pubblici e privati, che rientrano nellambito di applicazione della disciplina
cyber o con i quali lAgenzia deve interagire ai sensi della stessa;
2 di 16
Agenzia per la Cybersicurezza Nazionale
g) “SPID”, il Sistema pubblico dellidentità digitale, istituito ai sensi dell'art. 64 del CAD,
modificato dallart. 17-ter del decreto-legge 21 giugno 2013, n. 69, convertito con
modificazioni, dalla legge 9 agosto 2013, n. 98, ai sensi del Decreto del Presidente del
Consiglio dei Ministri 24 ottobre 2014;
h) “CIE”, Carta di Identità Elettronica, è il documento didentità personale garantita dallo
Stato e rilasciata dal Ministero dellInterno che permette laccertamento dellidentità del
possessore e laccesso ai servizi online delle Pubbliche Amministrazioni;
i) “Servizi NIS”, i servizi, accessibili tramite il Portale ACN, necessari per supportare
lespletamento degli adempimenti previsti dal decreto NIS e le interlocuzioni tra
lAutorità nazionale compente NIS e i soggetti;
j) “Servizio NIS/Dichiarazione”, il Servizio NIS reso disponibile dallAutorità nazionale
competente NIS ai soggetti ai fini della registrazione di cui allarticolo 7, comma 1, del
decreto NIS;
k) “Servizio NIS/Aggiornamento annuale informazioni”, il Servizio NIS reso disponibile
dallAutorità nazionale competente NIS ai soggetti NIS per laggiornamento annuale,
delle informazioni di cui allarticolo 7, commi 4 e 5, del decreto NIS;
l) “Servizio NIS/Aggiornamento continuo informazioni”, il Servizio NIS reso disponibile
dallAutorità nazionale competente NIS ai soggetti NIS per laggiornamento continuo,
delle informazioni trasmesse ai sensi dellarticolo 7, comma 7, del decreto NIS;
m) “sito web”, il sito web istituzionale dellAgenzia per la cybersicurezza nazionale
(acn.gov.it);
n) “piattaforma digitale”, la piattaforma digitale di cui allarticolo 7, comma 1, del decreto
NIS, accessibile tramite il Portale ACN per lerogazione dei Servizi NIS;
o) “soggetto”, un soggetto, di cui allarticolo 2, comma 1, lettera hhh), del decreto NIS, di
natura giuridica pubblica o privata per conto della quale un utente accede al Portale
ACN e, in particolare, ai Servizi NIS;
p) “soggetto NIS”, un soggetto che rientra nellambito di applicazione del decreto NIS;
q) “punto di contatto”, la persona fisica designata dal soggetto NIS ai sensi dellarticolo 7,
comma 1, lettera c), del decreto NIS;
r) “sostituto punto di contatto”, la persona fisica designata dal soggetto NIS ai sensi
dellarticolo 7, comma 4, lettera d), del decreto NIS;
s) “segreteria”, la persona fisica che svolge funzioni di supporto al punto di contatto e al
sostituto punto di contatto per promuovere lefficace interlocuzione con lAutorità
nazionale competente NIS;
t) “operatore”, la persona fisica che svolge funzioni di supporto al punto di contatto e al
sostituto punto di contatto operando sui servizi NIS;
u) “utente”, i componenti degli organi di amministrazione e direttivi, il punto di contatto,
il sostituto punto di contatto, la segreteria, il referente CSIRT, il sostituto referente
CSIRT e gli operatori che accedono al Portale ACN e, in particolare, accedono ai servizi
di competenza;
v) “referente CSIRT e sostituti”, le persone fisiche designate dal Punto di contatto per
interloquire con lo CSIRT Italia, di cui allarticolo 2, comma 1, lettera i) del decreto
NIS, ed effettuare le notifiche di cui agli articoli 25 e 26 del medesimo decreto;
w) “rappresentante NIS”, il rappresentante di cui allarticolo 5, comma 3, del decreto NIS;
3 di 16
Agenzia per la Cybersicurezza Nazionale
x) “censimento”, il processo di autenticazione, tracciamento e verifica di un utente
finalizzato alla sua associazione a un soggetto per accedere al Portale ACN e, in
particolare, ai Servizi NIS;
y) “associazione”, il processo di tracciamento, verifica e convalida dellassociazione
dellutenza con un soggetto che consente allutente stesso di accedere al Portale ACN
e, in particolare, ai Servizi NIS;
z) “registrazione”, il processo di cui allarticolo 7, comma 1, del decreto NIS;
aa) “dichiarazione”, la dichiarazione resa dal punto di contatto ai fini della registrazione;
bb) “elenco dei soggetti NIS”, lelenco dei soggetti essenziali e dei soggetti importanti di
cui allarticolo 7, comma 3, del decreto NIS;
cc) “impresa collegata”, un soggetto che soddisfa i criteri di cui allarticolo 3, paragrafi 2 e
3, dellallegato alla raccomandazione 2003/361/CE, o che fa parte di un gruppo di
imprese;
dd) “impresa autonoma”, una impresa non identificabile come impresa collegata;
ee) “gruppo di imprese”, ai sensi dellarticolo 2, comma 1, lettera h), del decreto legislativo
12 gennaio 2019, n. 14, l'insieme delle società, delle imprese e degli enti, esclusi lo
Stato e gli enti territoriali, che esercitano o sono sottoposti, ai sensi degli articoli
2497 e 2545-septies del codice civile, alla direzione e coordinamento di una società, di
un ente o di una persona fisica; a tal fine si presume, salvo prova contraria, che l'attività
di direzione e coordinamento delle società del gruppo sia esercitata dalla società o ente
tenuto al consolidamento dei loro bilanci oppure dalla società o ente che le controlla,
direttamente o indirettamente, anche nei casi di controllo congiunto;
ff) “gruppo europeo di interesse economico (GEIE)”, un gruppo di imprese costituito sulla
base delle condizioni, modalità ed effetti disciplinati dal Regolamento (CEE) n.
2137/85.
gg) “aggiornamento annuale delle informazioni”, il processo tramite il quale i soggetti NIS
forniscono e, ogni anno, aggiornano le informazioni di cui allarticolo 7, commi 4 e 5,
del decreto NIS;
hh) “aggiornamento continuo delle informazioni”, il processo tramite il quale, ai sensi
dellarticolo 7, comma 7, del decreto NIS, i soggetti NIS comunicano qualsiasi modifica
delle informazioni trasmesse ai sensi dei commi 4 e 5 del medesimo articolo
tempestivamente e, in ogni caso, entro quattordici giorni dalla data della modifica;
ii) “spazio di indirizzamento IP pubblico in uso o nella disponibilità del soggetto NIS”, gli
indirizzi IP pubblici e statici che un soggetto NIS utilizza o ha nella propria disponibilità
in forza di contratti o accordi con fornitori di servizi Internet (ISP), Registri Internet
Regionali o altre organizzazioni deputate alla fornitura di indirizzi IP sulla base delle
normative e degli accordi nazionali, europei e internazionali vigenti;
jj) “nomi di dominio in uso o nella disponibilità del soggetto NIS”, i nomi di dominio che
un soggetto NIS utilizza o ha nella propria disponibilità in forza di contratti o accordi
con fornitori di servizi di registrazione di nomi di dominio o altre organizzazioni
deputate loro fornitura di nomi di dominio sulla base delle normative e degli accordi
nazionali, europei e internazionali vigenti;
kk) “accordi di condivisione”, gli accordi di condivisione delle informazioni sulla sicurezza
informatica, di cui allarticolo 17, comma 2, del decreto NIS.
4 di 16
Agenzia per la Cybersicurezza Nazionale
Articolo 2
(Oggetto, ambito di applicazione e finalità)
1. La presente determinazione stabilisce termini, modalità e procedimenti di utilizzo e accesso
al Portale ACN e, in particolare, ai Servizi NIS nonché le ulteriori informazioni che i
soggetti NIS devono fornire allAutorità nazionale competente NIS ai fini dello svolgimento
delle funzioni attribuite dal decreto NIS, i termini, le modalità e i procedimenti di
designazione dei rappresentanti NIS nellUnione.
2. Ai fini del comma 1, la presente determinazione definisce:
a) le modalità di designazione del punto di contatto e del sostituto punto di contatto:
b) il processo per il censimento degli utenti per accedere al Portale ACN;
c) il procedimento per lassociazione degli utenti al soggetto per conto del quale accedono
ai Servizi NIS;
d) il procedimento per la registrazione, tramite il “Servizio NIS/Dichiarazione”;
e) il processo per la conferma annuale delle informazioni, tramite il “Servizio
NIS/Aggiornamento annuale informazioni”;
f) il processo per laggiornamento continuo delle informazioni, tramite il “Servizio
NIS/Aggiornamento continuo informazioni”.
3. Ai sensi dellarticolo 23, comma 1, lettera b), del decreto NIS, gli organi di amministrazione
e direttivi dei soggetti NIS sovrintendono alla registrazione, comunicazione o
aggiornamento delle informazioni di cui allarticolo 7 del medesimo decreto e sono
responsabili delle eventuali violazioni.
4. La mancata registrazione, comunicazione o aggiornamento delle informazioni di cui
allarticolo 7 del decreto NIS, con le modalità sopra indicate, è punita ai sensi dellarticolo
38 del medesimo decreto.
Articolo 3
(Termini di uso del Portale ACN e dei Servizi NIS)
1. I soggetti comunicano con lAutorità nazionale competente NIS, anche ai fini del
censimento, dellassociazione e della registrazione, esclusivamente tramite i Servizi NIS o
tramite la sezione dedicata nellarea NIS del sito web, salvo in caso di diversa espressa
specifica indicazione dellAutorità nazionale competente o per cause di forza maggiore,
fermo restando quanto previsto dal decreto NIS.
2. Le istruttorie dellAutorità nazionale competente NIS e delle Autorità di settore NIS ai fini
della presente determinazione sono svolte prioritariamente sulla base delle informazioni
trasmesse dai soggetti tramite i Servizi NIS.
3. Gli utenti aggiornano le informazioni trasmesse tramite il Portale ACN o tramite i Servizi
NIS tempestivamente, secondo eventuale specifica indicazione dellAutorità nazionale
competente NIS, nel rispetto dei termini indicati dal decreto NIS.
4. Gli utenti sono tenuti a verificare la correttezza delle informazioni visualizzate o ricevute
tramite il Portale ACN e i Servizi NIS, e in caso di incongruenze effettuano la segnalazione
di cui al comma 6.
5 di 16
Agenzia per la Cybersicurezza Nazionale
5. Resta ferma la responsabilità penale, ai sensi dellarticolo 76 del decreto del Presidente della
Repubblica del 28 dicembre 2000, n. 445, in caso di rilascio di dichiarazioni mendaci,
formazione di atti falsi o, comunque, contenenti dati non più rispondenti a verità.
6. Gli utenti segnalano, tramite gli appositi canali di comunicazione del Portale ACN o tramite
la sezione dedicata nellarea NIS del sito web, malfunzionamenti o comportamenti inattesi
del Portale ACN stesso e dei Servizi NIS.
7. Le informazioni visualizzate o ricevute tramite il Portale ACN e i Servizi NIS sono
condivise nel rispetto della politica di condivisione delle informazioni. Salvo diversa
specifica indicazione, le informazioni visualizzate o ricevute tramite il Portale ACN e i
Servizi NIS sono a divulgazione limitata e sono ristrette alloriginatore e ai destinatari
dellinformazione, nonché alle loro organizzazioni, alle loro terze parti e ai propri clienti. I
destinatari non possono condividere le informazioni ricevute al di fuori della propria
organizzazione, delle loro terze parti e dei propri clienti. La condivisione delle informazioni
ricevute nella propria organizzazione con le terze parti e con i clienti è limitata ai dati
strettamente necessari per lo svolgimento delle attività (principio del need-to-know).
Articolo 4
(Punto di contatto)
1. Il punto di contatto è una persona fisica designata dal soggetto NIS con il compito di curare
l'attuazione delle disposizioni del decreto NIS per conto del soggetto stesso. In particolare,
il punto di contatto accede al Portale ACN e ai Servizi NIS, effettua, per conto del soggetto,
la registrazione di cui allarticolo 7 del decreto NIS, e interloquisce, per conto del soggetto
NIS, con lAutorità nazionale competente NIS.
2. Le funzioni di punto di contatto possono essere svolte dal rappresentante legale del soggetto
NIS, da uno dei procuratori generali del soggetto NIS, censiti sul registro delle imprese di
cui allarticolo 8 della legge 29 dicembre 1993, n. 580, o da un dipendente del soggetto NIS
delegato dal rappresentante legale del soggetto medesimo. Laddove il punto di contatto,
nellespletamento delle proprie funzioni, si avvalga di personale esterno, restano comunque
ferme le disposizioni di cui al comma 1.
3. Qualora il soggetto sia parte di un gruppo di imprese, le funzioni di punto di contatto
possono essere svolte da un dipendente di unaltra impresa del gruppo che rientra
nellambito di applicazione del decreto NIS, delegato dal rappresentante legale del soggetto
stesso.
4. Qualora il soggetto NIS sia una pubblica amministrazione, le funzioni di punto di contatto
possono essere svolte da personale che presta servizio o dipendente di unaltra pubblica
amministrazione che rientra nellambito di applicazione del decreto NIS, previa
autorizzazione di quest'ultima ai sensi dell'articolo 53 del decreto legislativo 30 marzo 2001,
n. 165, delegato dal rappresentante legale del soggetto stesso.
5. Il punto di contatto riferisce direttamente al vertice gerarchico del soggetto NIS nonché agli
organi di amministrazione e direttivi del soggetto medesimo ai fini di quanto previsto dal
decreto NIS.
6 di 16
Agenzia per la Cybersicurezza Nazionale
6. Resta ferma, in ogni caso, la responsabilità degli organi di amministrazione e direttivi del
soggetto NIS ai sensi dellarticolo 23 del decreto NIS e delle persone fisiche ai sensi
dellarticolo 38 del medesimo decreto.
7. Nel caso di avvicendamento del punto di contatto, gli organi di amministrazione e direttivi
provvedono senza ingiustificato ritardo alla designazione del nuovo punto di contatto e
assicurano il suo censimento sul Portale ACN.
8. La designazione del punto di contatto da parte dei soggetti di cui allarticolo 1, comma 1,
della legge 28 giugno 2024, n. 90, che rientrano nellambito di applicazione del decreto NIS,
può soddisfare lobbligo di nomina e comunicazione del referente per la cybersicurezza di
cui allarticolo 8, comma 2, della medesima legge.
Articolo 5
(Sostituto punto di contatto)
1. Il sostituto punto di contatto è una persona fisica, distinta dal punto di contatto, designato
con le medesime modalità di questultimo ai sensi dellarticolo 4, a cui si applicano le
previsioni del citato articolo.
2. Il sostituto punto di contatto supporta il punto di contatto nellesercizio delle proprie
funzioni, può interloquire direttamente con lAutorità nazionale competente NIS e può
effettuare sulla piattaforma digitale le medesime azioni del punto di contatto, ad eccezione
della registrazione di cui allarticolo 7 del decreto NIS.
3. Il sostituto punto di contatto è designato entro il 31 maggio dellanno in cui il soggetto NIS
ha ricevuto comunicazione di inserimento nellelenco dei soggetti NIS.
4. Lobbligo di designazione del sostituto punto di contatto non si applica ai soggetti NIS che
versino nellimpossibilità materiale di effettuare tale adempimento, in quanto il punto di
contatto è lunica persona fisica operante nellorganizzazione.
Articolo 6
(Rappresentante nellUnione)
1. Per designare il proprio rappresentante NIS in Italia, i soggetti NIS di cui allarticolo 5,
comma 1, lettera b), del decreto NIS, trasmettono e aggiornano, dal primo settembre al trenta
novembre di ogni anno, al domicilio digitale dellAgenzia per la cybersicurezza nazionale
la documentazione indicata nella sezione dedicata del sito web. Con le medesime modalità,
tali soggetti comunicano il domicilio digitale per le conseguenti interlocuzioni con
lAutorità nazionale competente NIS.
2. LAutorità nazionale competente NIS comunica al domicilio digitale del soggetto
lautorizzazione, o il diniego, a procedere al censimento e alla registrazione entro trenta
giorni dalla ricezione della trasmissione di cui al comma 1.
3. Ove si renda necessario richiedere al soggetto integrazioni o informazioni, i termini di cui
al comma 2 sono sospesi e ricominciano a decorrere dalla data di ricevimento delle
integrazioni e delle informazioni che sono rese entro il termine di dieci giorni dalla richiesta.
Il tardivo riscontro alle richieste di cui al presente comma può essere motivo di diniego di
censimento e registrazione.
7 di 16
Agenzia per la Cybersicurezza Nazionale
4. Fermo restando quanto previsto dallarticolo 4, comma 2, i soggetti di cui al comma 1 del
presente articolo possono delegare le funzioni di punto di contatto:
a) al rappresentante NIS stesso, qualora sia una persona fisica;
b) al rappresentante legale, a uno dei procuratori generali o a un dipendente del
rappresentante NIS stesso, qualora questultimo sia una persona giuridica.
Articolo 7
(Referente CSIRT e sostituti)
1. Il referente CSIRT è una persona fisica designata dal Punto di Contatto, a partire dal 20
novembre ed entro il 31 dicembre 2025, tramite la dedicata procedura telematica resa
disponibile dal Portale ACN.
2. Il referente CSIRT ha il compito di interloquire con lo CSIRT Italia, di cui allarticolo 2,
comma 1, lettera i) del decreto NIS, ed effettuare le notifiche di cui agli articoli 25 e 26 del
medesimo decreto per conto del soggetto NIS.
3. Al fine di assicurare il tempestivo svolgimento dei compiti del referente CSIRT, con
particolare riferimento alla notifica degli incidenti significativi di cui allarticolo 25 del
decreto NIS e relativi seguiti, con le medesime modalità di cui al comma 1, possono essere
designati uno o più sostituti referente CSIRT.
4. I sostituti referente CSIRT, ove designati, supportano il referente CSIRT nellesercizio delle
funzioni di cui al comma 2 e possono svolgerle per suo conto.
5. Il referente CSIRT e i suoi sostituti, ove designati, possiedono almeno competenze di base
in materia di sicurezza informatica e di gestione di incidenti informatici, nonché una
conoscenza approfondita dei sistemi informativi e di rete del soggetto per conto del quale
operano.
Capo II
Censimento e associazione delle utenze
Articolo 8
(Censimento degli utenti)
1. Gli utenti si autenticano sul Portale ACN tramite CIE o SPID personale.
2. Gli utenti completano la propria anagrafica fornendo le informazioni seguenti, se non già
condivise tramite CIE o SPID:
a) nome e cognome;
b) codice fiscale;
c) luogo e data di nascita;
d) cittadinanza;
e) Paese di residenza e, ove richiesto, di domicilio;
f) indirizzo della sede prevalente di servizio, aziendale o professionale;
g) indirizzo di posta elettronica ordinaria, preferibilmente individuale, nonché di servizio,
aziendale o professionale;
8 di 16
Agenzia per la Cybersicurezza Nazionale
h) ove disponibile, un indirizzo di posta elettronica certificata, preferibilmente individuale,
nonché di servizio, aziendale o professionale;
i) numero di telefono, preferibilmente individuale, nonché di servizio, aziendale o
professionale;
j) ove disponibile, un numero alternativo di telefono, preferibilmente individuale di
servizio, aziendale o professionale;
k) denominazione e codice fiscale dellorganizzazione di appartenenza prevalente.
3. Qualora, ai sensi della normativa vigente, un utente non possa disporre di credenziali SPID
o di CIE, può autenticarsi con credenziali personali. La procedura per la richiesta delle
credenziali personali è pubblicata nella sezione dedicata del sito web. Tali utenti forniscono
un codice di identificazione nazionale in luogo del codice fiscale di cui al comma 2, lettera
c).
Articolo 9
(Associazione dellutenza del punto di contatto e del sostituto al soggetto NIS)
1. Il punto di contatto, censito sul Portale ACN, effettua lassociazione della sua utenza con il
soggetto che lo ha designato attraverso la digitazione del suo codice fiscale o del codice
dellindice dei domicili digitali delle pubbliche amministrazioni e dei gestori di pubblici
servizi (IPA).
2. Lutente:
a) verifica la denominazione nonché lindirizzo, il domicilio digitale e i recapiti della sede
legale del soggetto visualizzati dal Portale ACN;
b) indica se è:
1) rappresentante legale del soggetto;
2) procuratore generale del soggetto;
3) delegato dal rappresentante legale del soggetto;
c) indica il codice fiscale dellorganizzazione di cui è dipendente qualora diverso dal
soggetto al quale si sta associando;
d) indica il ruolo svolto presso il soggetto.
3. Nel caso di cui al comma 2, lettera b), numero 3, lutente inserisce sul Portale ACN la delega
rilasciata a suo nome dal soggetto NIS, dichiarando che la stessa lo autorizza ad accedere al
Portale ACN stesso e ai Servizi NIS per conto del medesimo soggetto.
4. Lassociazione dellutenza del punto di contatto è sottoposta alla convalida del soggetto
NIS, secondo la procedura telematica indicata nella richiesta inviata al domicilio digitale di
questultimo.
5. Al termine del processo di censimento e associazione, il soggetto riceve al suo domicilio
digitale la comunicazione di conclusione del processo stesso.
6. La convalida dellassociazione dellutenza del punto di contatto ad un soggetto NIS
determina la dissociazione, se presente, dellutenza del punto di contatto precedentemente
associata.
7. Il sostituto punto di contatto effettua, su invito del punto di contatto, lassociazione con le
medesime modalità del punto di contatto di cui al presente articolo.
9 di 16
Agenzia per la Cybersicurezza Nazionale
Articolo 10
(Associazione delle utenze al soggetto NIS)
1. Al punto di contatto è data facoltà di invitare ulteriori utenti con il ruolo di operatore e, al
più, un utente con il ruolo di segreteria.
2. Gli utenti censiti sul Portale ACN che non sono stati designati e associati quali punti di
contatto o sostituti punti di contatto:
a) sono associati al soggetto NIS, per conto del quale operano, su indicazione e invito
del punto di contatto;
b) non possono effettuare azioni sul Portale ACN che determinano la trasmissione di
comunicazioni, inerenti il perfezionamento degli adempimenti di cu al decreto NIS,
al domicilio digitale del soggetto NIS o allAutorità nazionale competente NIS.
3. Tutti gli utenti possono:
a) annullare la propria associazione con il soggetto NIS;
b) disabilitare la propria utenza.
4. Il punto di contatto, il sostituto punto di contatto, e la segreteria possono ridurre il proprio
ruolo ad operatore.
Capo III
Registrazione dei soggetti NIS e elaborazione dellelenco dei soggetti NIS
Articolo 11
(Registrazione)
1. Dal 1° gennaio al 28 febbraio di ogni anno, gli utenti compilano, tramite il “Servizio NIS/
Dichiarazione”, la dichiarazione per il soggetto per cui operano ai fini della sua
registrazione, assicurandosi che le informazioni fornite siano corrette e aggiornate.
2. In particolare, lutente:
a) qualora il soggetto non sia unimpresa autonoma, indica se il soggetto è parte di un
gruppo di imprese e, in tal caso, indica se il soggetto è la capo gruppo ovvero indica il
codice fiscale della capo gruppo;
b) qualora il soggetto non sia unimpresa autonoma, elenca i soggetti NIS di cui è a
conoscenza che sono imprese collegate nei confronti delle quali soddisfi almeno uno
dei criteri di cui allarticolo 3, comma 10, del decreto NIS, indicando il codice fiscale
di tali imprese e quale dei criteri è soddisfatto;
c) qualora il soggetto non sia unimpresa autonoma, elenca le imprese collegate che
soddisfano nei suoi confronti almeno uno dei criteri di cui allarticolo 3, comma 10, del
decreto NIS, indicando il codice fiscale di tali imprese e quale dei criteri è soddisfatto,
ai fini della loro identificazione come soggetti NIS ai sensi del medesimo comma;
d) elenca i codici ATECO che descrivono lattività del soggetto;
e) indica le normative settoriali dellUnione europea citate negli allegati I e II del decreto
NIS per definire le tipologie di soggetto che rientrano nellambito di applicazione del
decreto NIS;
10 di 16
Agenzia per la Cybersicurezza Nazionale
f) indica i valori del fatturato e del bilancio nonché del numero di dipendenti al fine di
determinare lappartenenza del soggetto NIS alla categoria delle medie o grandi imprese
ai sensi della raccomandazione 2003/361/CE. Le pubbliche amministrazioni possono
non indicare i valori del fatturato e del bilancio;
g) elenca le tipologie di soggetto di cui agli allegati I, II, III e IV del decreto NIS a cui il
soggetto è riconducibile.
3. Fermo restando lobbligo di registrazione per i soggetti di cui allarticolo 3, comma 10, del
decreto NIS, lAutorità nazionale competente NIS informa le imprese di cui al comma 2,
lettera c), del presente articolo che nei loro confronti si sono verificati i presupposti di cui
al citato articolo del decreto NIS.
4. Qualora il soggetto non sia una impresa autonoma, il calcolo del fatturato e del bilancio
nonché del numero di dipendenti di cui al comma 2, lettera f), del presente articolo è
effettuato ai sensi dellarticolo 6, paragrafo 2, dellallegato alla raccomandazione
2003/361/CE.
5. Qualora in fase di compilazione della dichiarazione siano rilevate incongruenze, queste sono
segnalate allutente che deve procedere a:
a) modificare la dichiarazione correggendo le informazioni errate o incomplete;
b) fornire ulteriori elementi informativi per giustificare lincongruenza rilevata.
6. Al termine della compilazione della dichiarazione, allutente è rimessa la conferma della
valutazione preliminare fornita automaticamente dalla piattaforma, sulla base dei criteri di
cui agli articoli 3 e 6 del decreto NIS, fondata sulla base delle informazioni fornite ai sensi
del presente articolo.
7. Gli utenti designati quali punti di contatto confermano, ai sensi del decreto del Presidente
della Repubblica del 28 dicembre 2000, n. 445, le informazioni fornite e le trasmettono
telematicamente tramite il “Servizio NIS/Dichiarazione” allAutorità nazionale competente
NIS. Copia di tali informazioni, per ricevuta, è inviata al domicilio digitale del soggetto con
lavvertenza che tale dichiarazione potrà essere sottoposta alle verifiche di coerenza di cui
allarticolo 14.
8. Decorsi dieci giorni solari dalla sottomissione della dichiarazione questa si intende
definitivamente acquisita e non ulteriormente modificabile dallutente.
9. Le dichiarazioni sottomesse o modificate oltre i termini di cui al comma 1 sono considerate
tardive, salvo che il ritardo sia determinato da documentate criticità tecnico-operative non
imputabili allutente.
10. Ai soggetti già inseriti nellelenco dei soggetti NIS, allavvio della registrazione per lanno
2026, viene presentata una bozza di dichiarazione precompilata sulla base delle
informazioni trasmesse nel corso dellanno solare precedente tramite i Servizi NIS.
Articolo 12
(Clausola di salvaguardia)
1. Nel caso in cui lutente ritenga che il calcolo effettuato ai sensi dellarticolo 11, comma 4,
non sia proporzionato tenuto conto dei criteri di cui al decreto del Presidente del Consiglio
dei ministri di cui allarticolo 40, comma 1, lettera a), del decreto NIS, nel corso della
11 di 16
Agenzia per la Cybersicurezza Nazionale
registrazione può chiedere lapplicazione della clausola di salvaguardia di cui allarticolo 3,
comma 12, del decreto NIS.
2. Ai fini della richiesta di cui al comma 1, tramite il “Servizio NIS/Dichiarazione”, lutente
fornisce gli elementi di valutazione corrispondenti ai criteri di cui al decreto del Presidente
del Consiglio dei ministri di cui allarticolo 40, comma 1, lettera a), del decreto NIS
allAutorità nazionale competente NIS, che li condivide con le Autorità di settore interessate
ai fini delle valutazioni di cui allarticolo 11, comma 4, lettera c), del medesimo decreto.
3. Al soggetto NIS è fornito riscontro con la comunicazione dellAutorità nazionale
competente NIS ai sensi dellarticolo 7, comma 3, del decreto NIS.
Articolo 13
(Individuazione da parte dellAutorità nazionale competente NIS)
1. I soggetti che ricevono una notifica di individuazione da parte dellAutorità nazionale
competente NIS, su proposta delle Autorità di settore, ai sensi dellarticolo 3, comma 13,
del decreto NIS, procedono al censimento e alla registrazione alla stregua delle disposizioni
del presente capo.
2. In fase di registrazione, tali soggetti prendono visione e confermano gli elementi presenti
nella notifica di cui al comma 1.
Articolo 14
(Verifiche di coerenza)
1. Le verifiche di coerenza delle informazioni contenute nelle dichiarazioni sono svolte, a
campione, dallAutorità nazionale competente NIS dintesa con le Autorità di settore e non
sollevano il soggetto NIS dallobbligo del rispetto dei termini duso di cui allarticolo 3
della presente determinazione e, in particolare, dalla responsabilità per le dichiarazioni
mendaci di cui al comma 5 del medesimo articolo.
2. Nei casi di cui al comma 1, lAutorità nazionale competente NIS fornisce riscontro al
soggetto entro trenta giorni dalla presentazione della dichiarazione tramite il “Servizio NIS/
Dichiarazione”. Il predetto termine può essere prorogato dallAutorità nazionale competente
NIS, per una sola volta e fino ad un massimo di ulteriori venti giorni, qualora sia necessario
svolgere approfondimenti complessi riguardanti la coerenza delle informazioni contenute
nella dichiarazione.
3. Ove si renda necessario richiedere al soggetto integrazioni, informazioni aggiuntive o
modifiche della dichiarazione, i termini di cui al comma 2 sono sospesi e ricominciano a
decorrere dalla data di ricevimento delle integrazioni e delle informazioni che sono rese
entro il termine di dieci giorni dalla richiesta. Il tardivo riscontro alle richieste di cui al
presente comma può essere motivo di rigetto della dichiarazione.
4. Al termine delle verifiche di coerenza delle informazioni contenute nelle dichiarazioni,
lAutorità nazionale competente comunica, tramite i Servizi NIS al domicilio digitale del
soggetto NIS:
a) lesito positivo della verifica;
12 di 16
Agenzia per la Cybersicurezza Nazionale
b) lesito negativo della verifica.
5. La comunicazione di cui al comma 4, lettera b), non solleva il soggetto NIS dallobbligo di
registrazione di cui allarticolo 7, comma 1, del decreto NIS.
Articolo 15
(Elaborazione dellelenco dei soggetti NIS e comunicazioni)
1. Lelenco dei soggetti NIS di cui allarticolo 7, comma 2, del decreto NIS è elaborato
dallAutorità nazionale competente NIS sulla base delle informazioni trasmesse dai soggetti
NIS ai sensi del capo III della presente determinazione e delle verifiche svolte dalle Autorità
di settore ai sensi dellarticolo 11, comma 4, lettera a), del medesimo decreto che, ove
necessario, possono proseguire anche successivamente allelaborazione dellelenco dei
soggetti NIS.
2. Ai sensi del comma 1, il processo di registrazione di cui alla presente determinazione
costituisce la fase endoprocedimentale del procedimento di costituzione dellelenco dei
soggetti NIS.
3. Ai sensi dellarticolo 7, comma 3, del decreto NIS, lAutorità nazionale competente NIS
comunica ai soggetti registrati l'inserimento, o meno, nell'elenco dei soggetti NIS. Ai
soggetti inseriti nellelenco dei soggetti NIS e ai loro punti di contatto viene, altresì,
comunicato un codice identificativo univoco, per il soggetto NIS, al fine di facilitare le
interlocuzioni con lAutorità nazionale competente NIS.
Capo IV
Aggiornamento delle informazioni
Articolo 16
(Processo per laggiornamento annuale delle informazioni)
1. Dal 15 aprile al 31 maggio di ogni anno, gli utenti aggiornano, tramite il “Servizio NIS/
Aggiornamento annuale informazioni”, le informazioni per conto del soggetto per cui
operano, assicurandone la correttezza.
2. Per tutti i soggetti NIS:
a) il punto di contatto si assicura che i propri dati anagrafici e di contatto siano corretti e
aggiornati. Ove prevista dallarticolo 4, il punto di contatto si assicura che la delega
conferitagli dal rappresentante legale del soggetto sia corretta, aggiornata e conforme a
quanto previsto dallarticolo medesimo;
b) il sostituto punto di contatto si assicura che i propri dati anagrafici e di contatto siano
corretti e aggiornati. Ove prevista delega il sostituto punto di contatto si assicura che la
delega conferitagli dal rappresentante legale del soggetto sia corretta, aggiornata e
conforme a quanto previsto dallarticolo medesimo;
c) la segreteria, ove presente, si assicura che i propri dati anagrafici e di contatto siano
corretti e aggiornati.
3. Per tutti i soggetti NIS, gli utenti verificano la correttezza e laggiornamento:
13 di 16
Agenzia per la Cybersicurezza Nazionale
a) dei dati anagrafici e di contatto del soggetto NIS. Tali informazioni includono almeno
il codice fiscale, la denominazione, lindirizzo della sede legale, lindicazione del
rappresentante legale, lelenco dei procuratori generali, il numero di telefono, il
domicilio digitale e un indirizzo di posta elettronica ordinaria funzionale;
b) dellelenco dei componenti degli organi di amministrazione e direttivi, quali persone
fisiche responsabili ai sensi dellarticolo 38, comma 5, del decreto NIS;
c) ove applicabile, dellelenco dei servizi che rientrano nellambito di applicazione della
direttiva 2022/2555 che il soggetto NIS offre nellUE e indicando in quali Stati membri;
d) dello spazio di indirizzamento IP pubblico e dei nomi di dominio in uso o nella
disponibilità del soggetto NIS, eventualmente distinto a livello di articolazioni di primo
livello;
e) dellelenco degli accordi di condivisione delle informazioni
f) dei dati identificativi del referente CSIRT e degli eventuali sostituti.
4. Per i soggetti NIS di cui allarticolo 7, comma 5, del decreto NIS gli utenti verificano la
correttezza e laggiornamento dellelenco delle sedi del soggetto NIS nellUnione,
indicandone lindirizzo.
5. Per i soggetti NIS di cui allarticolo 5, comma 1, lettera b), del decreto NIS, che hanno
designato il proprio rappresentante NIS in Italia ai sensi dellarticolo 6 della presente
determinazione, gli utenti verificano che dati anagrafici e di contatto del rappresentante NIS
siano corretti e aggiornati.
6. Qualora ritenuto opportuno, è data la facoltà di descrivere la struttura organizzativa
dellorganizzazione, indicandone la suddivisione in articolazioni di primo livello. Tale
descrizione della struttura organizzativa potrà essere impiegata per leventuale conferimento
di informazioni di dettaglio relative alle articolazioni di primo livello.
7. Gli utenti designati quali punti di contatto confermano, ai sensi del decreto del Presidente
della Repubblica del 28 dicembre 2000, n. 445, le informazioni fornite e le trasmettono
telematicamente tramite il “Servizio NIS/Aggiornamento annuale informazioni”
allAutorità nazionale competente NIS. Copia di tali informazioni, per ricevuta, è inviata al
domicilio digitale del soggetto.
8. La modifica, confermata da punto di contatto, dellindicazione del rappresentante legale o
dellelenco dei procuratori generali del soggetto NIS è sottoposta alla convalida del soggetto
medesimo, secondo la procedura telematica indicata nella richiesta inviata al domicilio
digitale di questultimo.
9. Le modifiche dei dati anagrafici e di contatto del soggetto NIS sono trasmesse, per ricevuta,
al domicilio digitale di questultimo.
10. Le modifiche dei dati anagrafici e di contatto degli utenti sono trasmesse, per ricevuta,
allindirizzo di posta elettronica certificata indicata dallutente stesso o, in subordine,
allindirizzo di posta elettronica ordinaria indicata dallutente stesso.
11. In fase di prima applicazione, fermo restando quanto previsto dallarticolo 35, comma 3,
lettera c), e dallarticolo 42, comma 1, lettera c), del decreto NIS, in caso di registrazione
tardiva, il termine per completare laggiornamento annuale è comunicato di volta in volta
dallAutorità nazionale competente NIS.
14 di 16
Agenzia per la Cybersicurezza Nazionale
12. Il comma 3, lettera b), del presente articolo e larticolo 17 non si applicano ai soggetti che
rientrano nellambito di applicazione del decreto NIS e del Regolamento UE 2022/2554
(DORA).
Articolo 17
(Elencazione degli organi di amministrazione e direttivi)
1. Ai fini dellarticolo 16, comma 3, lettera b), tramite il “Servizio NIS/Aggiornamento
annuale informazioni”, gli utenti elencano i codici fiscali delle persone fisiche che
compongono gli organi di amministrazione e direttivi, indicandone lindirizzo di posta
elettronica certificata.
2. Le informazioni di cui al comma 1 sono confermate dal punto di contatto.
3. Ai fini dellarticolo 7, comma 4, lettera c), del decreto NIS, le persone fisiche appartenenti
agli organi di amministrazione e direttivi del soggetto NIS accettano tale indicazione
accedendo al Portale ACN, secondo la procedura telematica indicata nella richiesta inviata
a loro indirizzo di posta elettronica certificata di cui al comma 1.
Articolo 18
(Processo per laggiornamento continuo delle informazioni)
1. A seguito del perfezionamento dellaggiornamento annuale, laddove siano sopravvenute
modifiche alle informazioni trasmesse ai sensi dellarticolo 16, tramite il “Servizio NIS/
Aggiornamento continuo informazioni” gli utenti forniscono le informazioni aggiornate per
conto del soggetto per cui operano, assicurandone la correttezza.
2. Laggiornamento continuo delle informazioni è possibile fino al 14 aprile di ogni anno
successivo alla ricezione della comunicazione di cui allarticolo 7, comma 3, lettera a), del
decreto NIS.
3. Gli utenti designati quali punti di contatto confermano, ai sensi del decreto del Presidente
della Repubblica del 28 dicembre 2000, n. 445, le informazioni fornite e le trasmettono
telematicamente tramite il “Servizio NIS/Aggiornamento continuo informazioni”
allAutorità nazionale competente NIS. Copia di tali informazioni, per ricevuta, è inviata al
domicilio digitale del soggetto.
4. La modifica, confermata da punto di contatto, dellindicazione del rappresentante legale o
dellelenco dei procuratori generali del soggetto NIS è sottoposta alla convalida del soggetto
medesimo, secondo la procedura telematica indicata nella richiesta inviata al domicilio
digitale di questultimo.
5. Le modifiche dei dati anagrafici e di contatto del soggetto NIS sono trasmesse, per ricevuta,
al domicilio digitale di questultimo.
6. Le modifiche dei dati anagrafici e di contatto degli utenti sono trasmesse, per ricevuta,
allindirizzo di posta elettronica certificata indicata dallutente stesso o, in subordine,
allindirizzo di posta elettronica ordinaria indicata dallutente stesso.
15 di 16
Agenzia per la Cybersicurezza Nazionale
Capo V
Disposizioni finali
Articolo 19
(Disposizioni finanziarie)
1. Agli oneri derivanti dalla presente determinazione, a carico dellAutorità nazionale
competente NIS e delle Autorità di settore, si provvede, rispettivamente, con le risorse di
cui agli articoli 10 e 11 del decreto NIS.
Articolo 20
(Pubblicità)
1. La presente determinazione è pubblicata sul sito web e sui siti web istituzionali delle
Autorità di settore NIS e ne sarà data, altresì, comunicazione tramite pubblicazione nella
Gazzetta Ufficiale della Repubblica italiana.
Articolo 21
(Applicazione)
1. La presente determinazione aggiorna e sostituisce la determinazione ACN n. 333017 del 22
settembre 2025.
2. Per quanto non previsto dalla presente determinazione, si applicano le disposizioni del
decreto NIS.
3. La presente determinazione si applica a decorrere dal 31 dicembre 2025.
Roma, data del protocollo
IL DIRETTORE GENERALE
Bruno Frattasi
Bruno
Frattasi
18.12.2025
18:12:36
GMT+01:00
16 di 16

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
# Integrazione analisi `docs/nis2/` → NIS2 Agile
> Data: 2026-05-29 (CEST) · Versione: v1.7.0 · Stato: **✅ DEPLOYATO ED ESEGUITO IN PRODUZIONE (Hetzner)**
## ✅ Eseguito in produzione (2026-05-29 ~17:06 CEST)
- **Codice**: live su `/var/www/nis2-agile` (stesso filesystem del container dev via bind mount → nessuno scp necessario). 5 nuovi endpoint verificati live (HTTP 401 auth, routing OK).
- **Migrazioni DB 020/021/022**: applicate e verificate (colonne assets/incidents + tabella `incident_pir` create).
- **Ingest KB**: **287 chunk** normativi indicizzati in Qdrant `nis2_kb` scope SYSTEM (171 NIS2 + 77 CER + 25 Det.333017 + 9 Det.164179 + 5 Ambiti). Retrieval verificato (query "preallarme CSIRT" → Direttiva NIS2; "settori alta criticità" → Ambiti Allegati I/II).
- **Backup**: `/root/backup_pre_v170_20260529_165447.sql`.
### ⚠️ Azione consigliata residua (richiede conferma — recreate container)
**Qdrant IP drift**: il container `nis2-qdrant` non ha IP statico in `docker-compose.yml` ed è driftato da `172.21.0.5``172.21.0.3`. Fallback in `VectorService.php` aggiornato a `.3` (live). Per evitare ricorrenze: in `docker/docker-compose.yml` assegnare `ipv4_address` statico al servizio `qdrant` e allineare `QDRANT_URL`, poi `docker compose up -d --force-recreate qdrant app`. NB: nota anche che `kb_uploaded_documents` non esiste su questo DB (migrazioni KB 012-014 non applicate qui) → il tracking MySQL dei doc KB è saltato (best-effort), ma la ricerca RAG legge da Qdrant e funziona.
---
Integrazione del materiale di analisi (mockup HTML + testi normativi PDF) nel prodotto NIS2 Agile.
Tutto il codice è scritto e lint-clean (`php -l` / `node --check`). **Le migrazioni DB e l'ingest KB
NON sono ancora stati eseguiti** (questo container dev non raggiunge il DB/Qdrant di produzione).
---
## Cosa è stato implementato
### Fase 1 — Asset Relevance Scoring NIS2 (GV.OC-04)
Metodologia di scoring 0-100 su 6 criteri pesati, soglia rilevanza ≥40, classi critico/alto/medio/basso/trascurabile.
- `docs/sql/020_asset_relevance.sql` — colonne `relevance_score`, `relevance_criteria` (JSON), `relevance_class`, `is_nis2_relevant`, `relevance_assessed_at/by` + indice
- `application/services/AssetScoringService.php` — logica pura + griglia ufficiale in costante
- `application/controllers/AssetController.php``GET scoringGrid`, `POST {id}/score`, `GET relevantSystems`, filtro `nis2_relevant`
- `public/assets.html` — colonna "Rilevanza NIS2" + modale di valutazione a 6 criteri con anteprima punteggio
- `public/js/api.js``getScoringGrid`, `scoreAsset`, `listRelevantSystems`, `deleteAsset`
- Verifica: esempio ERP del mockup = **91/100 → critico**
### Fase 2 — Tassonomia incidenti (Determina ACN 164179/2025)
- `docs/sql/021_incident_nis2_taxonomy.sql` — colonne `nis2_incident_type` ENUM(IS-1..IS-4), `entity_obligation` ENUM(essential/important)
- `application/controllers/IncidentController.php``create()` deriva il regime (Allegato 3 essenziali / Allegato 4 importanti) e blocca IS-4 per gli importanti
- `application/services/AIService.php``classifyIncident()` cita le fonti e restituisce `nis2_incident_type` + `notification_basis`
### Fase 3 — Post-Incident Review + metriche TTD/TTC/TTR
- `docs/sql/022_incident_metrics_pir.sql` — timestamp di fase (`triaged_at`, `contained_at`, `eradicated_at`, `recovered_at`) + tabella `incident_pir` (5-Whys, metriche, costo, lesson learned)
- `IncidentController.php``GET {id}/metrics`, `GET {id}/pir`, `POST {id}/pir`; `update()` timbra i timestamp di fase al cambio stato
- `public/js/api.js``getIncidentMetrics`, `getIncidentPir`, `saveIncidentPir`, `aiClassifyIncident`
### Fase 4 — Layer mapping NIST CSF 2.0 (reference, non invasivo)
- `application/controllers/AuditController.php``GET nistCsfMapping`: 43 controlli NIST CSF 2.0 → NIS2 Art.21/23 → modulo. **Nessuna migrazione**, nessuna modifica all'assessment esistente.
### Fonti normative certe (richiesta esplicita: AI + help citano fonti certe)
- `application/config/nis2_sources.php`**registry canonico citabile** (Dir. 2022/2555, Dir. 2022/2557, D.Lgs. 138/2024, Determina ACN 164179/2025, Determina ACN 333017/2025, Ambiti NIS2)
- `application/services/AIService.php``authoritativeSourcesBlock()` iniettato nei system prompt (default, RAG, classifyIncident): impone di citare le fonti e vieta riferimenti inventati
- `public/js/help.js` — riferimenti normativi italiani aggiunti a incidenti e asset
- `scripts/ingest-nis2-sources.php` — indicizza i 5 PDF normativi nella KB (Qdrant `nis2_kb`, scope SYSTEM) per il grounding RAG
### Report
- `ReportService::generateRelevantSystemsRegister()` + `GET /api/audit/relevantSystemsRegister` — registro formale "Sistemi Rilevanti NIS2" (GV.OC-04) HTML stampabile con citazioni.
---
## Deploy su Hetzner (da eseguire, in ordine — CHIEDERE CONFERMA UTENTE)
```bash
# 0) Backup pre-migrazione
ssh -i docs/credentials/hetzner_key root@135.181.149.254
mysqldump nis2_agile_db assets incidents > /root/backup_pre_v170_$(date +%F).sql
# 1) Deploy codice (bind mount: PHP live; verificare path reale di produzione)
cd /var/www/nis2-agile && git pull origin main # dopo push su Gitea
# 2) Migrazioni DB (additive, idempotenti). NB: usare host MySQL, non docker exec nis2-db
mysql -h localhost nis2_agile_db -e "source /var/www/nis2-agile/docs/sql/020_asset_relevance.sql"
mysql -h localhost nis2_agile_db -e "source /var/www/nis2-agile/docs/sql/021_incident_nis2_taxonomy.sql"
mysql -h localhost nis2_agile_db -e "source /var/www/nis2-agile/docs/sql/022_incident_metrics_pir.sql"
# 3) Ingest fonti normative nella KB (richiede Qdrant + Voyage attivi)
docker exec -i nis2-app php /var/www/nis2-agile/scripts/ingest-nis2-sources.php --dry-run # verifica
docker exec -i nis2-app php /var/www/nis2-agile/scripts/ingest-nis2-sources.php # esegui
# 4) Smoke test
curl -s https://nis2.agile.software/api/assets/scoringGrid -H "Authorization: Bearer <jwt>" | head
```
### Rollback
Ogni `.sql` contiene la sezione ROLLBACK in coda. Per la KB: cancellare i chunk scope=SYSTEM/source=normativa o ripristinare la collection.
---
## Note
- **Phase 4** è volutamente reference-only (nessuna tabella/migrazione) per non toccare l'assessment Art.21 consolidato.
- I PDF normativi restano in `docs/nis2/*.pdf` come libreria sorgente referenziata da `nis2_sources.php`.
- I file `*copy.html` e `incidente_r00/` dei mockup non sono stati usati (duplicati/superati).

2015
docs/nis2/assets.html Normal file

File diff suppressed because it is too large Load Diff

1481
docs/nis2/dashboard.html Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,450 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Diagramma Topologia Rete - NIS2</title>
<style>
:root {
--bg-primary: #ffffff;
--text-primary: #1a1a1a;
--text-secondary: #666666;
--border-color: #cccccc;
--accent-primary: #0066cc;
--zone-dmz: #fff3cd;
--zone-internal: #d1ecf1;
--zone-secure: #d4edda;
--zone-external: #f8d7da;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
background-color: #f5f5f5;
color: var(--text-primary);
padding: 20px;
}
.document-container {
max-width: 297mm;
margin: 0 auto;
background-color: var(--bg-primary);
padding: 30px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
}
.header {
text-align: center;
border-bottom: 3px solid var(--text-primary);
padding-bottom: 20px;
margin-bottom: 30px;
}
.logo {
font-size: 24px;
font-weight: bold;
color: var(--accent-primary);
margin-bottom: 10px;
}
.doc-title {
font-size: 20px;
font-weight: bold;
margin: 15px 0;
}
.doc-subtitle {
font-size: 12px;
color: var(--text-secondary);
}
.classification {
text-align: center;
font-size: 11px;
font-weight: bold;
color: #d32f2f;
background-color: #ffebee;
padding: 10px;
border: 2px solid #d32f2f;
margin-bottom: 20px;
}
.diagram-container {
width: 100%;
background-color: #fafafa;
border: 2px solid var(--border-color);
padding: 30px;
margin: 20px 0;
position: relative;
}
.zone {
border: 2px dashed #333;
padding: 20px;
margin: 15px 0;
border-radius: 8px;
position: relative;
}
.zone-label {
position: absolute;
top: -12px;
left: 20px;
background-color: var(--bg-primary);
padding: 0 10px;
font-weight: bold;
font-size: 14px;
}
.zone-external {
background-color: var(--zone-external);
border-color: #721c24;
}
.zone-dmz {
background-color: var(--zone-dmz);
border-color: #856404;
}
.zone-internal {
background-color: var(--zone-internal);
border-color: #004085;
}
.zone-secure {
background-color: var(--zone-secure);
border-color: #155724;
}
.device {
background-color: white;
border: 2px solid #333;
border-radius: 6px;
padding: 15px;
margin: 10px;
display: inline-block;
min-width: 150px;
text-align: center;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.device-icon {
font-size: 32px;
margin-bottom: 8px;
}
.device-name {
font-weight: bold;
font-size: 12px;
margin-bottom: 4px;
}
.device-ip {
font-size: 10px;
color: var(--text-secondary);
}
.device-code {
font-size: 9px;
color: var(--accent-primary);
font-family: monospace;
}
.connection {
text-align: center;
font-size: 24px;
color: #333;
margin: 10px 0;
}
.legend {
margin-top: 30px;
padding: 20px;
background-color: #f9f9f9;
border: 1px solid var(--border-color);
border-radius: 6px;
}
.legend-title {
font-weight: bold;
font-size: 14px;
margin-bottom: 15px;
color: var(--accent-primary);
}
.legend-item {
display: inline-block;
margin: 5px 15px 5px 0;
font-size: 11px;
}
.legend-color {
display: inline-block;
width: 20px;
height: 20px;
border: 1px solid #333;
margin-right: 8px;
vertical-align: middle;
}
.info-box {
background-color: #e7f3ff;
border-left: 4px solid var(--accent-primary);
padding: 15px;
margin: 20px 0;
font-size: 11px;
}
.info-box-title {
font-weight: bold;
margin-bottom: 8px;
}
.no-print {
position: fixed;
top: 20px;
right: 20px;
z-index: 1000;
}
.btn-print {
padding: 12px 24px;
background-color: var(--accent-primary);
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
.btn-print:hover {
background-color: #0052a3;
}
.grid-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 15px;
justify-items: center;
}
@media print {
body {
background-color: white;
padding: 0;
}
.document-container {
box-shadow: none;
}
.no-print {
display: none;
}
}
</style>
</head>
<body>
<div class="no-print">
<button class="btn-print" onclick="window.print()">🖨️ Stampa PDF</button>
</div>
<div class="document-container">
<div class="classification">
⚠️ DOCUMENTO RISERVATO - SOLO PERSONALE AUTORIZZATO ⚠️
</div>
<div class="header">
<div class="logo">ACME CORPORATION S.p.A.</div>
<div class="doc-title">Diagramma Topologia Rete Fisica</div>
<div class="doc-subtitle">Versione 3.2 | Data: 15 Febbraio 2024 | Approvato da: CISO</div>
<div class="doc-subtitle">Documento ID.AM-03 - Requisito NIS2 (Solo Soggetti Essenziali)</div>
</div>
<div class="info-box">
<div class="info-box-title">📋 Informazioni Documento</div>
<strong>Codice:</strong> NET-TOPO-PHY-v3.2 |
<strong>Ultima Modifica:</strong> 15/02/2024 |
<strong>Prossima Revisione:</strong> 15/05/2024 |
<strong>Classificazione:</strong> RISERVATO
</div>
<div class="diagram-container">
<!-- ZONA INTERNET -->
<div class="zone zone-external">
<div class="zone-label">🌐 INTERNET / ZONA ESTERNA</div>
<div style="text-align: center;">
<div class="device">
<div class="device-icon">🌍</div>
<div class="device-name">Internet</div>
<div class="device-ip">Pubblico</div>
</div>
</div>
</div>
<div class="connection">⬇️ Connessione Fibra 1Gbps</div>
<!-- ZONA DMZ -->
<div class="zone zone-dmz">
<div class="zone-label">🛡️ DMZ (DeMilitarized Zone)</div>
<div class="grid-container">
<div class="device">
<div class="device-icon">🔥</div>
<div class="device-name">Firewall Perimetrale</div>
<div class="device-ip">10.10.0.1</div>
<div class="device-code">HW-NET-015</div>
</div>
<div class="device">
<div class="device-icon">🌐</div>
<div class="device-name">Web Server Pubblico</div>
<div class="device-ip">10.10.2.45</div>
<div class="device-code">HW-SRV-023</div>
</div>
<div class="device">
<div class="device-icon">📧</div>
<div class="device-name">Mail Gateway</div>
<div class="device-ip">10.10.2.50</div>
<div class="device-code">HW-SRV-028</div>
</div>
<div class="device">
<div class="device-icon">🔐</div>
<div class="device-name">VPN Gateway</div>
<div class="device-ip">10.10.2.60</div>
<div class="device-code">HW-NET-018</div>
</div>
</div>
</div>
<div class="connection">⬇️ VLAN Segmentation</div>
<!-- ZONA INTERNA -->
<div class="zone zone-internal">
<div class="zone-label">🏢 RETE INTERNA</div>
<div class="grid-container">
<div class="device">
<div class="device-icon">🔀</div>
<div class="device-name">Core Switch</div>
<div class="device-ip">10.10.1.1</div>
<div class="device-code">HW-NET-022</div>
</div>
<div class="device">
<div class="device-icon">💻</div>
<div class="device-name">Workstation (x150)</div>
<div class="device-ip">10.20.x.x</div>
<div class="device-code">VLAN 20</div>
</div>
<div class="device">
<div class="device-icon">🖨️</div>
<div class="device-name">Stampanti</div>
<div class="device-ip">10.30.x.x</div>
<div class="device-code">VLAN 30</div>
</div>
<div class="device">
<div class="device-icon">📱</div>
<div class="device-name">WiFi Guest</div>
<div class="device-ip">10.40.x.x</div>
<div class="device-code">VLAN 40</div>
</div>
</div>
</div>
<div class="connection">⬇️ Firewall Interno</div>
<!-- ZONA SICURA -->
<div class="zone zone-secure">
<div class="zone-label">🔒 DATACENTER / ZONA SICURA</div>
<div class="grid-container">
<div class="device">
<div class="device-icon">🖥️</div>
<div class="device-name">ERP Server</div>
<div class="device-ip">10.10.1.10</div>
<div class="device-code">HW-SRV-001</div>
</div>
<div class="device">
<div class="device-icon">💾</div>
<div class="device-name">Database Server</div>
<div class="device-ip">10.10.1.20</div>
<div class="device-code">HW-SRV-012</div>
</div>
<div class="device">
<div class="device-icon">📊</div>
<div class="device-name">SIEM Splunk</div>
<div class="device-ip">10.10.1.30</div>
<div class="device-code">HW-SRV-045</div>
</div>
<div class="device">
<div class="device-icon">💿</div>
<div class="device-name">Backup Server</div>
<div class="device-ip">10.10.1.40</div>
<div class="device-code">HW-SRV-050</div>
</div>
<div class="device">
<div class="device-icon">☁️</div>
<div class="device-name">VMware Cluster</div>
<div class="device-ip">10.10.1.50-55</div>
<div class="device-code">HW-SRV-060</div>
</div>
<div class="device">
<div class="device-icon">🔐</div>
<div class="device-name">Active Directory</div>
<div class="device-ip">10.10.1.60</div>
<div class="device-code">HW-SRV-065</div>
</div>
</div>
</div>
</div>
<div class="legend">
<div class="legend-title">📖 LEGENDA</div>
<div class="legend-item">
<span class="legend-color" style="background-color: var(--zone-external);"></span>
Zona Esterna (Internet)
</div>
<div class="legend-item">
<span class="legend-color" style="background-color: var(--zone-dmz);"></span>
DMZ (Servizi Esposti)
</div>
<div class="legend-item">
<span class="legend-color" style="background-color: var(--zone-internal);"></span>
Rete Interna (Utenti)
</div>
<div class="legend-item">
<span class="legend-color" style="background-color: var(--zone-secure);"></span>
Datacenter (Sistemi Critici)
</div>
</div>
<div class="info-box">
<div class="info-box-title">🔐 Note di Sicurezza</div>
• Tutti i flussi tra zone sono controllati da firewall con regole whitelist<br>
• Monitoraggio 24/7 tramite SIEM centralizzato (Splunk)<br>
• Segmentazione VLAN per separazione logica dei servizi<br>
• Backup giornaliero con replica off-site<br>
• Accesso datacenter con autenticazione biometrica e logging<br>
• Aggiornamento obbligatorio entro 5 giorni da modifiche infrastrutturali
</div>
<div style="margin-top: 40px; padding-top: 20px; border-top: 2px solid var(--border-color); text-align: center; font-size: 10px; color: var(--text-secondary);">
Documento NET-TOPO-PHY-v3.2 - RISERVATO - Pagina 1 di 1
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,482 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documento Formale - Sistemi Rilevanti NIS2</title>
<style>
:root {
--bg-primary: #ffffff;
--text-primary: #1a1a1a;
--text-secondary: #666666;
--border-color: #cccccc;
--accent-primary: #0066cc;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Times New Roman', Times, serif;
background-color: #f5f5f5;
color: var(--text-primary);
line-height: 1.6;
padding: 20px;
}
.document-container {
max-width: 210mm;
margin: 0 auto;
background-color: var(--bg-primary);
padding: 40mm 25mm;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
min-height: 297mm;
}
.header {
text-align: center;
border-bottom: 3px solid var(--text-primary);
padding-bottom: 20px;
margin-bottom: 30px;
}
.logo {
font-size: 24px;
font-weight: bold;
color: var(--accent-primary);
margin-bottom: 10px;
}
.doc-title {
font-size: 20px;
font-weight: bold;
margin: 20px 0 10px 0;
text-transform: uppercase;
}
.doc-subtitle {
font-size: 14px;
color: var(--text-secondary);
margin-bottom: 5px;
}
.doc-info {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin: 30px 0;
padding: 15px;
background-color: #f9f9f9;
border-left: 4px solid var(--accent-primary);
}
.doc-info-item {
font-size: 12px;
}
.doc-info-label {
font-weight: bold;
color: var(--text-secondary);
}
.section {
margin: 30px 0;
}
.section-title {
font-size: 16px;
font-weight: bold;
color: var(--accent-primary);
border-bottom: 2px solid var(--border-color);
padding-bottom: 5px;
margin-bottom: 15px;
}
.section-content {
font-size: 12px;
text-align: justify;
margin-bottom: 15px;
}
table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
font-size: 11px;
}
th {
background-color: #e8e8e8;
padding: 10px 8px;
text-align: left;
border: 1px solid var(--border-color);
font-weight: bold;
font-size: 10px;
}
td {
padding: 8px;
border: 1px solid var(--border-color);
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
.critical {
color: #d32f2f;
font-weight: bold;
}
.high {
color: #f57c00;
font-weight: bold;
}
.medium {
color: #388e3c;
font-weight: bold;
}
.footer {
margin-top: 50px;
padding-top: 20px;
border-top: 2px solid var(--border-color);
}
.signature-block {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 40px;
margin-top: 40px;
}
.signature {
text-align: center;
}
.signature-line {
border-top: 1px solid var(--text-primary);
margin-top: 60px;
padding-top: 10px;
font-size: 11px;
}
.signature-role {
font-size: 10px;
color: var(--text-secondary);
}
.page-number {
text-align: center;
font-size: 10px;
color: var(--text-secondary);
margin-top: 30px;
}
.classification {
text-align: center;
font-size: 11px;
font-weight: bold;
color: #d32f2f;
background-color: #ffebee;
padding: 10px;
border: 2px solid #d32f2f;
margin-bottom: 20px;
}
@media print {
body {
background-color: white;
padding: 0;
}
.document-container {
box-shadow: none;
padding: 20mm 15mm;
}
}
.no-print {
position: fixed;
top: 20px;
right: 20px;
z-index: 1000;
}
.btn-print {
padding: 12px 24px;
background-color: var(--accent-primary);
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
.btn-print:hover {
background-color: #0052a3;
}
</style>
</head>
<body>
<div class="no-print">
<button class="btn-print" onclick="window.print()">🖨️ Stampa PDF</button>
</div>
<div class="document-container">
<div class="classification">
⚠️ DOCUMENTO RISERVATO - DISTRIBUZIONE LIMITATA ⚠️
</div>
<div class="header">
<div class="logo">ACME CORPORATION S.p.A.</div>
<div class="doc-title">Elenco Sistemi Rilevanti NIS2</div>
<div class="doc-subtitle">Documento Formale ai sensi della Direttiva (UE) 2022/2555</div>
<div class="doc-subtitle">Requisito GV.OC-04</div>
</div>
<div class="doc-info">
<div class="doc-info-item">
<span class="doc-info-label">Codice Documento:</span> NIS2-GV-OC-04-v2.3
</div>
<div class="doc-info-item">
<span class="doc-info-label">Data Emissione:</span> 15 Febbraio 2024
</div>
<div class="doc-info-item">
<span class="doc-info-label">Versione:</span> 2.3
</div>
<div class="doc-info-item">
<span class="doc-info-label">Prossima Revisione:</span> 15 Agosto 2024
</div>
<div class="doc-info-item">
<span class="doc-info-label">Redatto da:</span> CISO - Marco Bianchi
</div>
<div class="doc-info-item">
<span class="doc-info-label">Approvato da:</span> CdA - Delibera 05/2024
</div>
</div>
<div class="section">
<div class="section-title">1. PREMESSA</div>
<div class="section-content">
Il presente documento costituisce l'elenco formale dei sistemi classificati come rilevanti ai fini della conformità alla Direttiva NIS2 (UE) 2022/2555 e al relativo decreto di recepimento nazionale. La classificazione è stata effettuata secondo la metodologia di scoring approvata dal Consiglio di Amministrazione in data 10 Gennaio 2024, basata su sei criteri di valutazione con punteggio massimo di 100 punti.
</div>
<div class="section-content">
<strong>Soglia di Rilevanza:</strong> Sono considerati rilevanti tutti i sistemi con punteggio ≥ 40 punti.<br>
<strong>Sistemi Critici:</strong> Sistemi con punteggio ≥ 80 punti richiedono misure di sicurezza massime e monitoraggio continuo 24/7.
</div>
</div>
<div class="section">
<div class="section-title">2. RIEPILOGO STATISTICO</div>
<div class="section-content">
<table>
<tr>
<th>Categoria</th>
<th>Range Punteggio</th>
<th>Numero Sistemi</th>
<th>Percentuale</th>
</tr>
<tr>
<td><span class="critical">CRITICO</span></td>
<td>80-100</td>
<td>12</td>
<td>31.6%</td>
</tr>
<tr>
<td><span class="high">ALTO</span></td>
<td>60-79</td>
<td>15</td>
<td>39.5%</td>
</tr>
<tr>
<td><span class="medium">MEDIO</span></td>
<td>40-59</td>
<td>11</td>
<td>28.9%</td>
</tr>
<tr>
<td><strong>TOTALE RILEVANTI</strong></td>
<td>≥40</td>
<td><strong>38</strong></td>
<td><strong>100%</strong></td>
</tr>
</table>
</div>
</div>
<div class="section">
<div class="section-title">3. ELENCO SISTEMI CRITICI (≥80 PUNTI)</div>
<div class="section-content">
<table>
<tr>
<th>Codice</th>
<th>Nome Sistema</th>
<th>Tipo</th>
<th>Punteggio</th>
<th>RTO/RPO</th>
<th>Monitoraggio</th>
</tr>
<tr>
<td>SW-ERP-001</td>
<td>SAP ERP</td>
<td>Software</td>
<td class="critical">95</td>
<td>4h / 1h</td>
<td>24/7</td>
</tr>
<tr>
<td>SVC-001</td>
<td>Piattaforma ERP Cloud</td>
<td>Servizio</td>
<td class="critical">94</td>
<td>4h / 1h</td>
<td>24/7</td>
</tr>
<tr>
<td>SW-SEC-008</td>
<td>Splunk SIEM</td>
<td>Software</td>
<td class="critical">92</td>
<td>4h / 4h</td>
<td>24/7</td>
</tr>
<tr>
<td>HW-SRV-001</td>
<td>ERP-PROD-01</td>
<td>Hardware</td>
<td class="critical">91</td>
<td>4h / 1h</td>
<td>24/7</td>
</tr>
<tr>
<td>SW-DB-003</td>
<td>Oracle Database</td>
<td>Software</td>
<td class="critical">89</td>
<td>4h / 1h</td>
<td>24/7</td>
</tr>
<tr>
<td>HW-NET-015</td>
<td>FW-PERIMETRALE-01</td>
<td>Hardware</td>
<td class="critical">88</td>
<td>1h / N/A</td>
<td>24/7</td>
</tr>
<tr>
<td>SVC-002</td>
<td>Hosting Applicazioni</td>
<td>Servizio</td>
<td class="critical">88</td>
<td>8h / 4h</td>
<td>24/7</td>
</tr>
<tr>
<td>CLD-IAAS-001</td>
<td>VM Production AWS</td>
<td>Cloud</td>
<td class="critical">87</td>
<td>4h / 1h</td>
<td>24/7</td>
</tr>
<tr>
<td>CLD-PAAS-003</td>
<td>Azure SQL Database</td>
<td>Cloud</td>
<td class="critical">85</td>
<td>4h / 1h</td>
<td>24/7</td>
</tr>
<tr>
<td>HW-NET-022</td>
<td>Core Switch Datacenter</td>
<td>Hardware</td>
<td class="critical">84</td>
<td>2h / N/A</td>
<td>24/7</td>
</tr>
<tr>
<td>SW-BACKUP-001</td>
<td>Veeam Backup System</td>
<td>Software</td>
<td class="critical">82</td>
<td>24h / 24h</td>
<td>24/7</td>
</tr>
<tr>
<td>HW-SRV-012</td>
<td>DB-PROD-01</td>
<td>Hardware</td>
<td class="critical">80</td>
<td>4h / 1h</td>
<td>24/7</td>
</tr>
</table>
</div>
</div>
<div class="section">
<div class="section-title">4. MISURE DI SICUREZZA OBBLIGATORIE</div>
<div class="section-content">
Per tutti i sistemi rilevanti sono implementate le seguenti misure minime di sicurezza:
<ul style="margin-left: 20px; margin-top: 10px;">
<li>Backup giornaliero con retention minima 30 giorni</li>
<li>Patch management con SLA massimo 30 giorni per vulnerabilità critiche</li>
<li>Logging centralizzato su SIEM con retention 12 mesi</li>
<li>Controllo accessi con autenticazione multi-fattore (MFA)</li>
<li>Monitoraggio continuo con alerting automatico</li>
<li>Business Continuity Plan e Disaster Recovery Plan documentati e testati</li>
</ul>
</div>
<div class="section-content">
<strong>Sistemi Critici (≥80):</strong> Oltre alle misure sopra elencate, richiedono ridondanza hardware/software, monitoraggio 24/7 con reperibilità H24, test DR semestrali e revisione trimestrale delle configurazioni di sicurezza.
</div>
</div>
<div class="section">
<div class="section-title">5. REVISIONE E AGGIORNAMENTO</div>
<div class="section-content">
Il presente documento è soggetto a revisione semestrale obbligatoria. Revisioni straordinarie sono richieste in caso di:
<ul style="margin-left: 20px; margin-top: 10px;">
<li>Introduzione di nuovi sistemi con punteggio ≥40</li>
<li>Modifiche sostanziali ai sistemi esistenti che ne alterano il punteggio</li>
<li>Incidenti di sicurezza significativi</li>
<li>Cambiamenti normativi rilevanti</li>
</ul>
</div>
</div>
<div class="footer">
<div class="section-title">APPROVAZIONE</div>
<div class="section-content">
Il presente documento è stato approvato dal Consiglio di Amministrazione in data 15 Febbraio 2024 con Delibera n. 05/2024 e costituisce documento ufficiale ai fini della conformità NIS2.
</div>
<div class="signature-block">
<div class="signature">
<div class="signature-line">
<strong>Marco Bianchi</strong><br>
<span class="signature-role">Chief Information Security Officer (CISO)</span>
</div>
</div>
<div class="signature">
<div class="signature-line">
<strong>Giovanni Rossi</strong><br>
<span class="signature-role">Amministratore Delegato</span>
</div>
</div>
</div>
</div>
<div class="page-number">
Pagina 1 di 1 - Documento NIS2-GV-OC-04-v2.3 - RISERVATO
</div>
</div>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,353 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Accesso Sistema Gestione Incidenti - NIS2</title>
<style>
:root {
--bg-primary: #0d1117;
--bg-secondary: #161b22;
--bg-tertiary: #1c2128;
--border-color: #30363d;
--text-primary: #c9d1d9;
--text-secondary: #8b949e;
--accent-primary: #58a6ff;
--accent-secondary: #1f6feb;
--success: #3fb950;
--warning: #d29922;
--danger: #f85149;
--essential-bg: #fef3c7;
--essential-text: #92400e;
--essential-border: #f59e0b;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
background-color: var(--bg-primary);
color: var(--text-primary);
line-height: 1.6;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 20px;
}
.gate-container {
max-width: 900px;
width: 100%;
}
.gate-header {
text-align: center;
margin-bottom: 48px;
}
.gate-header h1 {
font-size: 32px;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 16px;
}
.gate-header p {
font-size: 16px;
color: var(--text-secondary);
line-height: 1.6;
}
.warning-box {
background-color: rgba(210, 153, 34, 0.1);
border: 2px solid var(--warning);
border-radius: 8px;
padding: 24px;
margin-bottom: 48px;
}
.warning-box-title {
display: flex;
align-items: center;
gap: 12px;
font-size: 18px;
font-weight: 600;
color: var(--warning);
margin-bottom: 16px;
}
.warning-icon {
font-size: 24px;
}
.warning-box-content {
font-size: 14px;
color: var(--text-primary);
line-height: 1.8;
}
.selection-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 24px;
margin-bottom: 32px;
}
.selection-card {
background-color: var(--bg-secondary);
border: 2px solid var(--border-color);
border-radius: 8px;
padding: 32px;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.selection-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary));
transform: scaleX(0);
transition: transform 0.3s ease;
}
.selection-card:hover {
border-color: var(--accent-primary);
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(88, 166, 255, 0.2);
}
.selection-card:hover::before {
transform: scaleX(1);
}
.selection-card.essential {
border-color: var(--essential-border);
}
.selection-card.essential:hover {
border-color: var(--essential-border);
box-shadow: 0 8px 24px rgba(245, 158, 11, 0.2);
}
.selection-card.essential::before {
background: linear-gradient(90deg, var(--essential-border), var(--warning));
}
.card-badge {
display: inline-block;
padding: 6px 12px;
border-radius: 4px;
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 16px;
}
.badge-important {
background-color: rgba(88, 166, 255, 0.2);
color: var(--accent-primary);
border: 1px solid var(--accent-primary);
}
.badge-essential {
background-color: var(--essential-bg);
color: var(--essential-text);
border: 1px solid var(--essential-border);
}
.card-title {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 16px;
}
.card-description {
font-size: 14px;
color: var(--text-secondary);
line-height: 1.6;
margin-bottom: 24px;
}
.card-features {
list-style: none;
margin-bottom: 24px;
}
.card-features li {
font-size: 13px;
color: var(--text-primary);
padding: 8px 0;
padding-left: 24px;
position: relative;
}
.card-features li::before {
content: '✓';
position: absolute;
left: 0;
color: var(--success);
font-weight: 700;
}
.card-button {
width: 100%;
padding: 14px 24px;
background-color: var(--bg-tertiary);
border: 2px solid var(--accent-primary);
border-radius: 6px;
color: var(--accent-primary);
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.card-button:hover {
background-color: var(--accent-primary);
color: var(--bg-primary);
}
.selection-card.essential .card-button {
border-color: var(--essential-border);
color: var(--essential-border);
}
.selection-card.essential .card-button:hover {
background-color: var(--essential-border);
color: var(--bg-primary);
}
.footer-note {
text-align: center;
font-size: 13px;
color: var(--text-secondary);
padding: 24px;
background-color: var(--bg-secondary);
border-radius: 6px;
border: 1px solid var(--border-color);
}
.footer-note strong {
color: var(--text-primary);
}
@media (max-width: 768px) {
.selection-grid {
grid-template-columns: 1fr;
}
.gate-header h1 {
font-size: 24px;
}
}
</style>
</head>
<body>
<div class="gate-container">
<div class="gate-header">
<h1>Sistema Gestione Incidenti NIS2</h1>
<p>Seleziona la tipologia del tuo soggetto per accedere alle funzionalità appropriate</p>
</div>
<div class="warning-box">
<div class="warning-box-title">
<span class="warning-icon">⚠️</span>
Accesso Obbligatorio
</div>
<div class="warning-box-content">
<strong>La conformità alla gestione incidenti è obbligatoria per TUTTI i soggetti NIS2</strong> (essenziali e importanti). La differenza sta nei tipi di incidenti da segnalare e nelle tempistiche, come definito negli Allegati 3 (soggetti essenziali) e 4 (soggetti importanti) della Determina 164179/2025.
<br><br>
Seleziona la categoria corretta per visualizzare solo i requisiti applicabili alla tua organizzazione.
</div>
</div>
<div class="selection-grid">
<div class="selection-card" onclick="selectType('important')">
<span class="card-badge badge-important">Soggetto Importante</span>
<h2 class="card-title">Soggetto Importante</h2>
<p class="card-description">
Organizzazioni che rientrano nella categoria "importanti" secondo il D.Lgs. 138/2024, con obblighi di notifica per incidenti significativi secondo Allegato 4.
</p>
<ul class="card-features">
<li>Notifica incidenti IS-1, IS-2, IS-3</li>
<li>Preallarme entro 24 ore</li>
<li>Notifica completa entro 72 ore</li>
<li>Relazione finale entro 1 mese</li>
<li>Gestione incidenti ricorrenti (IS-4) non obbligatoria</li>
</ul>
<button class="card-button">Accedi come Soggetto Importante</button>
</div>
<div class="selection-card essential" onclick="selectType('essential')">
<span class="card-badge badge-essential">Soggetto Essenziale</span>
<h2 class="card-title">Soggetto Essenziale</h2>
<p class="card-description">
Organizzazioni che rientrano nella categoria "essenziali" secondo il D.Lgs. 138/2024, con obblighi estesi di notifica per incidenti significativi secondo Allegato 3.
</p>
<ul class="card-features">
<li>Notifica incidenti IS-1, IS-2, IS-3, IS-4</li>
<li>Preallarme entro 24 ore</li>
<li>Notifica completa entro 72 ore</li>
<li>Relazione finale entro 1 mese</li>
<li>Gestione incidenti ricorrenti (IS-4) obbligatoria</li>
<li>Monitoraggio 24/7 richiesto</li>
</ul>
<button class="card-button">Accedi come Soggetto Essenziale</button>
</div>
</div>
<div class="footer-note">
<strong>Riferimenti normativi:</strong> D.Lgs. 138/2024 (Direttiva NIS2), Determina ACN 164179/2025 (Allegati 3 e 4)<br>
<strong>Requisiti NIS2 coperti:</strong> RS.MA-01 (Gestione Incidenti), RS.CO-02 (Notifiche), RC.RP-01 (Ripristino), RC.CO-03 (Comunicazioni)
</div>
</div>
<script>
function selectType(type) {
// Salva la selezione in sessionStorage
sessionStorage.setItem('nis2_subject_type', type);
// Reindirizza alla dashboard
window.location.href = 'incident-dashboard.html';
}
// Verifica se c'è già una selezione
window.addEventListener('DOMContentLoaded', function() {
const savedType = sessionStorage.getItem('nis2_subject_type');
if (savedType) {
// Mostra un messaggio che sta caricando
const container = document.querySelector('.gate-container');
container.innerHTML = `
<div style="text-align: center; padding: 60px 20px;">
<h2 style="font-size: 24px; margin-bottom: 16px;">Accesso già configurato</h2>
<p style="color: var(--text-secondary); margin-bottom: 24px;">
Sei configurato come: <strong style="color: var(--text-primary);">${savedType === 'essential' ? 'Soggetto Essenziale' : 'Soggetto Importante'}</strong>
</p>
<button onclick="window.location.href='incident-dashboard.html'"
style="padding: 12px 24px; background-color: var(--accent-primary); border: none; border-radius: 6px; color: white; font-weight: 600; cursor: pointer; margin-right: 12px;">
Vai alla Dashboard
</button>
<button onclick="sessionStorage.removeItem('nis2_subject_type'); location.reload();"
style="padding: 12px 24px; background-color: var(--bg-tertiary); border: 1px solid var(--border-color); border-radius: 6px; color: var(--text-primary); font-weight: 600; cursor: pointer;">
Cambia Selezione
</button>
</div>
`;
}
});
</script>
</body>
</html>

View File

@ -0,0 +1,956 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nuovo Incidente - NIS2 Management System</title>
<style>
:root {
--bg-primary: #0d1117;
--bg-secondary: #161b22;
--bg-tertiary: #1c2128;
--border-color: #30363d;
--text-primary: #c9d1d9;
--text-secondary: #8b949e;
--accent-primary: #58a6ff;
--accent-secondary: #1f6feb;
--success: #3fb950;
--warning: #d29922;
--danger: #f85149;
--essential-bg: #fef3c7;
--essential-text: #92400e;
--essential-border: #f59e0b;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
background-color: var(--bg-primary);
color: var(--text-primary);
line-height: 1.6;
overflow-y: auto;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
background-color: var(--bg-secondary);
border-bottom: 1px solid var(--border-color);
padding: 24px 0;
margin-bottom: 32px;
position: sticky;
top: 0;
z-index: 100;
}
.header-content {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.header h1 {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
}
.breadcrumb {
font-size: 13px;
color: var(--text-secondary);
margin-top: 4px;
}
.breadcrumb a {
color: var(--accent-primary);
text-decoration: none;
}
.btn {
padding: 8px 16px;
background-color: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-radius: 6px;
color: var(--text-primary);
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
text-decoration: none;
}
.btn:hover {
border-color: var(--accent-primary);
color: var(--accent-primary);
}
.form-section {
background-color: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 24px;
margin-bottom: 24px;
}
.form-section-title {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
gap: 8px;
}
.help-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
background-color: rgba(167, 139, 250, 0.2);
border: 2px solid #a78bfa;
border-radius: 50%;
font-size: 13px;
font-weight: 700;
color: #a78bfa;
cursor: help;
position: relative;
}
.help-icon:hover {
background-color: rgba(167, 139, 250, 0.3);
transform: scale(1.1);
}
.tooltip {
visibility: hidden;
position: absolute;
z-index: 1000;
background-color: var(--bg-tertiary);
color: var(--text-primary);
padding: 12px;
border-radius: 6px;
border: 1px solid var(--border-color);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
width: 320px;
top: 28px;
left: 50%;
transform: translateX(-50%);
font-size: 12px;
line-height: 1.5;
opacity: 0;
transition: opacity 0.2s;
white-space: normal;
}
.tooltip::before {
content: '';
position: absolute;
top: -6px;
left: 50%;
transform: translateX(-50%);
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid var(--border-color);
}
.tooltip-title {
color: var(--accent-primary);
font-weight: 600;
margin-bottom: 8px;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.help-icon:hover .tooltip {
visibility: visible;
opacity: 1;
}
.form-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.form-group {
margin-bottom: 20px;
}
.form-group.full-width {
grid-column: 1 / -1;
}
.form-label {
display: block;
font-size: 13px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 8px;
}
.form-label .required {
color: var(--danger);
margin-left: 4px;
}
.form-input, .form-select, .form-textarea {
width: 100%;
padding: 10px 12px;
background-color: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-radius: 6px;
color: var(--text-primary);
font-size: 14px;
font-family: inherit;
}
.form-input:focus, .form-select:focus, .form-textarea:focus {
outline: none;
border-color: var(--accent-primary);
box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.1);
}
.form-textarea {
resize: vertical;
min-height: 100px;
}
.form-help {
font-size: 12px;
color: var(--text-secondary);
margin-top: 6px;
line-height: 1.4;
}
.severity-selector {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 12px;
}
.severity-option {
padding: 16px;
background-color: var(--bg-tertiary);
border: 2px solid var(--border-color);
border-radius: 6px;
cursor: pointer;
text-align: center;
transition: all 0.2s;
}
.severity-option:hover {
border-color: var(--accent-primary);
}
.severity-option.selected {
border-color: var(--accent-primary);
background-color: rgba(88, 166, 255, 0.1);
}
.severity-option.sev-1 {
border-color: var(--danger);
}
.severity-option.sev-1.selected {
background-color: rgba(248, 81, 73, 0.1);
}
.severity-option.sev-2 {
border-color: var(--warning);
}
.severity-option.sev-2.selected {
background-color: rgba(210, 153, 34, 0.1);
}
.severity-label {
font-weight: 700;
font-size: 14px;
margin-bottom: 4px;
}
.severity-desc {
font-size: 11px;
color: var(--text-secondary);
}
.alert-box {
background-color: rgba(88, 166, 255, 0.1);
border: 1px solid var(--accent-primary);
border-left: 4px solid var(--accent-primary);
border-radius: 6px;
padding: 16px;
margin-bottom: 20px;
}
.alert-box.warning {
background-color: rgba(210, 153, 34, 0.1);
border-color: var(--warning);
border-left-color: var(--warning);
}
.alert-box.danger {
background-color: rgba(248, 81, 73, 0.1);
border-color: var(--danger);
border-left-color: var(--danger);
}
.alert-title {
font-weight: 600;
margin-bottom: 8px;
font-size: 14px;
}
.alert-content {
font-size: 13px;
color: var(--text-secondary);
line-height: 1.5;
}
.form-actions {
display: flex;
gap: 12px;
justify-content: flex-end;
padding-top: 24px;
border-top: 1px solid var(--border-color);
}
.btn-primary {
background-color: var(--accent-primary);
border-color: var(--accent-primary);
color: white;
}
.btn-primary:hover {
background-color: var(--accent-secondary);
border-color: var(--accent-secondary);
color: white;
}
.btn-danger {
background-color: var(--danger);
border-color: var(--danger);
color: white;
}
.btn-danger:hover {
background-color: #dc2626;
border-color: #dc2626;
}
.checklist {
list-style: none;
}
.checklist li {
padding: 10px;
background-color: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-radius: 4px;
margin-bottom: 8px;
display: flex;
align-items: center;
gap: 12px;
}
.checklist input[type="checkbox"] {
width: 18px;
height: 18px;
cursor: pointer;
}
.essential-badge {
display: inline-block;
background-color: var(--essential-bg);
color: var(--essential-text);
padding: 4px 8px;
border-radius: 3px;
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
border: 1px solid var(--essential-border);
margin-left: 8px;
}
@media (max-width: 768px) {
.form-grid {
grid-template-columns: 1fr;
}
.severity-selector {
grid-template-columns: 1fr 1fr;
}
}
</style>
</head>
<body>
<div class="header">
<div class="header-content">
<div>
<h1>🚨 Segnalazione Nuovo Incidente</h1>
<div class="breadcrumb">
<a href="dashboard.html">Dashboard NIS2</a> /
<a href="incident-dashboard.html">Gestione Incidenti</a> /
Nuovo Incidente
</div>
</div>
<a href="incident-dashboard.html" class="btn">← Annulla</a>
</div>
</div>
<div class="container">
<div class="alert-box danger">
<div class="alert-title">⚠️ Procedura di Emergenza</div>
<div class="alert-content">
Per incidenti <strong>SEV-1 (Critici)</strong>: attivare immediatamente il Crisis Team e il CISO.
La notifica al CSIRT Italia (preallarme) deve essere effettuata entro <strong>24 ore</strong> dalla conoscenza dell'incidente significativo.
</div>
</div>
<form id="incidentForm">
<!-- Sezione 1: Rilevazione -->
<div class="form-section">
<div class="form-section-title">
Fase 1: Rilevazione e Segnalazione
<span class="help-icon">?
<div class="tooltip">
<div class="tooltip-title">HELP DELLA SEZIONE</div>
Registrazione iniziale dell'incidente. Compilare entro 15 minuti dalla rilevazione per incidenti critici. Tutte le informazioni possono essere aggiornate durante la gestione.
</div>
</span>
</div>
<div class="form-grid">
<div class="form-group">
<label class="form-label">
Data/Ora Rilevazione <span class="required">*</span>
</label>
<input type="datetime-local" class="form-input" id="detectionTime" required>
<div class="form-help">Momento in cui l'incidente è stato rilevato</div>
</div>
<div class="form-group">
<label class="form-label">
Fonte di Rilevazione <span class="required">*</span>
</label>
<select class="form-select" id="detectionSource" required>
<option value="">Seleziona...</option>
<option value="siem">Alert SIEM/SOC</option>
<option value="edr">Alert EDR/Antimalware</option>
<option value="user">Segnalazione Utente</option>
<option value="supplier">Notifica Fornitore/Partner</option>
<option value="csirt">Notifica CSIRT Italia</option>
<option value="threat-intel">Threat Intelligence</option>
<option value="external">Segnalazione Esterna</option>
<option value="monitoring">Monitoraggio Infrastruttura</option>
</select>
</div>
<div class="form-group">
<label class="form-label">
Analista Assegnato <span class="required">*</span>
</label>
<select class="form-select" id="analyst" required>
<option value="">Seleziona...</option>
<option value="ciso">CISO</option>
<option value="soc-lead">SOC Lead</option>
<option value="analyst1">Analista Sicurezza 1</option>
<option value="analyst2">Analista Sicurezza 2</option>
<option value="sysadmin">System Administrator</option>
<option value="netadmin">Network Administrator</option>
</select>
</div>
<div class="form-group full-width">
<label class="form-label">
Descrizione Preliminare <span class="required">*</span>
</label>
<textarea class="form-textarea" id="description" required placeholder="Descrizione dettagliata dell'incidente rilevato..."></textarea>
<div class="form-help">Fornire tutti i dettagli disponibili: cosa è stato rilevato, su quali sistemi, sintomi osservati</div>
</div>
<div class="form-group full-width">
<label class="form-label">
Sistemi Potenzialmente Impattati
</label>
<input type="text" class="form-input" id="affectedSystems" placeholder="es. HW-SRV-001, SW-ERP-001, CLD-IAAS-001">
<div class="form-help">Codici asset da inventario (Org.01). Separare con virgola se multipli</div>
</div>
</div>
</div>
<!-- Sezione 2: Triage e Classificazione -->
<div class="form-section">
<div class="form-section-title">
Fase 2: Triage e Classificazione
<span class="help-icon">?
<div class="tooltip">
<div class="tooltip-title">HELP DELLA SEZIONE</div>
Determinazione della severità e classificazione secondo NIS2. Completare entro 1 ora per SEV-1/SEV-2. La classificazione determina gli obblighi di notifica al CSIRT.
</div>
</span>
</div>
<div class="form-group full-width">
<label class="form-label">
Severità Incidente <span class="required">*</span>
</label>
<div class="severity-selector">
<div class="severity-option sev-1" onclick="selectSeverity('sev-1')">
<div class="severity-label" style="color: var(--danger);">SEV-1</div>
<div class="severity-desc">CRITICO</div>
</div>
<div class="severity-option sev-2" onclick="selectSeverity('sev-2')">
<div class="severity-label" style="color: var(--warning);">SEV-2</div>
<div class="severity-desc">ALTO</div>
</div>
<div class="severity-option" onclick="selectSeverity('sev-3')">
<div class="severity-label" style="color: var(--accent-primary);">SEV-3</div>
<div class="severity-desc">MEDIO</div>
</div>
<div class="severity-option" onclick="selectSeverity('sev-4')">
<div class="severity-label" style="color: var(--text-secondary);">SEV-4</div>
<div class="severity-desc">BASSO</div>
</div>
</div>
<input type="hidden" id="severity" required>
<div class="form-help" id="severityHelp" style="margin-top: 12px;"></div>
</div>
<div class="alert-box warning" id="sev1Alert" style="display: none;">
<div class="alert-title">🚨 Incidente SEV-1 - Azioni Immediate Richieste</div>
<div class="alert-content">
<ul style="margin-left: 20px; margin-top: 8px;">
<li>Attivare immediatamente Crisis Team</li>
<li>Notificare CISO e Direzione</li>
<li>Contenimento entro 30 minuti</li>
<li>Preallarme CSIRT entro 24h (se significativo)</li>
</ul>
</div>
</div>
<div class="form-grid">
<div class="form-group">
<label class="form-label">
Classificazione NIS2 <span class="required">*</span>
</label>
<select class="form-select" id="classification" required onchange="updateClassificationHelp()">
<option value="">Seleziona...</option>
<option value="is-1">IS-1 - Impatto su servizi rilevanti</option>
<option value="is-2">IS-2 - Impatto su integrità/riservatezza dati</option>
<option value="is-3">IS-3 - Impatto su altri soggetti/supply chain</option>
<option value="is-4" id="is4ClassOption">IS-4 - Incidente ricorrente (SOLO ESSENZIALI)</option>
<option value="not-significant">Non Significativo</option>
</select>
<div class="form-help" id="classificationHelp"></div>
</div>
<div class="form-group">
<label class="form-label">
Impatto
</label>
<select class="form-select" id="impact" multiple size="4">
<option value="availability">Disponibilità</option>
<option value="integrity">Integrità</option>
<option value="confidentiality">Riservatezza</option>
<option value="authenticity">Autenticità</option>
</select>
<div class="form-help">Tenere premuto Ctrl/Cmd per selezione multipla</div>
</div>
<div class="form-group">
<label class="form-label">
Categoria MITRE ATT&CK
</label>
<select class="form-select" id="mitreCategory">
<option value="">Non determinata</option>
<option value="initial-access">Initial Access</option>
<option value="execution">Execution</option>
<option value="persistence">Persistence</option>
<option value="privilege-escalation">Privilege Escalation</option>
<option value="defense-evasion">Defense Evasion</option>
<option value="credential-access">Credential Access</option>
<option value="discovery">Discovery</option>
<option value="lateral-movement">Lateral Movement</option>
<option value="collection">Collection</option>
<option value="exfiltration">Exfiltration</option>
<option value="impact">Impact</option>
</select>
</div>
<div class="form-group">
<label class="form-label">
Vettore di Attacco
</label>
<select class="form-select" id="attackVector">
<option value="">Non determinato</option>
<option value="phishing">Phishing/Spear Phishing</option>
<option value="malware">Malware</option>
<option value="ransomware">Ransomware</option>
<option value="vulnerability">Exploit Vulnerabilità</option>
<option value="brute-force">Brute Force</option>
<option value="credential-stuffing">Credential Stuffing</option>
<option value="supply-chain">Supply Chain Attack</option>
<option value="ddos">DDoS</option>
<option value="insider">Insider Threat</option>
<option value="physical">Accesso Fisico</option>
<option value="social-engineering">Social Engineering</option>
</select>
</div>
</div>
<div class="alert-box" id="csirtAlert" style="display: none;">
<div class="alert-title">📡 Notifica CSIRT Obbligatoria</div>
<div class="alert-content">
Questo incidente rientra nella classificazione di incidente significativo secondo NIS2.
È obbligatorio inviare il <strong>preallarme al CSIRT Italia entro 24 ore</strong> dalla conoscenza dell'incidente.
<br><br>
Dopo la creazione dell'incidente, procedere alla sezione "Notifiche CSIRT" per gestire le comunicazioni obbligatorie.
</div>
</div>
</div>
<!-- Sezione 3: Dati Impattati -->
<div class="form-section">
<div class="form-section-title">
Dati e Servizi Impattati
<span class="help-icon">?
<div class="tooltip">
<div class="tooltip-title">HELP DELLA SEZIONE</div>
Identificazione dei dati e servizi coinvolti nell'incidente. Fondamentale per valutare obblighi GDPR e impatto operativo.
</div>
</span>
</div>
<div class="form-grid">
<div class="form-group">
<label class="form-label">
Dati Personali Coinvolti?
</label>
<select class="form-select" id="personalData" onchange="toggleGDPR()">
<option value="no">No</option>
<option value="yes"></option>
<option value="unknown">Da verificare</option>
</select>
</div>
<div class="form-group" id="gdprGroup" style="display: none;">
<label class="form-label">
Notifica Garante Privacy
</label>
<select class="form-select" id="gdprNotification">
<option value="not-required">Non richiesta</option>
<option value="required">Richiesta (entro 72h)</option>
<option value="sent">Già inviata</option>
</select>
<div class="form-help">Art. 33 GDPR - coordinare con DPO</div>
</div>
<div class="form-group full-width">
<label class="form-label">
Servizi Rilevanti NIS2 Impattati
</label>
<input type="text" class="form-input" id="affectedServices" placeholder="Codici servizi da catalogo (ID.AM-04)">
<div class="form-help">Elencare i servizi rilevanti NIS2 che hanno subito interruzione o degrado</div>
</div>
<div class="form-group">
<label class="form-label">
Numero Utenti Impattati (stima)
</label>
<input type="number" class="form-input" id="usersImpacted" min="0">
</div>
<div class="form-group">
<label class="form-label">
Impatto Economico Stimato (€)
</label>
<input type="number" class="form-input" id="financialImpact" min="0" step="100">
<div class="form-help">Stima preliminare (diretto + indiretto)</div>
</div>
</div>
</div>
<!-- Sezione 4: Azioni Immediate -->
<div class="form-section">
<div class="form-section-title">
Azioni Immediate di Contenimento
<span class="help-icon">?
<div class="tooltip">
<div class="tooltip-title">HELP DELLA SEZIONE</div>
Checklist delle azioni di contenimento immediato. Per SEV-1: contenimento entro 30 minuti. Documentare ogni azione con timestamp.
</div>
</span>
</div>
<ul class="checklist">
<li>
<input type="checkbox" id="action1">
<label for="action1">Sistema compromesso isolato dalla rete</label>
</li>
<li>
<input type="checkbox" id="action2">
<label for="action2">Account compromessi bloccati</label>
</li>
<li>
<input type="checkbox" id="action3">
<label for="action3">IP/domini malevoli bloccati su firewall</label>
</li>
<li>
<input type="checkbox" id="action4">
<label for="action4">Evidenze preservate (snapshot, log, memoria)</label>
</li>
<li>
<input type="checkbox" id="action5">
<label for="action5">Monitoraggio intensivo attivato su sistemi correlati</label>
</li>
<li>
<input type="checkbox" id="action6">
<label for="action6">Crisis Team / IRT notificato</label>
</li>
<li>
<input type="checkbox" id="action7">
<label for="action7">Direzione informata (per SEV-1/SEV-2)</label>
</li>
</ul>
<div class="form-group" style="margin-top: 20px;">
<label class="form-label">
Note Azioni di Contenimento
</label>
<textarea class="form-textarea" id="containmentNotes" placeholder="Descrivere in dettaglio le azioni di contenimento effettuate con timestamp..."></textarea>
</div>
</div>
<!-- Sezione 5: Collegamento Risk -->
<div class="form-section">
<div class="form-section-title">
Collegamento Risk Assessment
<span class="help-icon">?
<div class="tooltip">
<div class="tooltip-title">HELP DELLA SEZIONE</div>
Collegare l'incidente a rischi già identificati nel risk assessment (Org.05). Se l'incidente evidenzia un nuovo rischio, verrà creato automaticamente.
</div>
</span>
</div>
<div class="form-group">
<label class="form-label">
Collegamento N°RISK
</label>
<input type="text" class="form-input" id="riskId" placeholder="es. RISK-2024-015">
<div class="form-help">Codice rischio da Org.05 - Risk Assessment. Lasciare vuoto se nuovo rischio</div>
</div>
<div class="form-group">
<label class="form-label">
Root Cause Preliminare
</label>
<select class="form-select" id="rootCause">
<option value="">Da determinare</option>
<option value="vulnerability">Vulnerabilità non patchata</option>
<option value="misconfiguration">Errore di configurazione</option>
<option value="weak-credentials">Credenziali deboli/compromesse</option>
<option value="missing-controls">Controlli di sicurezza mancanti</option>
<option value="human-error">Errore umano</option>
<option value="third-party">Compromissione terza parte</option>
<option value="zero-day">Zero-day exploit</option>
<option value="insider">Insider threat</option>
</select>
</div>
</div>
<!-- Form Actions -->
<div class="form-section">
<div class="form-actions">
<a href="incident-dashboard.html" class="btn">Annulla</a>
<button type="button" class="btn" onclick="saveDraft()">Salva Bozza</button>
<button type="submit" class="btn btn-danger">🚨 Crea Incidente e Attiva Gestione</button>
</div>
</div>
</form>
</div>
<script>
// Verifica tipo soggetto
function checkSubjectType() {
const subjectType = sessionStorage.getItem('nis2_subject_type');
if (!subjectType) {
window.location.href = 'incident-gate.html';
return;
}
// Nascondi IS-4 per soggetti importanti
if (subjectType === 'important') {
const is4Option = document.getElementById('is4ClassOption');
if (is4Option) is4Option.style.display = 'none';
}
}
// Imposta data/ora corrente
function setCurrentDateTime() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
document.getElementById('detectionTime').value = `${year}-${month}-${day}T${hours}:${minutes}`;
}
// Selezione severità
function selectSeverity(severity) {
// Rimuovi selezione precedente
document.querySelectorAll('.severity-option').forEach(opt => {
opt.classList.remove('selected');
});
// Seleziona nuovo
event.target.closest('.severity-option').classList.add('selected');
document.getElementById('severity').value = severity;
// Mostra help specifico
const helpTexts = {
'sev-1': '🚨 <strong>CRITICO:</strong> Interruzione totale servizio critico, ransomware attivo, esfiltrazione massiva dati, compromissione infrastruttura core. <strong>Attivazione immediata Crisis Team.</strong>',
'sev-2': '⚠️ <strong>ALTO:</strong> Interruzione parziale servizio rilevante, compromissione account privilegiato, malware attivo non contenuto, accesso non autorizzato a dati riservati.',
'sev-3': ' <strong>MEDIO:</strong> Degrado prestazioni servizio rilevante, malware contenuto automaticamente, tentativo intrusione bloccato ma significativo.',
'sev-4': '📝 <strong>BASSO:</strong> Anomalia senza impatto operativo, tentativo attacco bloccato (routine), violazione policy minore.'
};
document.getElementById('severityHelp').innerHTML = helpTexts[severity];
// Mostra alert per SEV-1
const sev1Alert = document.getElementById('sev1Alert');
if (severity === 'sev-1') {
sev1Alert.style.display = 'block';
} else {
sev1Alert.style.display = 'none';
}
}
// Aggiorna help classificazione
function updateClassificationHelp() {
const classification = document.getElementById('classification').value;
const helpTexts = {
'is-1': 'Interruzione operativa o degrado significativo di un servizio rilevante NIS2. <strong>Notifica CSIRT obbligatoria.</strong>',
'is-2': 'Accesso non autorizzato, modifica, esfiltrazione o violazione riservatezza dati. <strong>Notifica CSIRT obbligatoria.</strong> Verificare anche obbligo GDPR.',
'is-3': 'Incidente che si propaga a soggetti terzi o impatta la catena di fornitura. <strong>Notifica CSIRT obbligatoria.</strong>',
'is-4': 'Serie di incidenti correlati con impatto cumulativo significativo. <strong>Notifica CSIRT obbligatoria SOLO per soggetti ESSENZIALI.</strong>',
'not-significant': 'Incidente gestito internamente, senza obbligo di notifica CSIRT. Documentare comunque nel registro incidenti.'
};
const helpElement = document.getElementById('classificationHelp');
helpElement.innerHTML = helpTexts[classification] || '';
// Mostra/nascondi alert CSIRT
const csirtAlert = document.getElementById('csirtAlert');
if (['is-1', 'is-2', 'is-3', 'is-4'].includes(classification)) {
csirtAlert.style.display = 'block';
} else {
csirtAlert.style.display = 'none';
}
}
// Toggle GDPR
function toggleGDPR() {
const personalData = document.getElementById('personalData').value;
const gdprGroup = document.getElementById('gdprGroup');
if (personalData === 'yes') {
gdprGroup.style.display = 'block';
} else {
gdprGroup.style.display = 'none';
}
}
// Salva bozza
function saveDraft() {
alert('Bozza salvata con successo. Puoi continuare la compilazione in seguito.');
}
// Submit form
document.getElementById('incidentForm').addEventListener('submit', function(e) {
e.preventDefault();
// Validazione
const severity = document.getElementById('severity').value;
if (!severity) {
alert('Seleziona la severità dell\'incidente');
return;
}
// Genera codice incidente
const year = new Date().getFullYear();
const progressive = Math.floor(Math.random() * 900) + 100; // Simulato
const incidentCode = `INC-${year}-${progressive}`;
// Simula creazione incidente
const tooltip = document.createElement('div');
tooltip.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: var(--bg-secondary);
border: 2px solid var(--success);
border-radius: 8px;
padding: 32px;
box-shadow: 0 8px 24px rgba(0,0,0,0.5);
z-index: 10000;
max-width: 500px;
text-align: center;
`;
tooltip.innerHTML = `
<div style="font-size: 48px; margin-bottom: 16px;"></div>
<h2 style="color: var(--success); margin-bottom: 16px;">Incidente Creato con Successo</h2>
<p style="color: var(--text-secondary); margin-bottom: 8px;">Codice incidente:</p>
<p style="font-size: 24px; font-weight: 700; color: var(--text-primary); margin-bottom: 24px; font-family: monospace;">${incidentCode}</p>
<div style="background: var(--bg-tertiary); padding: 16px; border-radius: 6px; margin-bottom: 24px; text-align: left;">
<p style="font-size: 13px; color: var(--text-secondary); margin-bottom: 12px;"><strong>Prossime azioni:</strong></p>
<ul style="font-size: 13px; color: var(--text-secondary); margin-left: 20px;">
<li>Incident Response Team notificato</li>
<li>Timeline di gestione avviata</li>
${severity === 'sev-1' ? '<li style="color: var(--danger); font-weight: 600;">Crisis Team attivato</li>' : ''}
${['is-1', 'is-2', 'is-3'].includes(document.getElementById('classification').value) ? '<li style="color: var(--warning); font-weight: 600;">Preallarme CSIRT da inviare entro 24h</li>' : ''}
</ul>
</div>
<button onclick="window.location.href='incident-detail.html?id=${incidentCode}'"
style="padding: 12px 24px; background: var(--accent-primary); border: none; border-radius: 6px; color: white; font-weight: 600; cursor: pointer; margin-right: 12px;">
Vai all'Incidente
</button>
<button onclick="window.location.href='incident-dashboard.html'"
style="padding: 12px 24px; background: var(--bg-tertiary); border: 1px solid var(--border-color); border-radius: 6px; color: var(--text-primary); font-weight: 600; cursor: pointer;">
Torna alla Dashboard
</button>
`;
document.body.appendChild(tooltip);
});
// Inizializza
window.addEventListener('DOMContentLoaded', function() {
checkSubjectType();
setCurrentDateTime();
});
</script>
</body>
</html>

View File

@ -0,0 +1,758 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Notifiche CSIRT - INC-2024-047</title>
<style>
:root {
--bg-primary: #0d1117;
--bg-secondary: #161b22;
--bg-tertiary: #1c2128;
--border-color: #30363d;
--text-primary: #c9d1d9;
--text-secondary: #8b949e;
--accent-primary: #58a6ff;
--accent-secondary: #1f6feb;
--success: #3fb950;
--warning: #d29922;
--danger: #f85149;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
background-color: var(--bg-primary);
color: var(--text-primary);
line-height: 1.6;
overflow-y: auto;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
background-color: var(--bg-secondary);
border-bottom: 1px solid var(--border-color);
padding: 24px 0;
margin-bottom: 32px;
position: sticky;
top: 0;
z-index: 100;
}
.header-content {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.header h1 {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
}
.breadcrumb {
font-size: 13px;
color: var(--text-secondary);
margin-top: 4px;
}
.breadcrumb a {
color: var(--accent-primary);
text-decoration: none;
}
.btn {
padding: 8px 16px;
background-color: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-radius: 6px;
color: var(--text-primary);
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
text-decoration: none;
}
.btn:hover {
border-color: var(--accent-primary);
color: var(--accent-primary);
}
.btn-primary {
background-color: var(--accent-primary);
border-color: var(--accent-primary);
color: white;
}
.btn-primary:hover {
background-color: var(--accent-secondary);
color: white;
}
.btn-danger {
background-color: var(--danger);
border-color: var(--danger);
color: white;
}
.btn-danger:hover {
background-color: #dc2626;
}
.alert-box {
background-color: rgba(210, 153, 34, 0.1);
border: 1px solid var(--warning);
border-left: 4px solid var(--warning);
border-radius: 6px;
padding: 20px;
margin-bottom: 24px;
}
.alert-box.danger {
background-color: rgba(248, 81, 73, 0.1);
border-color: var(--danger);
border-left-color: var(--danger);
}
.alert-box.success {
background-color: rgba(63, 185, 80, 0.1);
border-color: var(--success);
border-left-color: var(--success);
}
.alert-title {
font-weight: 600;
margin-bottom: 8px;
font-size: 15px;
display: flex;
align-items: center;
gap: 8px;
}
.alert-content {
font-size: 13px;
color: var(--text-secondary);
line-height: 1.6;
}
.section {
background-color: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 24px;
margin-bottom: 24px;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
padding-bottom: 16px;
border-bottom: 1px solid var(--border-color);
}
.section-title {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
}
.timeline-horizontal {
display: flex;
justify-content: space-between;
margin-bottom: 32px;
position: relative;
}
.timeline-horizontal::before {
content: '';
position: absolute;
top: 20px;
left: 0;
right: 0;
height: 2px;
background: var(--border-color);
z-index: 0;
}
.timeline-step {
flex: 1;
text-align: center;
position: relative;
z-index: 1;
}
.timeline-circle {
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--bg-tertiary);
border: 2px solid var(--border-color);
margin: 0 auto 12px;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 14px;
}
.timeline-step.completed .timeline-circle {
background: var(--success);
border-color: var(--success);
color: white;
}
.timeline-step.active .timeline-circle {
background: var(--warning);
border-color: var(--warning);
color: white;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(210, 153, 34, 0.7); }
50% { box-shadow: 0 0 0 10px rgba(210, 153, 34, 0); }
}
.timeline-label {
font-size: 12px;
color: var(--text-secondary);
font-weight: 600;
margin-bottom: 4px;
}
.timeline-deadline {
font-size: 11px;
color: var(--text-secondary);
}
.timeline-step.completed .timeline-label,
.timeline-step.active .timeline-label {
color: var(--text-primary);
}
.form-section {
background-color: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 20px;
margin-bottom: 20px;
}
.form-group {
margin-bottom: 20px;
}
.form-label {
display: block;
font-size: 13px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 8px;
}
.form-input, .form-select, .form-textarea {
width: 100%;
padding: 10px 12px;
background-color: var(--bg-primary);
border: 1px solid var(--border-color);
border-radius: 6px;
color: var(--text-primary);
font-size: 14px;
font-family: inherit;
}
.form-input:focus, .form-select:focus, .form-textarea:focus {
outline: none;
border-color: var(--accent-primary);
}
.form-textarea {
resize: vertical;
min-height: 120px;
}
.form-help {
font-size: 12px;
color: var(--text-secondary);
margin-top: 6px;
}
.info-box {
background-color: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 16px;
margin-bottom: 16px;
}
.info-box-title {
font-weight: 600;
color: var(--accent-primary);
margin-bottom: 8px;
font-size: 13px;
}
.info-box-content {
font-size: 13px;
color: var(--text-secondary);
line-height: 1.6;
}
.data-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
.data-table th {
background-color: var(--bg-primary);
color: var(--text-secondary);
font-weight: 600;
text-align: left;
padding: 10px 12px;
border: 1px solid var(--border-color);
font-size: 11px;
text-transform: uppercase;
}
.data-table td {
padding: 10px 12px;
border: 1px solid var(--border-color);
color: var(--text-primary);
}
@media (max-width: 768px) {
.timeline-horizontal {
flex-direction: column;
}
.timeline-horizontal::before {
display: none;
}
.timeline-step {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
text-align: left;
}
.timeline-circle {
margin: 0;
}
}
</style>
</head>
<body>
<div class="header">
<div class="header-content">
<div>
<h1>📡 Notifiche CSIRT Italia - INC-2024-047</h1>
<div class="breadcrumb">
<a href="dashboard.html">Dashboard NIS2</a> /
<a href="incident-dashboard.html">Gestione Incidenti</a> /
<a href="incident-detail.html?id=INC-2024-047">INC-2024-047</a> /
Notifiche CSIRT
</div>
</div>
<a href="incident-detail.html?id=INC-2024-047" class="btn">← Torna all'Incidente</a>
</div>
</div>
<div class="container">
<!-- Alert Scadenza -->
<div class="alert-box danger">
<div class="alert-title">
⏰ Scadenza Imminente - Preallarme CSIRT
</div>
<div class="alert-content">
Il <strong>preallarme</strong> deve essere inviato al CSIRT Italia entro <strong>24 ore</strong> dalla conoscenza dell'incidente significativo.<br>
<strong>Scadenza:</strong> 2024-03-07 08:15 (rimangono <span style="color: var(--danger); font-weight: 700;">18 ore</span>)<br>
<strong>Canale:</strong> Portale CSIRT Italia (https://www.csirt.gov.it) o PEC dedicata
</div>
</div>
<!-- Timeline Notifiche -->
<div class="timeline-horizontal">
<div class="timeline-step active">
<div class="timeline-circle">1</div>
<div class="timeline-label">Preallarme</div>
<div class="timeline-deadline">Entro 24h</div>
</div>
<div class="timeline-step">
<div class="timeline-circle">2</div>
<div class="timeline-label">Notifica Completa</div>
<div class="timeline-deadline">Entro 72h</div>
</div>
<div class="timeline-step">
<div class="timeline-circle">3</div>
<div class="timeline-label">Relazioni Intermedie</div>
<div class="timeline-deadline">Su richiesta</div>
</div>
<div class="timeline-step">
<div class="timeline-circle">4</div>
<div class="timeline-label">Relazione Finale</div>
<div class="timeline-deadline">Entro 1 mese</div>
</div>
</div>
<!-- Sezione Preallarme -->
<div class="section">
<div class="section-header">
<div class="section-title">1. Preallarme (Entro 24 ore)</div>
</div>
<div class="info-box">
<div class="info-box-title">📋 Contenuto Minimo Preallarme</div>
<div class="info-box-content">
<ul style="margin-left: 20px; margin-top: 8px;">
<li>Se l'incidente è presumibilmente causato da atti illegittimi o malevoli</li>
<li>Se può avere impatto transfrontaliero</li>
<li>Descrizione preliminare dell'incidente</li>
</ul>
</div>
</div>
<form id="prealertForm">
<div class="form-section">
<h3 style="font-size: 16px; font-weight: 600; margin-bottom: 16px;">Dati Incidente</h3>
<div class="form-group">
<label class="form-label">Codice Incidente</label>
<input type="text" class="form-input" value="INC-2024-047" readonly>
</div>
<div class="form-group">
<label class="form-label">Data/Ora Rilevazione</label>
<input type="text" class="form-input" value="2024-03-06 08:15" readonly>
</div>
<div class="form-group">
<label class="form-label">Classificazione NIS2</label>
<input type="text" class="form-input" value="IS-2 - Impatto su integrità/riservatezza dati" readonly>
</div>
</div>
<div class="form-section">
<h3 style="font-size: 16px; font-weight: 600; margin-bottom: 16px;">Informazioni Preallarme</h3>
<div class="form-group">
<label class="form-label">Atti Illegittimi o Malevoli?</label>
<select class="form-select" required>
<option value="yes" selected>Sì - Attacco ransomware</option>
<option value="no">No</option>
<option value="unknown">Da determinare</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Impatto Transfrontaliero?</label>
<select class="form-select" required>
<option value="no" selected>No</option>
<option value="yes"></option>
<option value="possible">Possibile</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Descrizione Preliminare Incidente</label>
<textarea class="form-textarea" required>Tentativo di attacco ransomware (famiglia BlackCat/ALPHV) rilevato su server ERP di produzione. L'EDR ha bloccato l'esecuzione del malware in fase iniziale. Il vettore di attacco è stato identificato come email phishing con allegato malevolo, sfruttando credenziali utente precedentemente compromesse. Il sistema è stato immediatamente isolato. Nessuna crittografia dati avvenuta. Nessuna esfiltrazione rilevata. Incidente contenuto con successo.</textarea>
<div class="form-help">Fornire una descrizione sintetica ma completa dell'incidente</div>
</div>
<div class="form-group">
<label class="form-label">Sistemi/Servizi Impattati</label>
<textarea class="form-textarea">- Server ERP-PROD-01 (HW-SRV-001)
- Applicazione SAP ERP (SW-ERP-001)
- Servizio gestionale aziendale (SRV-ERP-001)
- 45 utenti interni impattati da interruzione servizio</textarea>
</div>
<div class="form-group">
<label class="form-label">Azioni di Contenimento Adottate</label>
<textarea class="form-textarea">- Isolamento immediato server dalla rete (08:22)
- Blocco account utente compromesso (09:30)
- Blocco IP C2 su firewall (09:45)
- Preservazione evidenze forensi (08:35)
- Attivazione Crisis Team e CISO (08:25)
- Contenimento confermato entro 2.2 ore dalla rilevazione</textarea>
</div>
</div>
<div class="form-section">
<h3 style="font-size: 16px; font-weight: 600; margin-bottom: 16px;">Dati Organizzazione</h3>
<div class="form-group">
<label class="form-label">Nome Organizzazione</label>
<input type="text" class="form-input" value="[Nome Azienda]" required>
</div>
<div class="form-group">
<label class="form-label">Settore NIS2</label>
<select class="form-select" required>
<option value="">Seleziona settore...</option>
<option value="energia">Energia</option>
<option value="trasporti">Trasporti</option>
<option value="bancario">Bancario</option>
<option value="sanitario">Sanitario</option>
<option value="digitale" selected>Infrastrutture Digitali</option>
<option value="acqua">Acqua Potabile</option>
<option value="altro">Altro</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Referente Tecnico</label>
<input type="text" class="form-input" value="CISO - P. Lombardi" required>
</div>
<div class="form-group">
<label class="form-label">Email Contatto</label>
<input type="email" class="form-input" value="ciso@azienda.it" required>
</div>
<div class="form-group">
<label class="form-label">Telefono Contatto</label>
<input type="tel" class="form-input" value="+39 02 1234567" required>
</div>
</div>
<div style="display: flex; gap: 12px; justify-content: flex-end; padding-top: 24px; border-top: 1px solid var(--border-color);">
<button type="button" class="btn" onclick="savePrealertDraft()">Salva Bozza</button>
<button type="button" class="btn btn-primary" onclick="previewPrealert()">Anteprima</button>
<button type="submit" class="btn btn-danger">📡 Invia Preallarme CSIRT</button>
</div>
</form>
</div>
<!-- Sezione Notifica Completa -->
<div class="section">
<div class="section-header">
<div class="section-title">2. Notifica Completa (Entro 72 ore)</div>
</div>
<div class="alert-box">
<div class="alert-title">⏸️ In Attesa Invio Preallarme</div>
<div class="alert-content">
La notifica completa può essere compilata dopo l'invio del preallarme.<br>
<strong>Scadenza:</strong> 2024-03-09 08:15 (72 ore dalla rilevazione)
</div>
</div>
<div class="info-box">
<div class="info-box-title">📋 Contenuto Notifica Completa</div>
<div class="info-box-content">
<ul style="margin-left: 20px; margin-top: 8px;">
<li>Aggiornamento informazioni del preallarme</li>
<li>Valutazione iniziale dell'incidente</li>
<li>Gravità e impatto dettagliato</li>
<li>Indicatori di compromissione (IoC) se disponibili</li>
<li>Misure di contenimento adottate</li>
<li>Stato corrente della gestione</li>
</ul>
</div>
</div>
<button class="btn" disabled style="opacity: 0.5;">Compila Notifica Completa (disponibile dopo preallarme)</button>
</div>
<!-- Sezione Relazione Finale -->
<div class="section">
<div class="section-header">
<div class="section-title">4. Relazione Finale (Entro 1 mese)</div>
</div>
<div class="alert-box">
<div class="alert-title">⏸️ In Attesa Chiusura Incidente</div>
<div class="alert-content">
La relazione finale deve essere inviata entro 1 mese dalla notifica completa,
o entro 1 mese dalla gestione completa dell'incidente se ancora in corso.<br>
<strong>Scadenza stimata:</strong> 2024-04-09
</div>
</div>
<div class="info-box">
<div class="info-box-title">📋 Contenuto Relazione Finale</div>
<div class="info-box-content">
<ul style="margin-left: 20px; margin-top: 8px;">
<li>Descrizione dettagliata dell'incidente</li>
<li>Causa radice (root cause) identificata o probabile</li>
<li>Misure di mitigazione applicate</li>
<li>Impatto transfrontaliero (se applicabile)</li>
<li>Tipo di minaccia o causa radice</li>
<li>Misure correttive in corso e pianificate</li>
<li>Lesson learned e raccomandazioni</li>
</ul>
</div>
</div>
<button class="btn" disabled style="opacity: 0.5;">Compila Relazione Finale (disponibile dopo chiusura incidente)</button>
</div>
<!-- Storico Comunicazioni -->
<div class="section">
<div class="section-header">
<div class="section-title">Storico Comunicazioni CSIRT</div>
</div>
<table class="data-table">
<thead>
<tr>
<th>Data/Ora</th>
<th>Tipo Comunicazione</th>
<th>Canale</th>
<th>Protocollo/Riferimento</th>
<th>Stato</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="6" style="text-align: center; color: var(--text-secondary); padding: 24px;">
Nessuna comunicazione inviata ancora
</td>
</tr>
</tbody>
</table>
</div>
</div>
<script>
function savePrealertDraft() {
alert('Bozza preallarme salvata con successo. Puoi continuare la compilazione in seguito.');
}
function previewPrealert() {
const tooltip = document.createElement('div');
tooltip.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 24px;
box-shadow: 0 8px 24px rgba(0,0,0,0.5);
z-index: 10000;
max-width: 700px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
`;
tooltip.innerHTML = `
<h3 style="margin-bottom: 16px; color: var(--text-primary);">📄 Anteprima Preallarme CSIRT Italia</h3>
<div style="background: var(--bg-tertiary); padding: 20px; border-radius: 6px; margin-bottom: 16px; font-size: 13px; line-height: 1.8; font-family: monospace;">
<strong>PREALLARME INCIDENTE SIGNIFICATIVO NIS2</strong><br>
<strong>Protocollo:</strong> [Generato automaticamente]<br>
<strong>Data invio:</strong> ${new Date().toLocaleString('it-IT')}<br>
<br>
<strong>ORGANIZZAZIONE:</strong> [Nome Azienda]<br>
<strong>Settore:</strong> Infrastrutture Digitali<br>
<strong>Tipologia soggetto:</strong> ${sessionStorage.getItem('nis2_subject_type') === 'essential' ? 'Essenziale' : 'Importante'}<br>
<br>
<strong>CODICE INCIDENTE:</strong> INC-2024-047<br>
<strong>Data/Ora rilevazione:</strong> 2024-03-06 08:15<br>
<strong>Classificazione:</strong> IS-2 (Impatto integrità/riservatezza dati)<br>
<br>
<strong>NATURA INCIDENTE:</strong><br>
☑ Atti illegittimi o malevoli: SÌ (Attacco ransomware)<br>
☐ Impatto transfrontaliero: NO<br>
<br>
<strong>DESCRIZIONE PRELIMINARE:</strong><br>
Tentativo di attacco ransomware (famiglia BlackCat/ALPHV) rilevato su server ERP di produzione.
L'EDR ha bloccato l'esecuzione del malware in fase iniziale. Il vettore di attacco è stato identificato
come email phishing con allegato malevolo, sfruttando credenziali utente precedentemente compromesse.
Il sistema è stato immediatamente isolato. Nessuna crittografia dati avvenuta. Nessuna esfiltrazione rilevata.
Incidente contenuto con successo.<br>
<br>
<strong>SISTEMI IMPATTATI:</strong><br>
- Server ERP-PROD-01<br>
- Applicazione SAP ERP<br>
- 45 utenti interni<br>
<br>
<strong>AZIONI CONTENIMENTO:</strong><br>
- Isolamento server (2.2h)<br>
- Blocco account compromesso<br>
- Blocco IP C2<br>
- Evidenze preservate<br>
<br>
<strong>REFERENTE:</strong> CISO - P. Lombardi<br>
<strong>Contatto:</strong> ciso@azienda.it / +39 02 1234567<br>
<br>
<em>Notifica completa seguirà entro 72 ore.</em>
</div>
<div style="display: flex; gap: 12px;">
<button onclick="this.parentElement.parentElement.remove()" style="flex: 1; padding: 10px; background: var(--bg-tertiary); border: 1px solid var(--border-color); border-radius: 6px; color: var(--text-primary); font-weight: 600; cursor: pointer;">
Chiudi
</button>
<button onclick="this.parentElement.parentElement.remove(); document.getElementById('prealertForm').dispatchEvent(new Event('submit'));" style="flex: 1; padding: 10px; background: var(--danger); border: none; border-radius: 6px; color: white; font-weight: 600; cursor: pointer;">
Conferma e Invia
</button>
</div>
`;
document.body.appendChild(tooltip);
}
document.getElementById('prealertForm').addEventListener('submit', function(e) {
e.preventDefault();
const tooltip = document.createElement('div');
tooltip.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: var(--bg-secondary);
border: 2px solid var(--success);
border-radius: 8px;
padding: 32px;
box-shadow: 0 8px 24px rgba(0,0,0,0.5);
z-index: 10000;
max-width: 500px;
text-align: center;
`;
tooltip.innerHTML = `
<div style="font-size: 48px; margin-bottom: 16px;"></div>
<h2 style="color: var(--success); margin-bottom: 16px;">Preallarme Inviato con Successo</h2>
<p style="color: var(--text-secondary); margin-bottom: 8px;">Protocollo CSIRT:</p>
<p style="font-size: 20px; font-weight: 700; color: var(--text-primary); margin-bottom: 24px; font-family: monospace;">CSIRT-2024-03-06-${Math.floor(Math.random() * 10000)}</p>
<div style="background: var(--bg-tertiary); padding: 16px; border-radius: 6px; margin-bottom: 24px; text-align: left;">
<p style="font-size: 13px; color: var(--text-secondary); margin-bottom: 12px;"><strong>Dettagli invio:</strong></p>
<ul style="font-size: 13px; color: var(--text-secondary); margin-left: 20px;">
<li>Data/Ora invio: ${new Date().toLocaleString('it-IT')}</li>
<li>Canale: Portale CSIRT Italia</li>
<li>Ricevuta PEC: Confermata</li>
<li>Scadenza rispettata: ✅ SÌ (entro 24h)</li>
</ul>
<p style="font-size: 13px; color: var(--text-secondary); margin-top: 12px;">
<strong>Prossimo step:</strong> Notifica completa entro 2024-03-09 08:15 (72h)
</p>
</div>
<button onclick="window.location.href='incident-detail.html?id=INC-2024-047'" style="padding: 12px 24px; background: var(--accent-primary); border: none; border-radius: 6px; color: white; font-weight: 600; cursor: pointer;">
Torna all'Incidente
</button>
`;
document.body.appendChild(tooltip);
});
</script>
</body>
</html>

View File

@ -0,0 +1,803 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Post-Incident Review - INC-2024-047</title>
<style>
:root {
--bg-primary: #0d1117;
--bg-secondary: #161b22;
--bg-tertiary: #1c2128;
--border-color: #30363d;
--text-primary: #c9d1d9;
--text-secondary: #8b949e;
--accent-primary: #58a6ff;
--success: #3fb950;
--warning: #d29922;
--danger: #f85149;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
background-color: var(--bg-primary);
color: var(--text-primary);
line-height: 1.6;
overflow-y: auto;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
.header {
background-color: var(--bg-secondary);
border-bottom: 1px solid var(--border-color);
padding: 24px 0;
margin-bottom: 32px;
position: sticky;
top: 0;
z-index: 100;
}
.header-content {
max-width: 1400px;
margin: 0 auto;
padding: 0 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.header h1 {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
}
.breadcrumb {
font-size: 13px;
color: var(--text-secondary);
margin-top: 4px;
}
.breadcrumb a {
color: var(--accent-primary);
text-decoration: none;
}
.btn {
padding: 8px 16px;
background-color: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-radius: 6px;
color: var(--text-primary);
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
text-decoration: none;
}
.btn:hover {
border-color: var(--accent-primary);
}
.btn-primary {
background-color: var(--accent-primary);
border-color: var(--accent-primary);
color: white;
}
.btn-primary:hover {
background-color: #1f6feb;
}
.alert-box {
background-color: rgba(88, 166, 255, 0.1);
border: 1px solid var(--accent-primary);
border-left: 4px solid var(--accent-primary);
border-radius: 6px;
padding: 20px;
margin-bottom: 24px;
}
.alert-title {
font-weight: 600;
margin-bottom: 8px;
font-size: 15px;
}
.alert-content {
font-size: 13px;
color: var(--text-secondary);
line-height: 1.6;
}
.section {
background-color: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 24px;
margin-bottom: 24px;
}
.section-title {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 1px solid var(--border-color);
}
.metrics-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
margin-bottom: 24px;
}
.metric-card {
background-color: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 16px;
text-align: center;
}
.metric-label {
font-size: 11px;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 8px;
}
.metric-value {
font-size: 28px;
font-weight: 700;
color: var(--text-primary);
}
.metric-value.success {
color: var(--success);
}
.metric-value.warning {
color: var(--warning);
}
.form-group {
margin-bottom: 20px;
}
.form-label {
display: block;
font-size: 13px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 8px;
}
.form-textarea {
width: 100%;
padding: 12px;
background-color: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-radius: 6px;
color: var(--text-primary);
font-size: 14px;
font-family: inherit;
resize: vertical;
min-height: 100px;
}
.form-textarea:focus {
outline: none;
border-color: var(--accent-primary);
}
.data-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
.data-table th {
background-color: var(--bg-tertiary);
color: var(--text-secondary);
font-weight: 600;
text-align: left;
padding: 10px 12px;
border: 1px solid var(--border-color);
font-size: 11px;
text-transform: uppercase;
}
.data-table td {
padding: 10px 12px;
border: 1px solid var(--border-color);
color: var(--text-primary);
}
.priority-high {
color: var(--danger);
font-weight: 700;
}
.priority-medium {
color: var(--warning);
font-weight: 600;
}
.priority-low {
color: var(--text-secondary);
}
.status-badge {
display: inline-block;
padding: 4px 8px;
border-radius: 3px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
}
.status-planned {
background-color: rgba(88, 166, 255, 0.2);
color: var(--accent-primary);
border: 1px solid var(--accent-primary);
}
.status-progress {
background-color: rgba(210, 153, 34, 0.2);
color: var(--warning);
border: 1px solid var(--warning);
}
.status-completed {
background-color: rgba(63, 185, 80, 0.2);
color: var(--success);
border: 1px solid var(--success);
}
</style>
</head>
<body>
<div class="header">
<div class="header-content">
<div>
<h1>📊 Post-Incident Review - INC-2024-047</h1>
<div class="breadcrumb">
<a href="dashboard.html">Dashboard NIS2</a> /
<a href="incident-dashboard.html">Gestione Incidenti</a> /
<a href="incident-detail.html?id=INC-2024-047">INC-2024-047</a> /
Post-Incident Review
</div>
</div>
<div style="display: flex; gap: 8px;">
<button class="btn" onclick="exportPIR()">📄 Esporta Report</button>
<a href="incident-detail.html?id=INC-2024-047" class="btn">← Torna all'Incidente</a>
</div>
</div>
</div>
<div class="container">
<div class="alert-box">
<div class="alert-title"> Post-Incident Review (RC.CO-03)</div>
<div class="alert-content">
Analisi completa dell'incidente per identificare lesson learned e azioni di miglioramento.
Da completare entro <strong>2 settimane dalla chiusura</strong> per incidenti SEV-1/SEV-2.
<br><br>
<strong>Partecipanti richiesti:</strong> Incident Response Team, CISO, Responsabili Divisione impattate, Direzione (per SEV-1)
</div>
</div>
<!-- Metriche Incidente -->
<div class="section">
<div class="section-title">Metriche Incidente</div>
<div class="metrics-grid">
<div class="metric-card">
<div class="metric-label">TTD (Time to Detect)</div>
<div class="metric-value success">0.5h</div>
<div style="font-size: 11px; color: var(--text-secondary); margin-top: 4px;">Target: <2h </div>
</div>
<div class="metric-card">
<div class="metric-label">TTC (Time to Contain)</div>
<div class="metric-value success">2.2h</div>
<div style="font-size: 11px; color: var(--text-secondary); margin-top: 4px;">Target SEV-1: <1h </div>
</div>
<div class="metric-card">
<div class="metric-label">TTR (Time to Recover)</div>
<div class="metric-value success">30.25h</div>
<div style="font-size: 11px; color: var(--text-secondary); margin-top: 4px;">RTO: ≤48h ✅</div>
</div>
<div class="metric-card">
<div class="metric-label">Downtime Totale</div>
<div class="metric-value warning">30h 15m</div>
<div style="font-size: 11px; color: var(--text-secondary); margin-top: 4px;">45 utenti impattati</div>
</div>
<div class="metric-card">
<div class="metric-label">Costo Stimato</div>
<div class="metric-value">€12.5K</div>
<div style="font-size: 11px; color: var(--text-secondary); margin-top: 4px;">Diretto + indiretto</div>
</div>
<div class="metric-card">
<div class="metric-label">Conformità Notifiche</div>
<div class="metric-value success">100%</div>
<div style="font-size: 11px; color: var(--text-secondary); margin-top: 4px;">CSIRT entro 24h ✅</div>
</div>
</div>
</div>
<!-- Root Cause Analysis -->
<div class="section">
<div class="section-title">Root Cause Analysis (5 Whys)</div>
<div style="background: var(--bg-tertiary); padding: 20px; border-radius: 6px; margin-bottom: 20px;">
<div style="margin-bottom: 16px;">
<strong style="color: var(--accent-primary);">Problema:</strong> Tentativo di attacco ransomware su server ERP
</div>
<div style="margin-bottom: 12px;">
<strong style="color: var(--text-primary);">1. Perché è successo?</strong><br>
<span style="color: var(--text-secondary);">→ Perché un utente ha aperto un allegato malevolo da email phishing</span>
</div>
<div style="margin-bottom: 12px;">
<strong style="color: var(--text-primary);">2. Perché l'utente ha aperto l'allegato?</strong><br>
<span style="color: var(--text-secondary);">→ Perché l'email sembrava legittima (spoofing fornitore) e l'utente non ha riconosciuto i segnali di phishing</span>
</div>
<div style="margin-bottom: 12px;">
<strong style="color: var(--text-primary);">3. Perché l'utente non ha riconosciuto il phishing?</strong><br>
<span style="color: var(--text-secondary);">→ Perché la formazione awareness sulla sicurezza non era sufficientemente frequente e pratica</span>
</div>
<div style="margin-bottom: 12px;">
<strong style="color: var(--text-primary);">4. Perché le credenziali dell'utente erano già compromesse?</strong><br>
<span style="color: var(--text-secondary);">→ Perché l'utente utilizzava la stessa password su servizi esterni (credential stuffing attack non rilevato)</span>
</div>
<div>
<strong style="color: var(--text-primary);">5. Perché non c'era MFA obbligatoria su account amministrativi?</strong><br>
<span style="color: var(--text-secondary);">→ Perché la policy MFA era in fase di rollout graduale e non ancora applicata a tutti gli account admin</span>
</div>
</div>
<div style="background: var(--bg-tertiary); padding: 16px; border-radius: 6px; border-left: 4px solid var(--danger);">
<strong style="color: var(--danger);">ROOT CAUSE IDENTIFICATA:</strong><br>
<span style="color: var(--text-secondary); font-size: 13px;">
1. MFA non obbligatoria su tutti gli account amministrativi<br>
2. Formazione awareness non sufficientemente efficace<br>
3. Monitoraggio credential stuffing non attivo<br>
4. Policy password debole (riutilizzo su servizi esterni)
</span>
</div>
</div>
<!-- Valutazione Efficacia -->
<div class="section">
<div class="section-title">Valutazione Efficacia Risposta</div>
<table class="data-table">
<thead>
<tr>
<th>Fase</th>
<th>Valutazione</th>
<th>Punti di Forza</th>
<th>Aree di Miglioramento</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Rilevazione</strong></td>
<td><span style="color: var(--success); font-weight: 600;">✅ Eccellente</span></td>
<td>EDR ha bloccato immediatamente il malware. TTD: 0.5h</td>
<td>Nessuna - detection efficace</td>
</tr>
<tr>
<td><strong>Triage</strong></td>
<td><span style="color: var(--success); font-weight: 600;">✅ Buono</span></td>
<td>Classificazione corretta entro 1h. Crisis Team attivato tempestivamente</td>
<td>Processo decisionale può essere ulteriormente accelerato</td>
</tr>
<tr>
<td><strong>Contenimento</strong></td>
<td><span style="color: var(--warning); font-weight: 600;">⚠️ Sufficiente</span></td>
<td>Isolamento rapido. Nessuna propagazione</td>
<td>TTC 2.2h > target 1h per SEV-1. Procedure di isolamento da ottimizzare</td>
</tr>
<tr>
<td><strong>Eradicazione</strong></td>
<td><span style="color: var(--success); font-weight: 600;">✅ Buono</span></td>
<td>Rimozione completa minaccia. Hardening applicato</td>
<td>Analisi forense richiede più tempo del previsto</td>
</tr>
<tr>
<td><strong>Ripristino</strong></td>
<td><span style="color: var(--success); font-weight: 600;">✅ Buono</span></td>
<td>TTR 30.25h < RTO 48h. Backup immutabile efficace</td>
<td>Processo di verifica post-ripristino può essere standardizzato</td>
</tr>
<tr>
<td><strong>Comunicazioni</strong></td>
<td><span style="color: var(--success); font-weight: 600;">✅ Eccellente</span></td>
<td>Preallarme CSIRT entro 24h. Comunicazioni interne tempestive</td>
<td>Template comunicazioni possono essere pre-compilati</td>
</tr>
</tbody>
</table>
</div>
<!-- Gap Identificati -->
<div class="section">
<div class="section-title">Gap Identificati</div>
<div class="form-group">
<label class="form-label">1. GAP TECNICI</label>
<textarea class="form-textarea" readonly>• MFA non obbligatoria su tutti gli account amministrativi
• Monitoraggio credential stuffing assente
• Filtri email anti-phishing non sufficientemente efficaci
• Procedure di isolamento automatico non implementate
• Tempo di contenimento superiore al target per SEV-1</textarea>
</div>
<div class="form-group">
<label class="form-label">2. GAP PROCEDURALI</label>
<textarea class="form-textarea" readonly>• Policy MFA in rollout graduale (non completata)
• Procedura di isolamento rapido non documentata
• Checklist contenimento SEV-1 non disponibile in formato rapido
• Processo di escalation Crisis Team può essere ottimizzato
• Template comunicazioni non pre-compilati</textarea>
</div>
<div class="form-group">
<label class="form-label">3. GAP ORGANIZZATIVI</label>
<textarea class="form-textarea" readonly>• Formazione awareness non sufficientemente frequente
• Simulazioni phishing non regolari
• Policy password debole (riutilizzo su servizi esterni non vietato esplicitamente)
• Competenze forensics interne limitate (dipendenza da consulenti esterni)</textarea>
</div>
<div class="form-group">
<label class="form-label">4. GAP FORMATIVI</label>
<textarea class="form-textarea" readonly>• Utenti non addestrati a riconoscere phishing avanzato
• Account amministrativi senza formazione specifica su minacce mirate
• Awareness su riutilizzo password insufficiente
• Esercitazioni tabletop incident response non regolari</textarea>
</div>
</div>
<!-- Azioni Correttive -->
<div class="section">
<div class="section-title">Piano Azioni Correttive</div>
<table class="data-table">
<thead>
<tr>
<th>ID</th>
<th>Azione Correttiva</th>
<th>Categoria</th>
<th>Priorità</th>
<th>Responsabile</th>
<th>Scadenza</th>
<th>Org. Rif.</th>
<th>Stato</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>AC-001</strong></td>
<td>Implementare MFA obbligatoria su TUTTI gli account amministrativi</td>
<td>Tecnica</td>
<td><span class="priority-high">ALTA</span></td>
<td>IT Manager</td>
<td>2024-03-20</td>
<td>Org.03</td>
<td><span class="status-badge status-progress">In corso</span></td>
</tr>
<tr>
<td><strong>AC-002</strong></td>
<td>Implementare monitoraggio credential stuffing su SIEM</td>
<td>Tecnica</td>
<td><span class="priority-high">ALTA</span></td>
<td>SOC Lead</td>
<td>2024-03-25</td>
<td>Org.09</td>
<td><span class="status-badge status-planned">Pianificata</span></td>
</tr>
<tr>
<td><strong>AC-003</strong></td>
<td>Rafforzare filtri anti-phishing email gateway</td>
<td>Tecnica</td>
<td><span class="priority-high">ALTA</span></td>
<td>IT Security</td>
<td>2024-03-15</td>
<td>Org.08</td>
<td><span class="status-badge status-progress">In corso</span></td>
</tr>
<tr>
<td><strong>AC-004</strong></td>
<td>Implementare isolamento automatico endpoint compromessi</td>
<td>Tecnica</td>
<td><span class="priority-medium">MEDIA</span></td>
<td>IT Security</td>
<td>2024-04-15</td>
<td>Org.08</td>
<td><span class="status-badge status-planned">Pianificata</span></td>
</tr>
<tr>
<td><strong>AC-005</strong></td>
<td>Aggiornare policy password (vietare riutilizzo su servizi esterni)</td>
<td>Procedurale</td>
<td><span class="priority-high">ALTA</span></td>
<td>CISO</td>
<td>2024-03-10</td>
<td>Org.03</td>
<td><span class="status-badge status-progress">In corso</span></td>
</tr>
<tr>
<td><strong>AC-006</strong></td>
<td>Creare checklist rapida contenimento SEV-1</td>
<td>Procedurale</td>
<td><span class="priority-medium">MEDIA</span></td>
<td>CISO</td>
<td>2024-03-15</td>
<td>Org.10</td>
<td><span class="status-badge status-planned">Pianificata</span></td>
</tr>
<tr>
<td><strong>AC-007</strong></td>
<td>Preparare template comunicazioni pre-compilati</td>
<td>Procedurale</td>
<td><span class="priority-low">BASSA</span></td>
<td>CISO</td>
<td>2024-04-30</td>
<td>Org.10</td>
<td><span class="status-badge status-planned">Pianificata</span></td>
</tr>
<tr>
<td><strong>AC-008</strong></td>
<td>Aumentare frequenza formazione awareness (mensile)</td>
<td>Formativa</td>
<td><span class="priority-high">ALTA</span></td>
<td>HR + CISO</td>
<td>2024-04-01</td>
<td>Org.04</td>
<td><span class="status-badge status-planned">Pianificata</span></td>
</tr>
<tr>
<td><strong>AC-009</strong></td>
<td>Implementare simulazioni phishing trimestrali</td>
<td>Formativa</td>
<td><span class="priority-medium">MEDIA</span></td>
<td>CISO</td>
<td>2024-04-15</td>
<td>Org.04</td>
<td><span class="status-badge status-planned">Pianificata</span></td>
</tr>
<tr>
<td><strong>AC-010</strong></td>
<td>Formazione specifica account amministrativi su minacce mirate</td>
<td>Formativa</td>
<td><span class="priority-medium">MEDIA</span></td>
<td>CISO</td>
<td>2024-03-30</td>
<td>Org.04</td>
<td><span class="status-badge status-planned">Pianificata</span></td>
</tr>
<tr>
<td><strong>AC-011</strong></td>
<td>Aggiornare risk assessment con nuovo scenario ransomware</td>
<td>Organizzativa</td>
<td><span class="priority-high">ALTA</span></td>
<td>CISO</td>
<td>2024-03-20</td>
<td>Org.05</td>
<td><span class="status-badge status-planned">Pianificata</span></td>
</tr>
<tr>
<td><strong>AC-012</strong></td>
<td>Pianificare esercitazione tabletop ransomware</td>
<td>Organizzativa</td>
<td><span class="priority-medium">MEDIA</span></td>
<td>CISO</td>
<td>2024-05-31</td>
<td>Org.08</td>
<td><span class="status-badge status-planned">Pianificata</span></td>
</tr>
</tbody>
</table>
<div style="margin-top: 24px; padding: 16px; background: var(--bg-tertiary); border-radius: 6px;">
<strong style="color: var(--text-primary);">Riepilogo Azioni:</strong><br>
<div style="display: flex; gap: 24px; margin-top: 12px; font-size: 13px;">
<div>
<span style="color: var(--text-secondary);">Totali:</span>
<strong style="color: var(--text-primary);"> 12</strong>
</div>
<div>
<span style="color: var(--text-secondary);">Alta priorità:</span>
<strong style="color: var(--danger);"> 6</strong>
</div>
<div>
<span style="color: var(--text-secondary);">Media priorità:</span>
<strong style="color: var(--warning);"> 5</strong>
</div>
<div>
<span style="color: var(--text-secondary);">Bassa priorità:</span>
<strong style="color: var(--text-secondary);"> 1</strong>
</div>
</div>
</div>
</div>
<!-- Raccomandazioni -->
<div class="section">
<div class="section-title">Raccomandazioni e Lesson Learned</div>
<div style="background: var(--bg-tertiary); padding: 20px; border-radius: 6px; margin-bottom: 16px;">
<h4 style="font-size: 14px; font-weight: 600; color: var(--success); margin-bottom: 12px;">
✅ COSA HA FUNZIONATO BENE
</h4>
<ul style="font-size: 13px; color: var(--text-secondary); line-height: 1.8; margin-left: 20px;">
<li>EDR ha bloccato immediatamente il ransomware prima della crittografia</li>
<li>Backup immutabile ha permesso ripristino rapido e sicuro</li>
<li>Crisis Team ha risposto prontamente e in modo coordinato</li>
<li>Comunicazioni CSIRT rispettate nei tempi (preallarme entro 24h)</li>
<li>Nessuna propagazione dell'attacco ad altri sistemi</li>
<li>Preservazione evidenze forensi efficace</li>
</ul>
</div>
<div style="background: var(--bg-tertiary); padding: 20px; border-radius: 6px; margin-bottom: 16px;">
<h4 style="font-size: 14px; font-weight: 600; color: var(--warning); margin-bottom: 12px;">
⚠️ COSA MIGLIORARE
</h4>
<ul style="font-size: 13px; color: var(--text-secondary); line-height: 1.8; margin-left: 20px;">
<li>Tempo di contenimento (2.2h) superiore al target per SEV-1 (<1h)</li>
<li>MFA non era obbligatoria su account amministrativi</li>
<li>Formazione awareness non sufficientemente efficace</li>
<li>Monitoraggio credential stuffing assente</li>
<li>Procedure di isolamento rapido non documentate</li>
</ul>
</div>
<div style="background: var(--bg-tertiary); padding: 20px; border-radius: 6px;">
<h4 style="font-size: 14px; font-weight: 600; color: var(--accent-primary); margin-bottom: 12px;">
💡 LESSON LEARNED CHIAVE
</h4>
<ol style="font-size: 13px; color: var(--text-secondary); line-height: 1.8; margin-left: 20px;">
<li><strong>MFA è fondamentale:</strong> L'assenza di MFA su account amministrativi è stata la vulnerabilità critica. Implementazione immediata obbligatoria.</li>
<li><strong>Backup immutabile salva:</strong> Il backup immutabile ha evitato perdita dati e permesso ripristino rapido. Investimento essenziale.</li>
<li><strong>EDR efficace ma non sufficiente:</strong> EDR ha bloccato il malware ma non ha prevenuto il phishing. Serve approccio multi-layer.</li>
<li><strong>Formazione continua necessaria:</strong> Awareness sporadica non è efficace. Serve formazione continua e simulazioni pratiche.</li>
<li><strong>Procedure automatiche riducono TTC:</strong> Isolamento manuale ha richiesto tempo. Automazione può ridurre significativamente TTC.</li>
<li><strong>Comunicazioni tempestive cruciali:</strong> Rispetto scadenze CSIRT e comunicazioni interne hanno evitato complicazioni legali/reputazionali.</li>
</ol>
</div>
</div>
<!-- Finalizzazione -->
<div class="section">
<div class="section-title">Finalizzazione Post-Incident Review</div>
<div style="background: var(--bg-tertiary); padding: 16px; border-radius: 6px; margin-bottom: 20px;">
<strong style="color: var(--text-primary);">Partecipanti PIR:</strong><br>
<ul style="font-size: 13px; color: var(--text-secondary); margin-top: 8px; margin-left: 20px;">
<li>CISO - P. Lombardi (coordinatore)</li>
<li>SOC Lead - L. Verdi</li>
<li>System Admin - G. Rossi</li>
<li>Forensics Team - A. Neri</li>
<li>IT Manager - M. Bianchi</li>
<li>Responsabile Divisione Finance - S. Neri</li>
<li>Direzione - CdA Representative</li>
</ul>
</div>
<div style="display: flex; gap: 12px; justify-content: flex-end; padding-top: 24px; border-top: 1px solid var(--border-color);">
<button class="btn" onclick="savePIRDraft()">Salva Bozza</button>
<button class="btn" onclick="exportPIR()">📄 Esporta Report</button>
<button class="btn btn-primary" onclick="completePIR()">✅ Finalizza PIR e Chiudi Incidente</button>
</div>
</div>
</div>
<script>
function savePIRDraft() {
alert('Bozza Post-Incident Review salvata con successo.');
}
function exportPIR() {
const tooltip = document.createElement('div');
tooltip.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 24px;
box-shadow: 0 8px 24px rgba(0,0,0,0.5);
z-index: 10000;
max-width: 500px;
`;
tooltip.innerHTML = `
<h3 style="margin-bottom: 16px; color: var(--text-primary);">📄 Export Report Post-Incident Review</h3>
<p style="color: var(--text-secondary); margin-bottom: 16px; font-size: 13px;">
Report completo PIR per incidente INC-2024-047
</p>
<div style="background: var(--bg-tertiary); padding: 16px; border-radius: 4px; margin-bottom: 16px; font-family: monospace; font-size: 12px;">
📄 PIR_INC-2024-047_Report_Completo.pdf<br>
📄 PIR_INC-2024-047_Azioni_Correttive.xlsx<br>
📄 PIR_INC-2024-047_Executive_Summary.pdf<br>
📄 PIR_INC-2024-047_Lesson_Learned.docx
</div>
<button onclick="this.parentElement.remove()" style="width: 100%; padding: 10px; background: var(--accent-primary); border: none; border-radius: 6px; color: white; font-weight: 600; cursor: pointer;">
Chiudi
</button>
`;
document.body.appendChild(tooltip);
}
function completePIR() {
const tooltip = document.createElement('div');
tooltip.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: var(--bg-secondary);
border: 2px solid var(--success);
border-radius: 8px;
padding: 32px;
box-shadow: 0 8px 24px rgba(0,0,0,0.5);
z-index: 10000;
max-width: 600px;
text-align: center;
`;
tooltip.innerHTML = `
<div style="font-size: 48px; margin-bottom: 16px;"></div>
<h2 style="color: var(--success); margin-bottom: 16px;">Post-Incident Review Completata</h2>
<p style="color: var(--text-secondary); margin-bottom: 24px; font-size: 14px;">
Incidente INC-2024-047 ufficialmente chiuso
</p>
<div style="background: var(--bg-tertiary); padding: 20px; border-radius: 6px; margin-bottom: 24px; text-align: left;">
<p style="font-size: 13px; color: var(--text-secondary); margin-bottom: 12px;"><strong>Riepilogo Finale:</strong></p>
<ul style="font-size: 13px; color: var(--text-secondary); margin-left: 20px;">
<li>12 azioni correttive identificate e assegnate</li>
<li>6 azioni ad alta priorità in tracking</li>
<li>Risk assessment aggiornato (Org.05)</li>
<li>Lesson learned condivise con team</li>
<li>Report PIR distribuito a stakeholder</li>
<li>Relazione finale CSIRT da completare</li>
</ul>
<p style="font-size: 13px; color: var(--text-secondary); margin-top: 16px;">
<strong>Prossimi step:</strong> Monitoraggio azioni correttive tramite sistema non conformità (ID.IM-01)
</p>
</div>
<div style="display: flex; gap: 12px;">
<button onclick="window.location.href='incident-dashboard.html'" style="flex: 1; padding: 12px; background: var(--bg-tertiary); border: 1px solid var(--border-color); border-radius: 6px; color: var(--text-primary); font-weight: 600; cursor: pointer;">
Dashboard Incidenti
</button>
<button onclick="window.location.href='incident-notification.html?id=INC-2024-047&action=final'" style="flex: 1; padding: 12px; background: var(--accent-primary); border: none; border-radius: 6px; color: white; font-weight: 600; cursor: pointer;">
Completa Relazione Finale CSIRT
</button>
</div>
`;
document.body.appendChild(tooltip);
}
</script>
</body>
</html>

View File

@ -0,0 +1,660 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ripristino Servizi - INC-2024-047</title>
<style>
:root {
--bg-primary: #0d1117;
--bg-secondary: #161b22;
--bg-tertiary: #1c2128;
--border-color: #30363d;
--text-primary: #c9d1d9;
--text-secondary: #8b949e;
--accent-primary: #58a6ff;
--success: #3fb950;
--warning: #d29922;
--danger: #f85149;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
background-color: var(--bg-primary);
color: var(--text-primary);
line-height: 1.6;
overflow-y: auto;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
.header {
background-color: var(--bg-secondary);
border-bottom: 1px solid var(--border-color);
padding: 24px 0;
margin-bottom: 32px;
position: sticky;
top: 0;
z-index: 100;
}
.header-content {
max-width: 1400px;
margin: 0 auto;
padding: 0 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.header h1 {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
}
.breadcrumb {
font-size: 13px;
color: var(--text-secondary);
margin-top: 4px;
}
.breadcrumb a {
color: var(--accent-primary);
text-decoration: none;
}
.btn {
padding: 8px 16px;
background-color: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-radius: 6px;
color: var(--text-primary);
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
text-decoration: none;
}
.btn:hover {
border-color: var(--accent-primary);
}
.btn-success {
background-color: var(--success);
border-color: var(--success);
color: white;
}
.btn-success:hover {
background-color: #2ea043;
}
.alert-box {
background-color: rgba(88, 166, 255, 0.1);
border: 1px solid var(--accent-primary);
border-left: 4px solid var(--accent-primary);
border-radius: 6px;
padding: 20px;
margin-bottom: 24px;
}
.alert-title {
font-weight: 600;
margin-bottom: 8px;
font-size: 15px;
}
.alert-content {
font-size: 13px;
color: var(--text-secondary);
line-height: 1.6;
}
.section {
background-color: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 24px;
margin-bottom: 24px;
}
.section-title {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 1px solid var(--border-color);
}
.checklist {
list-style: none;
}
.checklist li {
padding: 12px;
background-color: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-radius: 4px;
margin-bottom: 8px;
display: flex;
align-items: center;
gap: 12px;
}
.checklist input[type="checkbox"] {
width: 20px;
height: 20px;
cursor: pointer;
}
.checklist li.completed {
background-color: rgba(63, 185, 80, 0.1);
border-color: var(--success);
}
.priority-badge {
display: inline-block;
padding: 4px 8px;
border-radius: 3px;
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
}
.priority-1 {
background-color: rgba(248, 81, 73, 0.2);
color: var(--danger);
border: 1px solid var(--danger);
}
.priority-2 {
background-color: rgba(210, 153, 34, 0.2);
color: var(--warning);
border: 1px solid var(--warning);
}
.data-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
.data-table th {
background-color: var(--bg-tertiary);
color: var(--text-secondary);
font-weight: 600;
text-align: left;
padding: 10px 12px;
border: 1px solid var(--border-color);
font-size: 11px;
text-transform: uppercase;
}
.data-table td {
padding: 10px 12px;
border: 1px solid var(--border-color);
color: var(--text-primary);
}
.data-table tr:hover {
background-color: var(--bg-tertiary);
}
</style>
</head>
<body>
<div class="header">
<div class="header-content">
<div>
<h1>🔄 Ripristino Servizi - INC-2024-047</h1>
<div class="breadcrumb">
<a href="dashboard.html">Dashboard NIS2</a> /
<a href="incident-dashboard.html">Gestione Incidenti</a> /
<a href="incident-detail.html?id=INC-2024-047">INC-2024-047</a> /
Ripristino
</div>
</div>
<a href="incident-detail.html?id=INC-2024-047" class="btn">← Torna all'Incidente</a>
</div>
</div>
<div class="container">
<div class="alert-box">
<div class="alert-title"> Processo di Ripristino (RC.RP-01)</div>
<div class="alert-content">
Ripristino graduale dei servizi secondo priorità BIA. Ogni sistema deve essere verificato per integrità,
funzionalità e sicurezza prima del ripristino in produzione. Monitoraggio intensivo post-ripristino per 72 ore.
</div>
</div>
<!-- Priorità Ripristino -->
<div class="section">
<div class="section-title">Priorità Ripristino (secondo BIA Org.08)</div>
<table class="data-table">
<thead>
<tr>
<th>Priorità</th>
<th>Sistema/Servizio</th>
<th>RTO Dichiarato</th>
<th>Stato</th>
<th>ETA Ripristino</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="priority-badge priority-1">P1 - Critico</span></td>
<td><strong>Server ERP-PROD-01</strong><br>Sistema gestionale principale</td>
<td>≤4h</td>
<td><span style="color: var(--warning);">🔄 In ripristino</span></td>
<td>2024-03-08 14:00</td>
<td><button class="btn" onclick="viewRecoveryDetails('erp')">Dettagli</button></td>
</tr>
<tr>
<td><span class="priority-badge priority-1">P1 - Critico</span></td>
<td><strong>Applicazione SAP ERP</strong><br>Software gestionale</td>
<td>≤4h</td>
<td><span style="color: var(--text-secondary);">⏸️ In attesa server</span></td>
<td>2024-03-08 16:00</td>
<td><button class="btn" disabled>In attesa</button></td>
</tr>
</tbody>
</table>
</div>
<!-- Checklist Ripristino -->
<div class="section">
<div class="section-title">Checklist Ripristino Server ERP-PROD-01</div>
<h3 style="font-size: 16px; font-weight: 600; margin-bottom: 16px; color: var(--text-primary);">1. Valutazione Pre-Ripristino</h3>
<ul class="checklist">
<li class="completed">
<input type="checkbox" checked disabled>
<span>Minaccia completamente rimossa ed eradicata</span>
</li>
<li class="completed">
<input type="checkbox" checked disabled>
<span>Nessun indicatore di compromissione residuo</span>
</li>
<li class="completed">
<input type="checkbox" checked disabled>
<span>Backup verificato e integro (hash matching)</span>
</li>
<li class="completed">
<input type="checkbox" checked disabled>
<span>Strategia di ripristino definita: Ripristino da backup immutabile</span>
</li>
</ul>
<h3 style="font-size: 16px; font-weight: 600; margin: 24px 0 16px; color: var(--text-primary);">2. Ripristino Sistema</h3>
<ul class="checklist">
<li class="completed">
<input type="checkbox" checked disabled>
<span>Ripristino da backup immutabile avviato (2024-03-07 10:00)</span>
</li>
<li class="completed">
<input type="checkbox" checked disabled>
<span>Sistema operativo ripristinato e verificato</span>
</li>
<li>
<input type="checkbox" id="check1" onchange="updateProgress()">
<span>Applicazione ERP ripristinata e configurata</span>
</li>
<li>
<input type="checkbox" id="check2" onchange="updateProgress()">
<span>Database ripristinato e verificato integrità</span>
</li>
<li>
<input type="checkbox" id="check3" onchange="updateProgress()">
<span>Patch di sicurezza applicati (ultimi aggiornamenti)</span>
</li>
<li>
<input type="checkbox" id="check4" onchange="updateProgress()">
<span>Configurazione hardening applicata (baseline CIS)</span>
</li>
</ul>
<h3 style="font-size: 16px; font-weight: 600; margin: 24px 0 16px; color: var(--text-primary);">3. Verifica Integrità e Sicurezza</h3>
<ul class="checklist">
<li>
<input type="checkbox" id="check5" onchange="updateProgress()">
<span>Integrità dati verificata (confronto checksum con backup)</span>
</li>
<li>
<input type="checkbox" id="check6" onchange="updateProgress()">
<span>Test funzionali applicativi completati con successo</span>
</li>
<li>
<input type="checkbox" id="check7" onchange="updateProgress()">
<span>Scansione antimalware completa (nessuna minaccia rilevata)</span>
</li>
<li>
<input type="checkbox" id="check8" onchange="updateProgress()">
<span>Verifica assenza IoC residui</span>
</li>
<li>
<input type="checkbox" id="check9" onchange="updateProgress()">
<span>Configurazione EDR verificata e attiva</span>
</li>
<li>
<input type="checkbox" id="check10" onchange="updateProgress()">
<span>Log attivi e integrati nel SIEM</span>
</li>
</ul>
<h3 style="font-size: 16px; font-weight: 600; margin: 24px 0 16px; color: var(--text-primary);">4. Ripristino Accessi</h3>
<ul class="checklist">
<li>
<input type="checkbox" id="check11" onchange="updateProgress()">
<span>Reset credenziali per tutti gli account potenzialmente compromessi</span>
</li>
<li>
<input type="checkbox" id="check12" onchange="updateProgress()">
<span>MFA verificata e funzionante per tutti gli account admin</span>
</li>
<li>
<input type="checkbox" id="check13" onchange="updateProgress()">
<span>Riattivazione accessi graduale per ruolo (admin → power user → user)</span>
</li>
<li>
<input type="checkbox" id="check14" onchange="updateProgress()">
<span>Monitoraggio accessi post-ripristino attivato</span>
</li>
</ul>
<h3 style="font-size: 16px; font-weight: 600; margin: 24px 0 16px; color: var(--text-primary);">5. Ripristino Connettività</h3>
<ul class="checklist">
<li>
<input type="checkbox" id="check15" onchange="updateProgress()">
<span>Regole firewall aggiornate (IP C2 bloccati permanentemente)</span>
</li>
<li>
<input type="checkbox" id="check16" onchange="updateProgress()">
<span>Segmentazione rete verificata</span>
</li>
<li>
<input type="checkbox" id="check17" onchange="updateProgress()">
<span>Test connettività end-to-end completati</span>
</li>
<li>
<input type="checkbox" id="check18" onchange="updateProgress()">
<span>Monitoraggio traffico anomalo attivo</span>
</li>
</ul>
<h3 style="font-size: 16px; font-weight: 600; margin: 24px 0 16px; color: var(--text-primary);">6. Validazione Finale</h3>
<ul class="checklist">
<li>
<input type="checkbox" id="check19" onchange="updateProgress()">
<span>Sign-off tecnico: Team Incident Response</span>
</li>
<li>
<input type="checkbox" id="check20" onchange="updateProgress()">
<span>Sign-off business: Responsabile Divisione</span>
</li>
<li>
<input type="checkbox" id="check21" onchange="updateProgress()">
<span>Sign-off sicurezza: CISO</span>
</li>
<li>
<input type="checkbox" id="check22" onchange="updateProgress()">
<span>Dichiarazione di ripristino completato</span>
</li>
</ul>
<div style="margin-top: 24px; padding: 20px; background: var(--bg-tertiary); border-radius: 6px; display: flex; justify-content: space-between; align-items: center;">
<div>
<div style="font-size: 13px; color: var(--text-secondary); margin-bottom: 8px;">Progresso Ripristino</div>
<div style="font-size: 24px; font-weight: 700; color: var(--text-primary);" id="progressText">9%</div>
</div>
<button class="btn btn-success" onclick="completeRecovery()" id="completeBtn" disabled style="opacity: 0.5;">
✅ Completa Ripristino
</button>
</div>
</div>
<!-- Monitoraggio Post-Ripristino -->
<div class="section">
<div class="section-title">Piano Monitoraggio Post-Ripristino</div>
<div style="background: var(--bg-tertiary); padding: 16px; border-radius: 6px; margin-bottom: 16px;">
<h4 style="font-size: 14px; font-weight: 600; color: var(--text-primary); margin-bottom: 12px;">
📊 Monitoraggio Intensivo (72 ore)
</h4>
<ul style="font-size: 13px; color: var(--text-secondary); line-height: 1.8; margin-left: 20px;">
<li>Monitoraggio continuo 24/7 da SOC</li>
<li>Soglie di alerting ridotte (maggiore sensibilità)</li>
<li>Threat hunting mirato ogni 8 ore</li>
<li>Report giornaliero stato sistema</li>
<li>Verifica integrità file system ogni 12 ore</li>
</ul>
</div>
<div style="background: var(--bg-tertiary); padding: 16px; border-radius: 6px; margin-bottom: 16px;">
<h4 style="font-size: 14px; font-weight: 600; color: var(--text-primary); margin-bottom: 12px;">
📈 Monitoraggio Elevato (30 giorni)
</h4>
<ul style="font-size: 13px; color: var(--text-secondary); line-height: 1.8; margin-left: 20px;">
<li>Monitoraggio continuo con priorità alta</li>
<li>Threat hunting settimanale</li>
<li>Report settimanale a CISO</li>
<li>Verifica periodica IoC</li>
<li>Analisi comportamentale utenti e processi</li>
</ul>
</div>
<table class="data-table">
<thead>
<tr>
<th>Metrica</th>
<th>Baseline Pre-Incidente</th>
<th>Target Post-Ripristino</th>
<th>Frequenza Check</th>
</tr>
</thead>
<tbody>
<tr>
<td>CPU Usage</td>
<td>45-60%</td>
<td>45-60% (±5%)</td>
<td>Ogni 5 minuti</td>
</tr>
<tr>
<td>Memory Usage</td>
<td>70-75%</td>
<td>70-75% (±5%)</td>
<td>Ogni 5 minuti</td>
</tr>
<tr>
<td>Network Traffic</td>
<td>2-5 Gbps</td>
<td>2-5 Gbps</td>
<td>Continuo</td>
</tr>
<tr>
<td>Failed Login Attempts</td>
<td><5/ora</td>
<td><3/ora</td>
<td>Real-time</td>
</tr>
<tr>
<td>Processi Anomali</td>
<td>0</td>
<td>0</td>
<td>Ogni 15 minuti</td>
</tr>
<tr>
<td>Connessioni Esterne Sospette</td>
<td>0</td>
<td>0</td>
<td>Real-time</td>
</tr>
</tbody>
</table>
</div>
<!-- Comunicazioni Ripristino -->
<div class="section">
<div class="section-title">Comunicazioni Ripristino Completato</div>
<div style="background: var(--bg-tertiary); padding: 16px; border-radius: 6px; margin-bottom: 16px;">
<h4 style="font-size: 14px; font-weight: 600; color: var(--text-primary); margin-bottom: 12px;">
📧 Template Comunicazione Interna
</h4>
<div style="font-size: 13px; color: var(--text-secondary); line-height: 1.8; font-family: monospace; background: var(--bg-primary); padding: 12px; border-radius: 4px;">
<strong>Oggetto:</strong> Ripristino Servizio ERP Completato<br><br>
Gentili colleghi,<br><br>
Vi informiamo che il servizio ERP è stato completamente ripristinato e
risulta nuovamente operativo a partire dalle ore [DATA/ORA].<br><br>
<strong>Azioni richieste agli utenti:</strong><br>
• Effettuare reset password al primo accesso<br>
• Verificare che MFA sia attiva<br>
• Segnalare immediatamente qualsiasi comportamento anomalo<br><br>
Il sistema è sotto monitoraggio intensivo per le prossime 72 ore.<br><br>
Per qualsiasi problema contattare l'helpdesk.<br><br>
Grazie per la collaborazione.<br>
IT Security Team
</div>
</div>
<div style="background: var(--bg-tertiary); padding: 16px; border-radius: 6px;">
<h4 style="font-size: 14px; font-weight: 600; color: var(--text-primary); margin-bottom: 12px;">
📡 Aggiornamento CSIRT Italia
</h4>
<div style="font-size: 13px; color: var(--text-secondary); line-height: 1.8;">
Aggiornare la notifica CSIRT con informazioni sul ripristino completato:
<ul style="margin-left: 20px; margin-top: 8px;">
<li>Data/ora ripristino completato</li>
<li>Servizi ripristinati</li>
<li>Downtime totale effettivo vs RTO</li>
<li>Misure di hardening applicate</li>
<li>Piano di monitoraggio post-ripristino</li>
</ul>
</div>
</div>
</div>
</div>
<script>
function updateProgress() {
const total = 22;
let checked = 2; // I primi 2 sono già checked
for (let i = 1; i <= 22; i++) {
const checkbox = document.getElementById('check' + i);
if (checkbox && checkbox.checked) {
checked++;
checkbox.parentElement.classList.add('completed');
} else if (checkbox) {
checkbox.parentElement.classList.remove('completed');
}
}
const percentage = Math.round((checked / total) * 100);
document.getElementById('progressText').textContent = percentage + '%';
const completeBtn = document.getElementById('completeBtn');
if (percentage === 100) {
completeBtn.disabled = false;
completeBtn.style.opacity = '1';
} else {
completeBtn.disabled = true;
completeBtn.style.opacity = '0.5';
}
}
function completeRecovery() {
const tooltip = document.createElement('div');
tooltip.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: var(--bg-secondary);
border: 2px solid var(--success);
border-radius: 8px;
padding: 32px;
box-shadow: 0 8px 24px rgba(0,0,0,0.5);
z-index: 10000;
max-width: 600px;
text-align: center;
`;
tooltip.innerHTML = `
<div style="font-size: 48px; margin-bottom: 16px;"></div>
<h2 style="color: var(--success); margin-bottom: 16px;">Ripristino Completato con Successo</h2>
<div style="background: var(--bg-tertiary); padding: 20px; border-radius: 6px; margin-bottom: 24px; text-align: left;">
<p style="font-size: 13px; color: var(--text-secondary); margin-bottom: 12px;"><strong>Riepilogo Ripristino:</strong></p>
<table style="width: 100%; font-size: 13px; color: var(--text-secondary);">
<tr>
<td><strong>Sistema:</strong></td>
<td>ERP-PROD-01</td>
</tr>
<tr>
<td><strong>Data ripristino:</strong></td>
<td>${new Date().toLocaleString('it-IT')}</td>
</tr>
<tr>
<td><strong>Downtime totale:</strong></td>
<td>30h 15m</td>
</tr>
<tr>
<td><strong>RTO dichiarato:</strong></td>
<td>≤48h</td>
</tr>
<tr>
<td><strong>Conformità RTO:</strong></td>
<td style="color: var(--success); font-weight: 600;">✅ Rispettato</td>
</tr>
<tr>
<td><strong>TTR (Time to Recover):</strong></td>
<td>30.25h</td>
</tr>
</table>
<p style="font-size: 13px; color: var(--text-secondary); margin-top: 16px;">
<strong>Stato:</strong> Sistema operativo, monitoraggio intensivo attivo per 72 ore.
</p>
</div>
<div style="background: var(--bg-tertiary); padding: 16px; border-radius: 6px; margin-bottom: 24px; text-align: left;">
<p style="font-size: 13px; color: var(--text-secondary); margin-bottom: 12px;"><strong>Prossime azioni:</strong></p>
<ul style="font-size: 13px; color: var(--text-secondary); margin-left: 20px;">
<li>Comunicazione utenti inviata</li>
<li>Aggiornamento notifica CSIRT</li>
<li>Monitoraggio intensivo 72h avviato</li>
<li>Pianificazione Post-Incident Review</li>
</ul>
</div>
<div style="display: flex; gap: 12px;">
<button onclick="window.location.href='incident-detail.html?id=INC-2024-047'" style="flex: 1; padding: 12px; background: var(--bg-tertiary); border: 1px solid var(--border-color); border-radius: 6px; color: var(--text-primary); font-weight: 600; cursor: pointer;">
Torna all'Incidente
</button>
<button onclick="window.location.href='incident-pir.html?id=INC-2024-047'" style="flex: 1; padding: 12px; background: var(--accent-primary); border: none; border-radius: 6px; color: white; font-weight: 600; cursor: pointer;">
Avvia Post-Incident Review
</button>
</div>
`;
document.body.appendChild(tooltip);
}
function viewRecoveryDetails(system) {
alert('Dettagli processo di ripristino per: ' + system);
}
// Inizializza progresso
window.addEventListener('DOMContentLoaded', updateProgress);
</script>
</body>
</html>

View File

@ -0,0 +1,66 @@
-- ============================================================================
-- Migration 020 - Asset Relevance Scoring (NIS2 GV.OC-04)
-- ----------------------------------------------------------------------------
-- Aggiunge la metodologia di scoring rilevanza NIS2 (0-100, 6 criteri pesati)
-- alla tabella assets. Adattata dai mockup docs/nis2/assets.html +
-- doc-relevant-systems.html (Determina/metodologia CdA, soglia >=40 rilevante).
--
-- Criteri: C1 Criticita Operativa (0-25), C2 Impatto Interruzione (0-25),
-- C3 Dati Trattati (0-20), C4 Dipendenze (0-15),
-- C5 Esposizione (0-10), C6 Obblighi Normativi (0-5).
-- Classificazione: >=80 critico | 60-79 alto | 40-59 medio | 20-39 basso | <20 trascurabile
-- Rilevanza NIS2: score >= 40.
--
-- IMPORTANTE (vedi CLAUDE.md / memoria): MySQL 8 Ubuntu NON supporta
-- "ADD COLUMN IF NOT EXISTS". Questo script usa una stored procedure idempotente
-- che verifica information_schema prima di ogni ALTER. Rilanciabile senza danni.
-- Eseguire con: mysql -h localhost nis2_agile_db -e "source docs/sql/020_asset_relevance.sql"
-- ============================================================================
DELIMITER //
DROP PROCEDURE IF EXISTS _mig020_add_col //
CREATE PROCEDURE _mig020_add_col(IN col VARCHAR(64), IN ddl TEXT)
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'assets' AND COLUMN_NAME = col
) THEN
SET @sql = CONCAT('ALTER TABLE assets ADD COLUMN ', ddl);
PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st;
END IF;
END //
DELIMITER ;
CALL _mig020_add_col('relevance_score', "relevance_score TINYINT UNSIGNED NULL COMMENT 'Punteggio rilevanza NIS2 0-100'");
CALL _mig020_add_col('relevance_criteria', "relevance_criteria JSON NULL COMMENT 'Dettaglio punteggi C1-C6 per audit'");
CALL _mig020_add_col('relevance_class', "relevance_class ENUM('critico','alto','medio','basso','trascurabile') NULL");
CALL _mig020_add_col('is_nis2_relevant', "is_nis2_relevant TINYINT(1) NOT NULL DEFAULT 0 COMMENT '1 se score >= 40'");
CALL _mig020_add_col('relevance_assessed_at', "relevance_assessed_at DATETIME NULL");
CALL _mig020_add_col('relevance_assessed_by', "relevance_assessed_by INT NULL");
DROP PROCEDURE IF EXISTS _mig020_add_col;
-- Indice per filtri "sistemi rilevanti" (idempotente via check)
DELIMITER //
DROP PROCEDURE IF EXISTS _mig020_add_idx //
CREATE PROCEDURE _mig020_add_idx()
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'assets' AND INDEX_NAME = 'idx_relevance'
) THEN
ALTER TABLE assets ADD INDEX idx_relevance (is_nis2_relevant, relevance_score);
END IF;
END //
DELIMITER ;
CALL _mig020_add_idx();
DROP PROCEDURE IF EXISTS _mig020_add_idx;
-- ROLLBACK (manuale):
-- ALTER TABLE assets
-- DROP COLUMN relevance_score, DROP COLUMN relevance_criteria,
-- DROP COLUMN relevance_class, DROP COLUMN is_nis2_relevant,
-- DROP COLUMN relevance_assessed_at, DROP COLUMN relevance_assessed_by,
-- DROP INDEX idx_relevance;

View File

@ -0,0 +1,36 @@
-- ============================================================================
-- Migration 021 - Tassonomia Incidenti NIS2 (Determina ACN 164179/2025)
-- ----------------------------------------------------------------------------
-- Aggiunge alla tabella incidents:
-- - nis2_incident_type: tipologia incidente significativo IS-1/IS-2/IS-3/IS-4
-- (Determinazione ACN n. 164179 del 14/04/2025, Allegati 3 e 4).
-- - entity_obligation: regime di obblighi applicabile (essential=Allegato 3,
-- important=Allegato 4). I soggetti importanti NON sono tenuti all'IS-4
-- (incidenti ricorrenti).
--
-- Fonte: D.Lgs. 138/2024 art. 23 + Determina ACN 164179/2025.
-- Idempotente via information_schema. Rilanciabile.
-- mysql -h localhost nis2_agile_db -e "source docs/sql/021_incident_nis2_taxonomy.sql"
-- ============================================================================
DELIMITER //
DROP PROCEDURE IF EXISTS _mig021_add_col //
CREATE PROCEDURE _mig021_add_col(IN col VARCHAR(64), IN ddl TEXT)
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'incidents' AND COLUMN_NAME = col
) THEN
SET @sql = CONCAT('ALTER TABLE incidents ADD COLUMN ', ddl);
PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st;
END IF;
END //
DELIMITER ;
CALL _mig021_add_col('nis2_incident_type', "nis2_incident_type ENUM('IS-1','IS-2','IS-3','IS-4') NULL COMMENT 'Tipologia incidente significativo - Determina ACN 164179/2025'");
CALL _mig021_add_col('entity_obligation', "entity_obligation ENUM('essential','important') NULL COMMENT 'Regime obblighi: essential=Allegato 3, important=Allegato 4'");
DROP PROCEDURE IF EXISTS _mig021_add_col;
-- ROLLBACK:
-- ALTER TABLE incidents DROP COLUMN nis2_incident_type, DROP COLUMN entity_obligation;

View File

@ -0,0 +1,73 @@
-- ============================================================================
-- Migration 022 - Metriche Incidente (TTD/TTC/TTR) + Post-Incident Review
-- ----------------------------------------------------------------------------
-- 1) Timestamp di fase su incidents per calcolare le metriche:
-- triaged_at, contained_at, eradicated_at, recovered_at.
-- (la tabella aveva solo detected_at e closed_at)
-- TTD = triaged_at - detected_at (Time to Detect/triage)
-- TTC = contained_at - detected_at (Time to Contain)
-- TTR = recovered_at - detected_at (Time to Recover)
-- 2) Tabella incident_pir: Post-Incident Review strutturato (RC.CO-03 / NIST CSF),
-- con Root Cause Analysis 5-Whys, metriche, costo stimato, lesson learned.
--
-- Idempotente. mysql -h localhost nis2_agile_db -e "source docs/sql/022_incident_metrics_pir.sql"
-- ============================================================================
DELIMITER //
DROP PROCEDURE IF EXISTS _mig022_add_col //
CREATE PROCEDURE _mig022_add_col(IN col VARCHAR(64), IN ddl TEXT)
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'incidents' AND COLUMN_NAME = col
) THEN
SET @sql = CONCAT('ALTER TABLE incidents ADD COLUMN ', ddl);
PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st;
END IF;
END //
DELIMITER ;
CALL _mig022_add_col('triaged_at', "triaged_at DATETIME NULL COMMENT 'Inizio triage'");
CALL _mig022_add_col('contained_at', "contained_at DATETIME NULL COMMENT 'Incidente contenuto'");
CALL _mig022_add_col('eradicated_at', "eradicated_at DATETIME NULL COMMENT 'Minaccia eradicata'");
CALL _mig022_add_col('recovered_at', "recovered_at DATETIME NULL COMMENT 'Servizi ripristinati'");
DROP PROCEDURE IF EXISTS _mig022_add_col;
-- Post-Incident Review (1:1 con incident)
CREATE TABLE IF NOT EXISTS incident_pir (
id INT AUTO_INCREMENT PRIMARY KEY,
incident_id INT NOT NULL,
organization_id INT NOT NULL,
-- Root Cause Analysis - 5 Whys
problem_statement TEXT,
why_1 TEXT, why_2 TEXT, why_3 TEXT, why_4 TEXT, why_5 TEXT,
root_cause TEXT,
-- Metriche (snapshot al momento della review, in minuti)
ttd_minutes INT NULL,
ttc_minutes INT NULL,
ttr_minutes INT NULL,
downtime_minutes INT NULL,
affected_users INT NULL,
estimated_cost_eur DECIMAL(12,2) NULL,
notification_compliance TINYINT(1) NULL COMMENT '1 se notifiche entro le tempistiche NIS2',
-- Lesson learned & azioni di miglioramento
what_went_well TEXT,
what_to_improve TEXT,
improvement_actions JSON NULL COMMENT 'lista azioni {desc, owner, due_date, status}',
participants TEXT,
reviewed_by INT NULL,
reviewed_at DATETIME NULL,
status ENUM('draft','completed') DEFAULT 'draft',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uniq_incident (incident_id),
INDEX idx_org (organization_id),
CONSTRAINT fk_pir_incident FOREIGN KEY (incident_id) REFERENCES incidents(id) ON DELETE CASCADE,
CONSTRAINT fk_pir_org FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ROLLBACK:
-- DROP TABLE IF EXISTS incident_pir;
-- ALTER TABLE incidents DROP COLUMN triaged_at, DROP COLUMN contained_at,
-- DROP COLUMN eradicated_at, DROP COLUMN recovered_at;

View File

@ -466,6 +466,7 @@
<th>Tipo</th> <th>Tipo</th>
<th>Categoria</th> <th>Categoria</th>
<th>Criticita'</th> <th>Criticita'</th>
<th>Rilevanza NIS2</th>
<th>Owner</th> <th>Owner</th>
<th>Stato</th> <th>Stato</th>
<th>Azioni</th> <th>Azioni</th>
@ -482,9 +483,13 @@
<td>${typeLabels[asset.asset_type] || asset.asset_type || '-'}</td> <td>${typeLabels[asset.asset_type] || asset.asset_type || '-'}</td>
<td>${escapeHtml(asset.category || '-')}</td> <td>${escapeHtml(asset.category || '-')}</td>
<td><span class="criticality-badge ${crit}">${criticalityLabels[crit] || crit}</span></td> <td><span class="criticality-badge ${crit}">${criticalityLabels[crit] || crit}</span></td>
<td>${relevanceBadge(asset)}</td>
<td>${escapeHtml(asset.owner_name || '-')}</td> <td>${escapeHtml(asset.owner_name || '-')}</td>
<td><span class="status-badge ${st}">${statusLabels[st] || st}</span></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"> <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> <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> </button>
@ -496,6 +501,106 @@
container.innerHTML = html; 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>&ge;${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 ─────────────────────────────────── // ── Asset Detail View ───────────────────────────────────
async function showAssetDetail(id) { async function showAssetDetail(id) {
try { try {
@ -581,6 +686,7 @@
size: 'lg', size: 'lg',
footer: ` footer: `
<button class="btn btn-secondary" onclick="closeModal()">Chiudi</button> <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> <button class="btn btn-primary" onclick="closeModal(); showEditAssetModal(${a.id})">Modifica</button>
` `
}); });

View File

@ -230,6 +230,9 @@ $actionMap = [
'POST:{id}/notification' => 'sendNotification', 'POST:{id}/notification' => 'sendNotification',
'POST:{id}/finalReport' => 'sendFinalReport', 'POST:{id}/finalReport' => 'sendFinalReport',
'POST:{id}/aiClassify' => 'aiClassify', 'POST:{id}/aiClassify' => 'aiClassify',
'GET:{id}/metrics' => 'metrics',
'GET:{id}/pir' => 'getPir',
'POST:{id}/pir' => 'savePir',
], ],
// ── PolicyController ──────────────────────────── // ── PolicyController ────────────────────────────
@ -269,10 +272,13 @@ $actionMap = [
'assets' => [ 'assets' => [
'GET:list' => 'list', 'GET:list' => 'list',
'POST:create' => 'create', 'POST:create' => 'create',
'GET:scoringGrid' => 'scoringGrid',
'GET:relevantSystems' => 'relevantSystems',
'GET:dependencyMap' => 'dependencyMap',
'GET:{id}' => 'get', 'GET:{id}' => 'get',
'PUT:{id}' => 'update', 'PUT:{id}' => 'update',
'DELETE:{id}' => 'delete', 'DELETE:{id}' => 'delete',
'GET:dependencyMap' => 'dependencyMap', 'POST:{id}/score' => 'score',
], ],
// ── AuditController ───────────────────────────── // ── AuditController ─────────────────────────────
@ -284,7 +290,9 @@ $actionMap = [
'GET:report' => 'generateReport', 'GET:report' => 'generateReport',
'GET:logs' => 'getAuditLogs', 'GET:logs' => 'getAuditLogs',
'GET:iso27001Mapping' => 'getIsoMapping', 'GET:iso27001Mapping' => 'getIsoMapping',
'GET:nistCsfMapping' => 'getNistCsfMapping',
'GET:executiveReport' => 'executiveReport', 'GET:executiveReport' => 'executiveReport',
'GET:relevantSystemsRegister' => 'relevantSystemsRegister',
'GET:export' => 'export', 'GET:export' => 'export',
'GET:chainVerify' => 'chainVerify', 'GET:chainVerify' => 'chainVerify',
'GET:exportCertified' => 'exportCertified', 'GET:exportCertified' => 'exportCertified',

View File

@ -212,6 +212,10 @@ class NIS2API {
sendEarlyWarning(id) { return this.post(`/incidents/${id}/early-warning`, {}); } sendEarlyWarning(id) { return this.post(`/incidents/${id}/early-warning`, {}); }
sendNotification(id) { return this.post(`/incidents/${id}/notification`, {}); } sendNotification(id) { return this.post(`/incidents/${id}/notification`, {}); }
sendFinalReport(id) { return this.post(`/incidents/${id}/final-report`, {}); } 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 // Policies
@ -251,6 +255,11 @@ class NIS2API {
createAsset(data) { return this.post('/assets/create', data); } createAsset(data) { return this.post('/assets/create', data); }
getAsset(id) { return this.get(`/assets/${id}`); } getAsset(id) { return this.get(`/assets/${id}`); }
updateAsset(id, data) { return this.put(`/assets/${id}`, data); } 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 // Audit

View File

@ -207,11 +207,13 @@ const HelpSystem = (function () {
} }
], ],
references: [ references: [
'Art. 23.1 - Obbligo di notifica degli incidenti significativi', 'Direttiva (UE) 2022/2555 - Art. 23.1 - Obbligo di notifica degli incidenti significativi',
'Art. 23.4 (a) - Early warning entro 24 ore', 'Direttiva (UE) 2022/2555 - Art. 23.4 (a) - Early warning entro 24 ore',
'Art. 23.4 (b) - Notifica entro 72 ore', 'Direttiva (UE) 2022/2555 - Art. 23.4 (b) - Notifica entro 72 ore',
'Art. 23.4 (d) - Relazione finale entro un mese', 'Direttiva (UE) 2022/2555 - Art. 23.4 (d) - Relazione finale entro un mese',
'Art. 23.3 - Definizione di incidente significativo' '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.' '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 &ge; 40</strong>. Classi: &ge;80 Critico, 60-79 Alto, 40-59 Medio, 20-39 Basso, &lt;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', heading: 'Mappa delle Dipendenze',
items: [ items: [
@ -399,10 +410,11 @@ const HelpSystem = (function () {
} }
], ],
references: [ references: [
'Art. 21.2 (i) - Sicurezza delle risorse umane, politiche di controllo dell\'accesso e gestione degli attivi', 'Direttiva (UE) 2022/2555 - Art. 21.2 (i) - Sicurezza delle risorse umane, controllo degli accessi e gestione degli attivi',
'Art. 21.2 (a) - Politiche di analisi dei rischi e di sicurezza dei sistemi informatici', 'Direttiva (UE) 2022/2555 - 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', 'Direttiva (UE) 2022/2555 - Art. 21.2 (c) - Continuita\' operativa, gestione dei backup e ripristino',
'Considerando 79 - Adeguatezza delle misure rispetto ai rischi per le reti e i sistemi informativi' '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'
] ]
}, },

View File

@ -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."}

View File

@ -0,0 +1,213 @@
<?php
/**
* NIS2 Agile - Ingest Fonti Normative Certe nella Knowledge Base (RAG)
* ----------------------------------------------------------------------------
* Indicizza i PDF normativi ufficiali (docs/nis2/*.pdf, registrati in
* application/config/nis2_sources.php) nella collection Qdrant `nis2_kb` con
* scope SYSTEM, cosi' che AIService::askWithRag() possa citare le fonti certe.
*
* ESEGUIRE SU HETZNER (richiede accesso a Qdrant + Voyage), es:
* docker exec -i nis2-app php /var/www/nis2-agile/scripts/ingest-nis2-sources.php
* # oppure dalla root del progetto:
* php scripts/ingest-nis2-sources.php
*
* Estrazione testo: usa `pdftotext` (poppler-utils) se disponibile, altrimenti
* ricade sull'API document di Claude. Idempotente: cancella i chunk SYSTEM del
* documento (per `source` stabile) prima del re-upsert.
*
* Opzioni:
* --only=determina_164179_2025 ingerisce una sola fonte (key del registry)
* --dry-run estrae e mostra le statistiche senza upsert
* ============================================================================
*/
if (PHP_SAPI !== 'cli') { fwrite(STDERR, "Solo CLI\n"); exit(1); }
if (!defined('BASE_PATH')) define('BASE_PATH', dirname(__DIR__));
if (!defined('APP_PATH')) define('APP_PATH', BASE_PATH . '/application');
require_once APP_PATH . '/config/env.php';
require_once APP_PATH . '/config/config.php';
require_once APP_PATH . '/config/database.php';
require_once APP_PATH . '/services/EmbedService.php';
require_once APP_PATH . '/services/VectorService.php';
$opts = getopt('', ['only::', 'dry-run']);
$only = $opts['only'] ?? null;
$dryRun = isset($opts['dry-run']);
$sources = require APP_PATH . '/config/nis2_sources.php';
function logln(string $m): void { echo '[' . date('Y-m-d H:i:s') . "] $m\n"; }
/** Estrae testo da un PDF: pdftotext -> fallback Claude document API. */
function extractPdfText(string $absPath): string
{
// 0) Cache di testo pre-estratto accanto al PDF (<file>.pdf.txt).
// Utile quando l'ingest gira in un container privo di pdftotext:
// si estrae prima sull'host e si rilegge il .txt qui.
$cache = $absPath . '.txt';
if (is_file($cache)) {
$t = (string) file_get_contents($cache);
if (strlen(trim($t)) > 200) { logln(' uso cache testo: ' . basename($cache)); return $t; }
}
// 1) pdftotext (veloce, gratuito)
$bin = trim((string)@shell_exec('command -v pdftotext 2>/dev/null'));
if ($bin !== '') {
$tmp = tempnam(sys_get_temp_dir(), 'nis2pdf') . '.txt';
@shell_exec(escapeshellcmd($bin) . ' -enc UTF-8 -nopgbrk ' . escapeshellarg($absPath) . ' ' . escapeshellarg($tmp) . ' 2>/dev/null');
$txt = is_file($tmp) ? (string)file_get_contents($tmp) : '';
@unlink($tmp);
if (strlen(trim($txt)) > 200) return $txt;
}
// 2) Fallback: Claude document API
logln(' pdftotext non disponibile/insufficiente -> uso Claude document API');
$data = base64_encode((string)file_get_contents($absPath));
$body = [
'model' => defined('ANTHROPIC_MODEL') ? ANTHROPIC_MODEL : 'claude-sonnet-4-5-20250929',
'max_tokens' => 8000,
'messages' => [[
'role' => 'user',
'content' => [
['type' => 'document', 'source' => ['type' => 'base64', 'media_type' => 'application/pdf', 'data' => $data]],
['type' => 'text', 'text' => 'Estrai integralmente il testo di questo documento normativo in testo semplice, mantenendo numeri di articolo, commi, allegati e tabelle. Non riassumere, non commentare.'],
],
]],
];
$ch = curl_init('https://api.anthropic.com/v1/messages');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'content-type: application/json',
'x-api-key: ' . ANTHROPIC_API_KEY,
'anthropic-version: 2023-06-01',
],
CURLOPT_POSTFIELDS => json_encode($body),
CURLOPT_TIMEOUT => 180,
]);
$res = curl_exec($ch);
if ($res === false) { logln(' ERRORE curl: ' . curl_error($ch)); curl_close($ch); return ''; }
curl_close($ch);
$j = json_decode($res, true);
return $j['content'][0]['text'] ?? '';
}
function chunkText(string $text, int $size = 2000, int $overlap = 200): array
{
// Multibyte-safe: usa mb_* per non spezzare caratteri UTF-8 a meta'
// (altrimenti json_encode produce body non valido -> Voyage HTTP 400).
$text = mb_convert_encoding($text, 'UTF-8', 'UTF-8'); // bonifica sequenze invalide
$chunks = []; $len = mb_strlen($text, 'UTF-8'); $start = 0;
while ($start < $len) {
$take = min($size, $len - $start);
$piece = mb_substr($text, $start, $take, 'UTF-8');
if (trim($piece) !== '') $chunks[] = $piece;
if ($start + $take >= $len) break;
$start += ($size - $overlap);
}
return $chunks;
}
function uuid(): string
{
$b = random_bytes(16);
$b[6] = chr((ord($b[6]) & 0x0f) | 0x40);
$b[8] = chr((ord($b[8]) & 0x3f) | 0x80);
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($b), 4));
}
logln('=== Ingest fonti normative NIS2 nella KB (scope SYSTEM) ===');
if ($dryRun) logln('MODALITA DRY-RUN: nessun upsert.');
$embed = null; $vector = null;
if (!$dryRun) {
$embed = new EmbedService();
$vector = new VectorService();
$vector->ensureCollection($embed->dims);
}
$totalChunks = 0; $done = 0;
foreach ($sources as $key => $src) {
if ($only && $key !== $only) continue;
if (empty($src['file'])) { logln("SKIP {$key}: nessun file PDF associato"); continue; }
$abs = BASE_PATH . '/' . $src['file'];
if (!is_file($abs)) { logln("SKIP {$key}: file non trovato {$abs}"); continue; }
logln("Fonte: {$src['short']} ({$src['file']})");
$text = extractPdfText($abs);
$text = preg_replace('/[ \t]+/', ' ', $text);
$text = preg_replace('/\n{3,}/', "\n\n", trim($text));
if (strlen($text) < 200) { logln(" ERRORE: testo estratto troppo breve, salto."); continue; }
// Prefisso citazione su ogni documento: aiuta il modello a citare correttamente
$header = "FONTE NORMATIVA: {$src['citation']}\nAUTORITA: {$src['authority']}\n\n";
$chunks = chunkText($header . $text, 2000, 200);
logln(' testo: ' . strlen($text) . ' char -> ' . count($chunks) . ' chunk');
$totalChunks += count($chunks);
if ($dryRun) { $done++; continue; }
// Idempotenza: rimuovi i chunk SYSTEM esistenti per questa fonte
try {
$vector->deleteByFilter(['must' => [
['key' => 'scope', 'match' => ['value' => 'SYSTEM']],
['key' => 'source', 'match' => ['value' => $src['citation']]],
]]);
} catch (Exception $e) { logln(' (warning) delete precedente: ' . $e->getMessage()); }
$docUuid = uuid();
$points = [];
foreach ($chunks as $i => $chunk) {
// Embedding con retry/backoff: Voyage puo' restituire errori transitori
// (HTTP 0 timeout / 429 rate limit) su grandi volumi di chunk.
$vec = null;
for ($try = 1; $try <= 5; $try++) {
try { $vec = $embed->embed($chunk); break; }
catch (Throwable $e) {
if ($try === 5) { logln(" ERRORE embed chunk {$i} dopo 5 tentativi: " . $e->getMessage()); throw $e; }
logln(" retry embed chunk {$i} (tentativo {$try}): " . substr($e->getMessage(), 0, 60));
sleep($try); // backoff lineare 1s,2s,3s,4s
}
}
$points[] = [
'id' => uuid(),
'vector' => $vec,
'payload' => [
'doc_uuid' => $docUuid,
'title' => $src['short'] . ($i > 0 ? ' (parte ' . ($i + 1) . ')' : ''),
'chunk' => $chunk,
'entity_type' => 'normativa',
'source' => $src['citation'],
'lang' => 'it',
'scope' => 'SYSTEM',
'consulting_firm_id' => null,
'organization_id' => null,
'shared_with_orgs' => [],
'uploaded_by' => 0,
],
];
}
// Upsert a batch (per non superare i limiti di payload)
foreach (array_chunk($points, 64) as $batch) {
$vector->upsertBatch($batch);
}
// Tracking MySQL (best-effort)
try {
$stmt = Database::getInstance()->prepare(
"INSERT INTO kb_uploaded_documents
(qdrant_doc_uuid, scope, consulting_firm_id, organization_id, uploaded_by, title, entity_type, source, lang, chunk_count, shared_with_orgs, status)
VALUES (?, 'SYSTEM', NULL, NULL, 0, ?, 'normativa', ?, 'it', ?, '[]', 'ready')"
);
$stmt->execute([$docUuid, $src['short'], $src['citation'], count($chunks)]);
} catch (Exception $e) { logln(' (warning) tracking insert: ' . $e->getMessage()); }
logln(" OK indicizzato (doc_uuid={$docUuid})");
$done++;
}
logln("=== Completato: {$done} fonti, {$totalChunks} chunk totali ===");