Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
152 lines
9.5 KiB
PHP
152 lines
9.5 KiB
PHP
<?php
|
|
/**
|
|
* Nis2ClassificationService — AUTOVALUTAZIONE PRELIMINARE del soggetto NIS2.
|
|
*
|
|
* Centralizza la logica prima DUPLICATA in OnboardingController e OrganizationController (A2).
|
|
* L'esito NON e' una qualifica giuridica: la classificazione formale e la registrazione spettano
|
|
* all'ACN (Det. 333017/2025: dichiarazione ex DPR 445/2000, finestra 1/1-28/2, verifiche art. 14).
|
|
*
|
|
* Riferimenti (D.Lgs. 138/2024 art. 3 — vedi docs/nis2/Dlgs138_art3_ambito.md):
|
|
* - c. 2 regola dimensionale (Allegato I/II oltre i massimali delle piccole imprese)
|
|
* - c. 5 soggetti A PRESCINDERE dalla dimensione (DNS/TLD/IXP, fiduciari, reti/servizi TLC pubblici, ...)
|
|
* - c. 9 lett. b/c/d criteri qualitativi sotto-soglia (unico fornitore / impatto sicurezza-salute pubblica / rischio sistemico)
|
|
* - c. 13 individuazione da parte dell'Autorita' (ACN) su proposta delle Autorita' di settore
|
|
* Soglie dimensionali: Racc. 2003/361/CE (grande >=250 dip o >50M EUR fatt o >43M EUR bilancio; media >=50 o >10M).
|
|
*/
|
|
class Nis2ClassificationService
|
|
{
|
|
/** Allegato I (alta criticita') -> essenziale se grande, importante se media */
|
|
private const ESSENTIAL_SECTORS = [
|
|
'energy','energy_electricity','energy_district_heating','energy_oil','energy_gas','energy_hydrogen',
|
|
'transport','transport_air','transport_rail','transport_water','transport_road',
|
|
'banking','financial_markets','health','water','drinking_water','waste_water',
|
|
'digital_infra','digital_infrastructure','ict_service_management',
|
|
'public_admin','public_administration','space',
|
|
];
|
|
|
|
/** Allegato II (altri settori critici) -> importante se media o grande */
|
|
private const IMPORTANT_SECTORS = [
|
|
'manufacturing','manufacturing_medical','manufacturing_computers','manufacturing_electrical',
|
|
'manufacturing_machinery','manufacturing_vehicles','manufacturing_transport',
|
|
'postal','postal_courier','chemical','chemicals','food',
|
|
'waste','waste_management','ict_services','digital_providers','research',
|
|
];
|
|
|
|
/** Categorie in ambito a prescindere dalla dimensione (art. 3 c. 5) -> classe fissa */
|
|
private const SIZE_INDEPENDENT = [
|
|
'dns' => 'essential', 'tld' => 'essential', 'ixp' => 'essential',
|
|
'trust_qualified' => 'essential', 'trust_unqualified' => 'important',
|
|
'ecomm_public_network' => 'essential', 'ecomm_public_service' => 'essential',
|
|
'domain_registrar' => 'important', 'pa_central' => 'essential', 'pa_regional_local' => 'important',
|
|
];
|
|
|
|
/**
|
|
* Classifica (autovalutazione). Input opzionali oltre a sector/employees/turnover:
|
|
* balance, size_independent_category, is_autonomous, crit_sole_supplier, crit_public_impact, crit_systemic_risk.
|
|
* @return array {entity_type, designation_basis, self_assessment_status, sub_threshold_candidate,
|
|
* label, size, description, obligations[], warnings[], disclaimer}
|
|
*/
|
|
public static function classify(array $in): array
|
|
{
|
|
$sector = (string) ($in['sector'] ?? '');
|
|
$employees = (int) ($in['employees'] ?? 0);
|
|
$turnover = (float) ($in['turnover'] ?? 0);
|
|
$balance = (float) ($in['balance'] ?? 0);
|
|
$sizeIndep = (string) ($in['size_independent_category'] ?? 'none');
|
|
$autonomous = array_key_exists('is_autonomous', $in) ? (int) $in['is_autonomous'] : 1;
|
|
$critSole = !empty($in['crit_sole_supplier']);
|
|
$critImpact = !empty($in['crit_public_impact']);
|
|
$critSystemic = !empty($in['crit_systemic_risk']);
|
|
|
|
$warnings = [];
|
|
if (!$autonomous) {
|
|
$warnings[] = "Impresa non autonoma (gruppo/collegate): il calcolo dimensionale va consolidato ai sensi dell'art. 6 della Raccomandazione 2003/361/CE. L'esito automatico potrebbe sottostimare la classe.";
|
|
}
|
|
|
|
// 1) Soggetti a prescindere dalla dimensione (art. 3 c. 5)
|
|
if ($sizeIndep !== '' && $sizeIndep !== 'none' && isset(self::SIZE_INDEPENDENT[$sizeIndep])) {
|
|
return self::result(self::SIZE_INDEPENDENT[$sizeIndep], 'size_independent', $employees, $turnover, $balance, $warnings,
|
|
"Soggetto incluso a prescindere dalla dimensione (art. 3, comma 5, D.Lgs. 138/2024).");
|
|
}
|
|
|
|
// 2) Matrice settore x dimensione (art. 3 c. 2)
|
|
$size = self::sizeClass($employees, $turnover, $balance); // large|medium|small
|
|
$isLarge = $size === 'large';
|
|
$isMedium = $size === 'medium';
|
|
if (in_array($sector, self::ESSENTIAL_SECTORS, true)) {
|
|
if ($isLarge) return self::result('essential', 'dimensional', $employees, $turnover, $balance, $warnings);
|
|
if ($isMedium) return self::result('important', 'dimensional', $employees, $turnover, $balance, $warnings);
|
|
}
|
|
if (in_array($sector, self::IMPORTANT_SECTORS, true)) {
|
|
if ($isLarge || $isMedium) return self::result('important', 'dimensional', $employees, $turnover, $balance, $warnings);
|
|
}
|
|
|
|
// 3) Sotto-soglia con criteri qualitativi (art. 3 c. 9 lett. b/c/d) -> CANDIDATO (no auto-classe)
|
|
if ($critSole || $critImpact || $critSystemic) {
|
|
$r = self::result('not_applicable', 'sub_threshold_candidate', $employees, $turnover, $balance, $warnings,
|
|
"Soggetto sotto la soglia dimensionale che soddisfa uno o piu' criteri qualitativi dell'art. 3, comma 9, lett. b/c/d del D.Lgs. 138/2024. NON e' una classificazione automatica: l'individuazione come soggetto essenziale o importante spetta all'Autorita' (ACN) ex art. 3, comma 13. Verificare/segnalarsi presso ACN.");
|
|
$r['sub_threshold_candidate'] = true;
|
|
$r['label'] = 'Potenziale soggetto individuabile (rinvio ACN)';
|
|
return $r;
|
|
}
|
|
|
|
// 4) Fuori ambito
|
|
return self::result('not_applicable', 'out_of_scope', $employees, $turnover, $balance, $warnings);
|
|
}
|
|
|
|
/** Solo il tipo (compat con i vecchi chiamanti). */
|
|
public static function entityType(string $sector, int $employees, float $turnover, float $balance = 0): string
|
|
{
|
|
return self::classify(['sector' => $sector, 'employees' => $employees, 'turnover' => $turnover, 'balance' => $balance])['entity_type'];
|
|
}
|
|
|
|
/** large | medium | small (micro inclusa) — Racc. 2003/361/CE (criteri alternativi su fatturato/bilancio). */
|
|
private static function sizeClass(int $employees, float $turnover, float $balance): string
|
|
{
|
|
if ($employees >= 250 || $turnover > 50000000 || $balance > 43000000) return 'large';
|
|
if ($employees >= 50 || $turnover > 10000000 || $balance > 10000000) return 'medium';
|
|
return 'small';
|
|
}
|
|
|
|
private static function result(string $type, string $basis, int $employees, float $turnover, float $balance, array $warnings, ?string $note = null): array
|
|
{
|
|
$sizeLabel = ($employees >= 250 || $turnover > 50000000 || $balance > 43000000) ? 'Grande impresa'
|
|
: (($employees >= 50 || $turnover > 10000000 || $balance > 10000000) ? 'Media impresa' : 'Piccola/micro impresa');
|
|
|
|
$obligations = match ($type) {
|
|
'essential', 'important' => [
|
|
'Misure di gestione del rischio di cybersicurezza (art. 24 D.Lgs. 138/2024)',
|
|
'Notifica degli incidenti significativi: preallarme 24h, notifica 72h, relazione finale 1 mese (art. 25 D.Lgs. 138/2024)',
|
|
'Responsabilita\' e formazione degli organi di amministrazione e direttivi (art. 23 D.Lgs. 138/2024)',
|
|
$type === 'essential' ? 'Vigilanza proattiva (ex ante) da parte dell\'Autorita\'' : 'Vigilanza reattiva (ex post) da parte dell\'Autorita\'',
|
|
$type === 'essential' ? 'Sanzioni fino a 10M EUR o 2% del fatturato mondiale annuo' : 'Sanzioni fino a 7M EUR o 1,4% del fatturato mondiale annuo',
|
|
],
|
|
default => [
|
|
'Nessun obbligo NIS2 automatico in base ai dati forniti',
|
|
'Buone pratiche di cybersicurezza comunque raccomandate',
|
|
],
|
|
};
|
|
|
|
$labels = ['essential' => 'Soggetto Essenziale', 'important' => 'Soggetto Importante', 'not_applicable' => 'Non in ambito (in base ai dati)'];
|
|
$descr = match ($type) {
|
|
'essential' => "Autovalutazione: la vostra organizzazione rientrerebbe tra i soggetti ESSENZIALI (settore ad alta criticita', Allegato I, dimensione di grande impresa).",
|
|
'important' => "Autovalutazione: la vostra organizzazione rientrerebbe tra i soggetti IMPORTANTI ai sensi del D.Lgs. 138/2024.",
|
|
default => "Autovalutazione: in base ai dati forniti la vostra organizzazione non rientrerebbe automaticamente nell'ambito del D.Lgs. 138/2024.",
|
|
};
|
|
if ($note) $descr .= ' ' . $note;
|
|
|
|
return [
|
|
'entity_type' => $type,
|
|
'designation_basis' => $basis,
|
|
'self_assessment_status' => 'preliminary',
|
|
'sub_threshold_candidate' => false,
|
|
'label' => $labels[$type] ?? $type,
|
|
'size' => $sizeLabel,
|
|
'description' => $descr,
|
|
'obligations' => $obligations,
|
|
'warnings' => $warnings,
|
|
'disclaimer' => "Autovalutazione PRELIMINARE non vincolante. La qualifica formale di soggetto essenziale/importante e la registrazione spettano esclusivamente all'ACN tramite la piattaforma (Determinazione ACN 333017/2025): dichiarazione ex DPR 445/2000 confermata dal punto di contatto, finestra di registrazione 1 gennaio - 28 febbraio, verifiche di coerenza ex art. 14. La classificazione formale e i pareri legali competono a Direzione/Legale.",
|
|
];
|
|
}
|
|
}
|