package ucca // ============================================================================ // NIS2 Module // ============================================================================ // // This module implements the NIS2 directive (EU 2022/2555) and the German // implementation (BSIG-E - BSI-Gesetz Entwurf). // // NIS2 applies to: // - Essential Entities (besonders wichtige Einrichtungen): Large enterprises in Annex I sectors // - Important Entities (wichtige Einrichtungen): Medium enterprises in Annex I/II sectors // // Split into: // - nis2_module.go — struct, sector maps, classification, derive methods, decision tree // - nis2_yaml.go — YAML loading and conversion helpers // - nis2_obligations.go — hardcoded fallback obligations/controls/deadlines // // ============================================================================ // NIS2Module implements the RegulationModule interface for NIS2 type NIS2Module struct { obligations []Obligation controls []ObligationControl incidentDeadlines []IncidentDeadline decisionTree *DecisionTree loaded bool } var ( // NIS2AnnexISectors contains Sectors of High Criticality NIS2AnnexISectors = map[string]bool{ "energy": true, "transport": true, "banking_financial": true, "financial_market": true, "health": true, "drinking_water": true, "wastewater": true, "digital_infrastructure": true, "ict_service_mgmt": true, "public_administration": true, "space": true, } // NIS2AnnexIISectors contains Other Critical Sectors NIS2AnnexIISectors = map[string]bool{ "postal": true, "waste": true, "chemicals": true, "food": true, "manufacturing": true, "digital_providers": true, "research": true, } // NIS2SpecialServices are always in scope regardless of size NIS2SpecialServices = map[string]bool{ "dns": true, "tld": true, "cloud": true, "datacenter": true, "cdn": true, "trust_service": true, "public_network": true, "electronic_comms": true, "msp": true, "mssp": true, } ) // NewNIS2Module creates a new NIS2 module, loading obligations from YAML func NewNIS2Module() (*NIS2Module, error) { m := &NIS2Module{ obligations: []Obligation{}, controls: []ObligationControl{}, incidentDeadlines: []IncidentDeadline{}, } if err := m.loadFromYAML(); err != nil { m.loadHardcodedObligations() } m.buildDecisionTree() m.loaded = true return m, nil } // ID returns the module identifier func (m *NIS2Module) ID() string { return "nis2" } // Name returns the human-readable name func (m *NIS2Module) Name() string { return "NIS2-Richtlinie / BSIG-E" } // Description returns a brief description func (m *NIS2Module) Description() string { return "EU-Richtlinie über Maßnahmen für ein hohes gemeinsames Cybersicherheitsniveau (NIS2) und deutsche Umsetzung (BSIG-E)" } // IsApplicable checks if NIS2 applies to the organization func (m *NIS2Module) IsApplicable(facts *UnifiedFacts) bool { return m.Classify(facts) != NIS2NotAffected } // GetClassification returns the NIS2 classification as string func (m *NIS2Module) GetClassification(facts *UnifiedFacts) string { return string(m.Classify(facts)) } // Classify determines the NIS2 classification for an organization func (m *NIS2Module) Classify(facts *UnifiedFacts) NIS2Classification { if m.hasSpecialService(facts) { return NIS2EssentialEntity } inAnnexI := NIS2AnnexISectors[facts.Sector.PrimarySector] inAnnexII := NIS2AnnexIISectors[facts.Sector.PrimarySector] if !inAnnexI && !inAnnexII { return NIS2NotAffected } meetsSize := facts.Organization.MeetsNIS2SizeThreshold() isLarge := facts.Organization.MeetsNIS2LargeThreshold() if !meetsSize { if facts.Sector.IsKRITIS && facts.Sector.KRITISThresholdMet { return NIS2EssentialEntity } return NIS2NotAffected } if inAnnexI { if isLarge { return NIS2EssentialEntity } return NIS2ImportantEntity } if inAnnexII { return NIS2ImportantEntity } return NIS2NotAffected } func (m *NIS2Module) hasSpecialService(facts *UnifiedFacts) bool { for _, service := range facts.Sector.SpecialServices { if NIS2SpecialServices[service] { return true } } return false } // DeriveObligations derives all applicable NIS2 obligations func (m *NIS2Module) DeriveObligations(facts *UnifiedFacts) []Obligation { classification := m.Classify(facts) if classification == NIS2NotAffected { return []Obligation{} } var result []Obligation for _, obl := range m.obligations { if m.obligationApplies(obl, classification, facts) { customized := obl customized.RegulationID = m.ID() result = append(result, customized) } } return result } func (m *NIS2Module) obligationApplies(obl Obligation, classification NIS2Classification, _ *UnifiedFacts) bool { switch obl.AppliesWhen { case "classification == 'besonders_wichtige_einrichtung'": return classification == NIS2EssentialEntity case "classification == 'wichtige_einrichtung'": return classification == NIS2ImportantEntity case "classification in ['wichtige_einrichtung', 'besonders_wichtige_einrichtung']": return classification == NIS2EssentialEntity || classification == NIS2ImportantEntity case "classification != 'nicht_betroffen'": return classification != NIS2NotAffected case "": return classification != NIS2NotAffected default: return classification != NIS2NotAffected } } // DeriveControls derives all applicable NIS2 controls func (m *NIS2Module) DeriveControls(facts *UnifiedFacts) []ObligationControl { classification := m.Classify(facts) if classification == NIS2NotAffected { return []ObligationControl{} } var result []ObligationControl for _, ctrl := range m.controls { ctrl.RegulationID = m.ID() result = append(result, ctrl) } return result } // GetDecisionTree returns the NIS2 applicability decision tree func (m *NIS2Module) GetDecisionTree() *DecisionTree { return m.decisionTree } // GetIncidentDeadlines returns NIS2 incident reporting deadlines func (m *NIS2Module) GetIncidentDeadlines(facts *UnifiedFacts) []IncidentDeadline { if m.Classify(facts) == NIS2NotAffected { return []IncidentDeadline{} } return m.incidentDeadlines } func (m *NIS2Module) buildDecisionTree() { m.decisionTree = &DecisionTree{ ID: "nis2_applicability", Name: "NIS2 Anwendbarkeits-Entscheidungsbaum", RootNode: &DecisionNode{ ID: "root", Question: "Erbringt Ihr Unternehmen spezielle digitale Dienste (DNS, TLD, Cloud, Rechenzentrum, CDN, MSP, MSSP, Vertrauensdienste)?", YesNode: &DecisionNode{ ID: "special_services", Result: string(NIS2EssentialEntity), Explanation: "Anbieter spezieller digitaler Dienste sind unabhängig von der Größe als besonders wichtige Einrichtungen einzustufen.", }, NoNode: &DecisionNode{ ID: "sector_check", Question: "Ist Ihr Unternehmen in einem der NIS2-Sektoren tätig (Energie, Verkehr, Gesundheit, Digitale Infrastruktur, Öffentliche Verwaltung, Finanzwesen, etc.)?", YesNode: &DecisionNode{ ID: "size_check", Question: "Hat Ihr Unternehmen mindestens 50 Mitarbeiter ODER mindestens 10 Mio. EUR Jahresumsatz UND Bilanzsumme?", YesNode: &DecisionNode{ ID: "annex_check", Question: "Ist Ihr Sektor in Anhang I der NIS2 (hohe Kritikalität: Energie, Verkehr, Gesundheit, Trinkwasser, Digitale Infrastruktur, Bankwesen, Öffentliche Verwaltung, Weltraum)?", YesNode: &DecisionNode{ ID: "large_check_annex1", Question: "Hat Ihr Unternehmen mindestens 250 Mitarbeiter ODER mindestens 50 Mio. EUR Jahresumsatz?", YesNode: &DecisionNode{ ID: "essential_annex1", Result: string(NIS2EssentialEntity), Explanation: "Großes Unternehmen in Anhang I Sektor = Besonders wichtige Einrichtung", }, NoNode: &DecisionNode{ ID: "important_annex1", Result: string(NIS2ImportantEntity), Explanation: "Mittleres Unternehmen in Anhang I Sektor = Wichtige Einrichtung", }, }, NoNode: &DecisionNode{ ID: "important_annex2", Result: string(NIS2ImportantEntity), Explanation: "Unternehmen in Anhang II Sektor = Wichtige Einrichtung", }, }, NoNode: &DecisionNode{ ID: "kritis_check", Question: "Ist Ihr Unternehmen als KRITIS-Betreiber eingestuft?", YesNode: &DecisionNode{ ID: "kritis_essential", Result: string(NIS2EssentialEntity), Explanation: "KRITIS-Betreiber sind unabhängig von der Größe als besonders wichtige Einrichtungen einzustufen.", }, NoNode: &DecisionNode{ ID: "too_small", Result: string(NIS2NotAffected), Explanation: "Unternehmen unterhalb der Größenschwelle ohne KRITIS-Status sind nicht von NIS2 betroffen.", }, }, }, NoNode: &DecisionNode{ ID: "not_in_sector", Result: string(NIS2NotAffected), Explanation: "Unternehmen außerhalb der NIS2-Sektoren sind nicht betroffen.", }, }, }, } }