[FEAT] Asset import CMDB/cloud + scoring automatico GV.OC-04 (P2)

- AssetScoringService::inferCriteria: euristica 6 criteri da campi CMDB
  (criticality, data_classification, internet_facing, dependencies, regulated)
- AssetController::import (JWT org_admin/compliance_manager) + bulkUpsert condiviso:
  upsert dedup su external_ref, scoring auto GV.OC-04, max 1000 asset/batch
- ServicesController::ingestAssets -> POST /services/assets-ingest (scope ingest:assets) per connettori CMDB/cloud
- Migrazione 025: assets += external_ref + discovery_source + indice univoco dedup
- Route POST:assetsIngest (services) + POST:import (assets)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-05-30 09:14:12 +02:00
co-authored by Claude Opus 4.8
parent 307993fbad
commit 4924075142
5 changed files with 206 additions and 0 deletions
@@ -106,6 +106,55 @@ class AssetScoringService
],
];
/**
* Inferenza euristica dei 6 criteri GV.OC-04 dai campi di un asset
* importato da CMDB/cloud. Permette lo scoring automatico in bulk senza
* compilazione manuale della griglia. I valori restituiti sono chiavi
* VALIDE della GRID, quindi calculate() li accetta senza eccezioni.
*
* Campi letti: criticality, asset_type, data_classification,
* internet_facing, dependencies_count, regulated.
*/
public static function inferCriteria(array $a): array
{
$crit = strtolower((string) ($a['criticality'] ?? 'medium'));
// C1 Criticita Operativa <- criticality
$c1 = ['critical' => 'critical', 'high' => 'high', 'medium' => 'medium', 'low' => 'low'][$crit] ?? 'medium';
// C2 Impatto Interruzione <- criticality (proxy)
$c2 = ['critical' => 'gt24h_gt70', 'high' => 'h4_8_30_50', 'medium' => 'h1_4_10_30', 'low' => 'lt1h_lt10'][$crit] ?? 'h1_4_10_30';
// C3 Dati Trattati <- data_classification (con proxy dal tipo asset)
$dc = strtolower((string) ($a['data_classification'] ?? ''));
$c3map = [
'special' => 'gdpr_art9', 'art9' => 'gdpr_art9', 'sensitive' => 'gdpr_art9',
'personal' => 'personal_large', 'pii' => 'personal_large',
'financial' => 'personal_fin', 'confidential' => 'confidential',
'internal' => 'confidential', 'public' => 'public',
];
$c3 = $c3map[$dc] ?? ((($a['asset_type'] ?? '') === 'data') ? 'personal_large' : 'confidential');
// C4 Dipendenze <- numero dipendenze critiche
$deps = (int) ($a['dependencies_count'] ?? 0);
$c4 = $deps >= 5 ? 'ge5_critical' : ($deps >= 3 ? 'n3_4_critical' : ($deps === 2 ? 'n2_critical' : ($deps === 1 ? 'n1_critical' : 'none')));
// C5 Esposizione <- internet_facing
$c5 = !empty($a['internet_facing']) ? 'internet_mfa' : 'intranet';
// C6 Obblighi Normativi <- regulated
$c6 = !empty($a['regulated']) ? 'nis2_required' : 'none';
return [
'c1_operational_criticality' => $c1,
'c2_disruption_impact' => $c2,
'c3_data_processed' => $c3,
'c4_dependencies' => $c4,
'c5_exposure' => $c5,
'c6_regulatory' => $c6,
];
}
/**
* Calcola lo score a partire dalle selezioni dell'utente.
*