[FEAT] Integrazione analisi docs/nis2 v1.7.0 — scoring asset, tassonomia incidenti, PIR, NIST CSF, fonti certe
Fase 1 - Asset Relevance Scoring NIS2 (GV.OC-04): metodologia 0-100 a 6 criteri, AssetScoringService + endpoint scoringGrid/score/relevantSystems + UI assets.html + registro stampabile. Fase 2 - Tassonomia incidenti Determina ACN 164179/2025: IS-1..4 + regime essenziale/importante (Allegati 3/4). Fase 3 - Post-Incident Review (5-Whys) + metriche TTD/TTC/TTR + timestamp di fase. Fase 4 - Mapping NIST CSF 2.0 (43 controlli) reference-only. Fonti certe: registry config/nis2_sources.php + grounding AI (vieta riferimenti inventati) + citazioni help.js + ingest PDF normativi nella KB RAG (scripts/ingest-nis2-sources.php). Migrazioni 020/021/022 (additive idempotenti). Fix VectorService IP Qdrant (drift .5->.3). Analisi concorrenza Evix (docs/EVIX_ANALISI_CONCORRENZA.html, gap-driven). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a7a21faa82
commit
5c545ea3d0
@@ -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
|
||||
*/
|
||||
@@ -162,8 +188,23 @@ PROMPT;
|
||||
*/
|
||||
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
|
||||
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
|
||||
- Titolo: {$title}
|
||||
@@ -171,21 +212,23 @@ Sei un analista di incident response. Classifica il seguente incidente di sicure
|
||||
|
||||
## Organizzazione
|
||||
- Settore: {$organization['sector']}
|
||||
- Tipo entità: {$organization['entity_type']}
|
||||
- Tipo entità: {$entityType}
|
||||
|
||||
Rispondi in formato JSON:
|
||||
{
|
||||
"classification": "cyber_attack|data_breach|system_failure|human_error|natural_disaster|supply_chain|other",
|
||||
"nis2_incident_type": "{$allowedIs}|none",
|
||||
"severity": "low|medium|high|critical",
|
||||
"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,
|
||||
"notification_basis": "Riferimento normativo dell'obbligo (es. art. 23 D.Lgs. 138/2024)",
|
||||
"suggested_actions": ["Azione immediata 1", "Azione immediata 2"],
|
||||
"potential_impact": "Descrizione impatto potenziale",
|
||||
"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;
|
||||
|
||||
$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.
|
||||
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 = [
|
||||
'model' => $this->model,
|
||||
'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"
|
||||
. "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)) {
|
||||
$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.";
|
||||
|
||||
@@ -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] ?? [];
|
||||
}
|
||||
}
|
||||
@@ -255,6 +255,118 @@ class ReportService
|
||||
* @param int $orgId ID dell'organizzazione
|
||||
* @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 ≥ 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">⚠ DOCUMENTO RISERVATO - DISTRIBUZIONE LIMITATA ⚠</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 — Requisito GV.OC-04 (NIST CSF 2.0)<br>
|
||||
Settore: {$sector} | Tipo soggetto: {$entity} | Data emissione: {$date} | 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 ≥ 40.</strong> I sistemi con punteggio ≥ 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 (≥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>
|
||||
• Direttiva (UE) 2022/2555 (NIS2) — Parlamento europeo e Consiglio UE<br>
|
||||
• D.Lgs. 4 settembre 2024, n. 138 — recepimento NIS2 (artt. 23, 24)<br>
|
||||
• NIST Cybersecurity Framework 2.0 — 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
|
||||
{
|
||||
$data = $this->generateComplianceReport($orgId);
|
||||
|
||||
@@ -19,7 +19,7 @@ class VectorService
|
||||
$url = getenv('QDRANT_URL')
|
||||
?: ($_SERVER['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->collection = $collection;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user