NIS2 Art.22–24: Supply Chain Risk Assessments, Incident Reporting, and Registration — Developer Guide (2026)
Articles 17–21 established what essential and important entities must implement internally — governance structures, risk management measures, and board-level oversight. Articles 22–24 move to the external obligations: how entity-level supply chain measures connect to EU-wide coordinated risk assessments (Art.22), when and how to report significant incidents to national authorities (Art.23), and how to register with your national competent authority (Art.24).
Art.23 is the article most developers encounter in practice. The three-phase reporting timeline — 24-hour early warning, 72-hour incident notification, one-month final report — creates concrete operational deadlines that your incident response tooling must support. Art.22's supply chain risk assessments produce guidance that directly updates Art.21(2)(d) obligations. Art.24's registration requirements create the entity catalogue that NCAs use to identify who must comply.
Art.22: Coordinated Security Risk Assessments of Critical Supply Chains
Article 22 extends the supply chain security obligation in Art.21(2)(d) from entity level to EU level. Where Art.21(2)(d) requires each entity to assess its own suppliers' security posture, Art.22 mandates the Commission, ENISA, and the Cooperation Group to jointly conduct coordinated risk assessments of critical ICT supply chains.
Why Art.22 Exists
The SolarWinds attack (2020) and the Log4Shell vulnerability (2021) demonstrated that entity-level supply chain assessments cannot detect systemic risks — risks embedded in ICT products and services used across thousands of entities simultaneously. Art.22 creates the mechanism for EU-wide coordinated risk assessments that produce sector-applicable threat intelligence.
Which Supply Chains Art.22 Targets
Art.22(1) requires the Cooperation Group, with ENISA support, to conduct coordinated risk assessments of critical ICT supply chains. The categories prioritised for assessment include:
| ICT Category | Typical Assessment Focus | Art.21 Downstream Effect |
|---|---|---|
| Cloud computing services | Hyperscaler concentration risk, data residency | Art.21(2)(d) cloud supplier contracts |
| Content delivery networks | BGP hijacking, single-AS concentration | Art.21(2)(a) network infrastructure measures |
| Domain Name System services | Resolver concentration, DNSSEC adoption | Art.21(2)(a) DNS resilience measures |
| Managed security services | Outsourcer access scope, incident data exposure | Art.21(2)(d)(e) supplier audits |
| Network equipment | Firmware supply chain, vendor country of origin | Art.21(2)(d) procurement criteria |
| Internet exchange points | IXP concentration, peering resilience | Art.21(2)(a) connectivity redundancy |
| Software development tools | Build pipeline integrity, dependency management | Art.21(2)(d) software supply chain |
Art.22 Methodology and Criteria
The risk assessment methodology under Art.22(2) evaluates supply chains against:
- Market concentration — the share of essential/important entities dependent on a single supplier
- Dependency depth — whether the ICT service is deeply embedded and hard to replace
- Country-of-origin risks — vendor jurisdiction, ownership structures, legal obligations to share data
- Vulnerability surface — known vulnerability history, patch cadence, exploit frequency
- Criticality — whether disruption would trigger Art.23 significant incident thresholds for multiple entities simultaneously
How Art.22 Assessments Affect Your Art.21 Obligations
Art.22 assessments produce two types of outputs that directly update your compliance obligations:
Commission Implementing Acts (Art.21(5)): The Commission may adopt sector-specific implementing acts adding mandatory measures for entities using high-risk supply chain components. If ENISA's assessment finds that a specific CDN provider creates systemic risk, entities using that provider may face additional Art.21 obligations.
Cooperation Group Guidance: The Cooperation Group publishes technical guidelines following each Art.22 assessment. These guidelines are not legally binding but inform NCA enforcement interpretation — an entity that ignored published supply chain risk guidance for a specific vendor category will face greater scrutiny during Art.32/33 supervisory proceedings.
from dataclasses import dataclass, field
from enum import Enum
from datetime import date
from typing import Optional
class SupplyChainRiskLevel(Enum):
CRITICAL = "critical" # Art.22 assessment identified systemic risk
HIGH = "high" # Cooperation Group guidance published
MEDIUM = "medium" # ENISA advisory only
LOW = "low" # No specific Art.22 findings
class ICTCategory(Enum):
CLOUD = "cloud_computing"
CDN = "content_delivery_network"
DNS = "dns_services"
MANAGED_SECURITY = "managed_security_services"
NETWORK_EQUIPMENT = "network_equipment"
IXP = "internet_exchange"
SOFTWARE_TOOLS = "software_development_tools"
OTHER = "other"
@dataclass
class SupplyChainVendor:
name: str
category: ICTCategory
country_of_origin: str # ISO 3166-1 alpha-2
market_share_eu: Optional[float] = None # 0.0–1.0
art22_risk_level: SupplyChainRiskLevel = SupplyChainRiskLevel.LOW
implementing_act_applies: bool = False
last_assessment_date: Optional[date] = None
cooperation_group_guidance: Optional[str] = None # Reference to published guidance
@dataclass
class Art22ComplianceTracker:
entity_name: str
vendors: list[SupplyChainVendor] = field(default_factory=list)
def add_vendor(self, vendor: SupplyChainVendor) -> None:
self.vendors.append(vendor)
def critical_vendors(self) -> list[SupplyChainVendor]:
return [v for v in self.vendors
if v.art22_risk_level == SupplyChainRiskLevel.CRITICAL]
def vendors_requiring_implementing_act_compliance(self) -> list[SupplyChainVendor]:
return [v for v in self.vendors if v.implementing_act_applies]
def art21_d_update_required(self) -> bool:
"""Returns True if any vendor risk level changed and Art.21(2)(d) contracts need updating."""
return len(self.critical_vendors()) > 0
def generate_supplier_risk_report(self) -> dict:
return {
"entity": self.entity_name,
"total_vendors": len(self.vendors),
"critical_count": len(self.critical_vendors()),
"implementing_act_applies_count": len(
self.vendors_requiring_implementing_act_compliance()
),
"art21_d_update_required": self.art21_d_update_required(),
"critical_vendors": [v.name for v in self.critical_vendors()],
}
# Example: entity using cloud + CDN vendors
tracker = Art22ComplianceTracker(entity_name="AcmeTech GmbH")
tracker.add_vendor(SupplyChainVendor(
name="HyperCloud EU",
category=ICTCategory.CLOUD,
country_of_origin="IE",
market_share_eu=0.32,
art22_risk_level=SupplyChainRiskLevel.HIGH,
implementing_act_applies=False,
cooperation_group_guidance="CG-2025-04-CLOUD-001",
))
tracker.add_vendor(SupplyChainVendor(
name="GlobalCDN Inc",
category=ICTCategory.CDN,
country_of_origin="US",
market_share_eu=0.41,
art22_risk_level=SupplyChainRiskLevel.CRITICAL,
implementing_act_applies=True,
cooperation_group_guidance="CG-2025-07-CDN-002",
))
report = tracker.generate_supplier_risk_report()
# {'entity': 'AcmeTech GmbH', 'total_vendors': 2, 'critical_count': 1,
# 'implementing_act_applies_count': 1, 'art21_d_update_required': True,
# 'critical_vendors': ['GlobalCDN Inc']}
Art.23: Reporting Obligations
Article 23 is the operational centrepiece of NIS2 for most development teams. It mandates that essential and important entities report significant incidents to their national CSIRT (and/or NCA) using a structured three-phase timeline.
What Constitutes a Significant Incident
Art.23(3) defines a significant incident as one that:
- Causes or is capable of causing severe operational disruption of the service or financial loss for the entity, OR
- Affects or is capable of affecting other natural or legal persons by causing considerable material or non-material damage
The "capable of causing" formulation is critical — you do not need confirmed impact to trigger reporting. A security incident that could cause severe disruption must be reported even if your mitigation succeeded before disruption occurred.
Significant Incident Classification Criteria
NCAs use the following factors to assess whether an incident meets the "significant" threshold:
| Factor | Threshold Indicators |
|---|---|
| Number of users affected | >1,000 affected users OR complete service unavailability for entity's user base |
| Duration | >1 hour for essential services; >4 hours for important services |
| Geographic scope | Incident affects multiple member states |
| Financial impact | Estimated loss >€1M or >1% annual turnover |
| Reputational damage | Media coverage or regulatory attention |
| Data exposure | Personal data of >1,000 individuals at risk |
| Infrastructure impact | Affects other network/information systems |
The Three-Phase Reporting Timeline
Art.23(1) establishes three sequential reporting obligations:
T+0: Awareness of significant incident
│
├─── T+24h: EARLY WARNING (Art.23(1)(a))
│ Required: Yes/No on possible malicious cause
│ Optional: initial assessment of impact + affected systems
│
├─── T+72h: INCIDENT NOTIFICATION (Art.23(1)(b))
│ Required: Full incident details (see schema below)
│ Optional: updated severity assessment
│
└─── T+1 month: FINAL REPORT (Art.23(1)(c))
Required: complete incident description + root cause
Required: mitigation measures implemented
Required: cross-border impact (if any)
"Awareness" definition: The clock starts when your organisation becomes aware — meaning any employee, system alert, or third-party notification that creates actual knowledge of an incident meeting the significance threshold. You cannot delay the clock by delaying internal escalation.
Technical Reporting Schema
The European Union Agency for Cybersecurity has published the NIS2 incident reporting schema. The 72-hour notification (Art.23(1)(b)) must include:
from dataclasses import dataclass, field
from enum import Enum
from datetime import datetime
from typing import Optional
class IncidentSeverity(Enum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
CRITICAL = "critical"
class IncidentType(Enum):
RANSOMWARE = "ransomware"
DATA_BREACH = "data_breach"
DDoS = "ddos"
SUPPLY_CHAIN = "supply_chain_compromise"
INSIDER_THREAT = "insider_threat"
PHISHING = "phishing_spear_phishing"
ZERO_DAY = "zero_day_exploitation"
SYSTEM_FAILURE = "system_failure_non_malicious"
CONFIGURATION_ERROR = "configuration_error"
UNKNOWN = "unknown"
class ReportingPhase(Enum):
EARLY_WARNING = "early_warning" # T+24h
NOTIFICATION = "notification" # T+72h
FINAL_REPORT = "final_report" # T+1 month
@dataclass
class AffectedService:
service_name: str
subsector: str # From NIS2 Annex I/II
users_affected_count: Optional[int]
availability_impact_hours: Optional[float]
geographic_scope: list[str] # ISO 3166-1 alpha-2 MS codes
cross_border: bool = False
@dataclass
class NIS2IncidentReport:
# Entity identification
entity_name: str
entity_nca_registration_id: str # From Art.24 registration
entity_sector: str # NIS2 Annex I/II
entity_country: str # ISO 3166-1 alpha-2 (lead NCA jurisdiction)
entity_contact_email: str
csirt_national: str # Target CSIRT identifier
# Incident metadata
incident_reference: str # Internal incident ID
awareness_datetime: datetime # T+0 clock start
incident_start_datetime: Optional[datetime]
incident_end_datetime: Optional[datetime] # None if ongoing
incident_type: IncidentType
severity: IncidentSeverity
reporting_phase: ReportingPhase
# Impact
affected_services: list[AffectedService] = field(default_factory=list)
financial_loss_estimated_eur: Optional[float] = None
# Art.23(1)(a) Early Warning specific
possible_malicious_cause: Optional[bool] = None
possible_cross_border_impact: Optional[bool] = None
# Art.23(1)(b) Notification specific
initial_assessment_text: Optional[str] = None
ioc_hashes: list[str] = field(default_factory=list) # sha256 IOCs
affected_ip_ranges: list[str] = field(default_factory=list) # CIDR notation
attack_vector: Optional[str] = None
# Art.23(1)(c) Final Report specific
root_cause: Optional[str] = None
mitigation_measures: Optional[str] = None
lessons_learned: Optional[str] = None
def validate_phase_completeness(self) -> list[str]:
"""Returns list of missing required fields for the current reporting phase."""
errors = []
if self.reporting_phase == ReportingPhase.EARLY_WARNING:
if self.possible_malicious_cause is None:
errors.append("possible_malicious_cause required for early warning")
if self.possible_cross_border_impact is None:
errors.append("possible_cross_border_impact required for early warning")
elif self.reporting_phase == ReportingPhase.NOTIFICATION:
if not self.initial_assessment_text:
errors.append("initial_assessment_text required for notification")
if not self.affected_services:
errors.append("at least one affected_service required for notification")
if not self.attack_vector:
errors.append("attack_vector required for notification")
elif self.reporting_phase == ReportingPhase.FINAL_REPORT:
if not self.root_cause:
errors.append("root_cause required for final report")
if not self.mitigation_measures:
errors.append("mitigation_measures required for final report")
if self.incident_end_datetime is None:
errors.append("incident_end_datetime required for final report (or mark as ongoing)")
return errors
def deadline_datetime(self) -> datetime:
"""Returns the reporting deadline for the current phase."""
from datetime import timedelta
deadlines = {
ReportingPhase.EARLY_WARNING: timedelta(hours=24),
ReportingPhase.NOTIFICATION: timedelta(hours=72),
ReportingPhase.FINAL_REPORT: timedelta(days=30),
}
return self.awareness_datetime + deadlines[self.reporting_phase]
def hours_remaining(self) -> float:
"""Returns hours remaining before current phase deadline."""
from datetime import timezone
now = datetime.now(tz=self.awareness_datetime.tzinfo)
delta = self.deadline_datetime() - now
return delta.total_seconds() / 3600
Where to Submit Reports
Art.23(1) requires submission to the national CSIRT. For entities also subject to NCA supervision under Art.32, the NCA may also receive reports. In practice most member states provide a unified submission portal:
| Member State | CSIRT / Submission Portal |
|---|---|
| Germany | BSI: meldestelle.bsi.de (BSIG §8b) |
| France | ANSSI: cert.ssi.gouv.fr/rapporter |
| Netherlands | NCSC-NL: ncsc.nl/meldingen |
| Austria | CERT.at: cert.at/de/meldungen |
| Belgium | CCB: report.ccb.belgium.be |
| Sweden | NCSC-SE: ncsc.se/rapportera |
| Spain | CCN-CERT: ens.ccn.cni.es |
For public administration entities within scope of CERT-EU, Art.23(1) additionally requires notification to CERT-EU.
Voluntary Reporting Under Art.23(7)
Art.23(7) permits entities to voluntarily report incidents that do not meet the significance threshold. Voluntary reports receive the same CSIRT assistance and confidentiality protections as mandatory reports. Reasons to report voluntarily:
- You are uncertain whether the significance threshold is met (early voluntary report protects against later finding that it was mandatory)
- You have threat intelligence that may benefit other entities in your sector
- You want CSIRT technical assistance without meeting the mandatory threshold
Voluntary reports are explicitly excluded from penalty proceedings — Art.23(7) states that voluntary notifications shall not subject the notifying entity to additional obligations.
Art.23 and GDPR Article 33 Interaction
Where a significant NIS2 incident also involves a personal data breach, Art.23 reporting and GDPR Art.33 reporting run concurrently with different timelines and recipients:
| Dimension | NIS2 Art.23 | GDPR Art.33 |
|---|---|---|
| Early warning | 24h to CSIRT | No equivalent |
| Notification | 72h to CSIRT/NCA | 72h to Data Protection Authority |
| Final report | 1 month to CSIRT/NCA | No mandatory follow-up (but may be requested) |
| Recipient | CSIRT + NCA | Data Protection Authority (DPA) |
| Data subject notification | Not required by NIS2 | Required by Art.34 if high risk |
| Penalty | Art.32(7)/(8) fines | GDPR Art.83 fines |
The two reporting streams are independent. Submitting an Art.23 report does not satisfy Art.33 obligations, and vice versa. Both must be filed.
@dataclass
class DualReportingObligation:
"""Tracks parallel NIS2 Art.23 and GDPR Art.33 reporting obligations."""
incident_reference: str
awareness_datetime: datetime
personal_data_affected: bool
personal_data_subjects_count: Optional[int] = None
personal_data_risk_level: Optional[str] = None # "low" | "medium" | "high"
def nis2_deadlines(self) -> dict[str, datetime]:
from datetime import timedelta
return {
"early_warning": self.awareness_datetime + timedelta(hours=24),
"notification": self.awareness_datetime + timedelta(hours=72),
"final_report": self.awareness_datetime + timedelta(days=30),
}
def gdpr_deadlines(self) -> dict[str, datetime]:
from datetime import timedelta
result = {
"art33_dpa_notification": self.awareness_datetime + timedelta(hours=72),
}
if self.personal_data_risk_level == "high":
result["art34_subject_notification"] = self.awareness_datetime + timedelta(hours=72)
return result
def all_pending_actions(self) -> list[dict]:
actions = []
for phase, deadline in self.nis2_deadlines().items():
actions.append({
"framework": "NIS2 Art.23",
"action": phase,
"deadline": deadline.isoformat(),
"recipient": "CSIRT + NCA",
})
if self.personal_data_affected:
for action, deadline in self.gdpr_deadlines().items():
actions.append({
"framework": "GDPR Art.33/34",
"action": action,
"deadline": deadline.isoformat(),
"recipient": "Data Protection Authority" if "art33" in action else "Data Subjects",
})
return sorted(actions, key=lambda x: x["deadline"])
Art.23 Enforcement: What Happens If You Miss a Deadline
Art.32(4)(a) and Art.33(4)(a) make failure to notify a significant incident an enforceable infringement. Sanctions include:
- Essential entities: Administrative fines up to €10M or 2% of global annual turnover (Art.34(4))
- Important entities: Administrative fines up to €7M or 1.4% of global annual turnover (Art.34(3))
- NCA corrective orders: Binding instruction to submit the overdue report immediately
- Publication: NCA may publish the entity name and the infringement finding
The intent-based approach applies: good-faith errors in classification (reporting too late because significance was genuinely unclear) are treated more leniently than deliberate concealment.
Art.24: Registration Obligations
Article 24 requires essential and important entities to register with their national competent authority. Registration is the mechanism by which NCAs build the entity catalogue used for Art.32/33 supervisory activities.
Registration Data Set
Art.24(1) requires entities to provide, at a minimum:
@dataclass
class NIS2EntityRegistration:
# Core identification
entity_name: str
legal_entity_identifier: Optional[str] # LEI code if available
country_of_registration: str # ISO 3166-1 alpha-2
registered_address: str
# Sector classification (NIS2 Annex I / Annex II)
annex: str # "I" for essential, "II" for important
sector: str # e.g. "energy", "transport", "banking"
subsector: str # e.g. "electricity", "air", "credit_institutions"
# NCA jurisdiction (may differ from registration country — see Art.18)
lead_nca_country: str # ISO 3166-1 alpha-2
lead_nca_identifier: str # NCA identifier per member state
# Contact details
primary_contact_name: str
primary_contact_email: str
primary_contact_phone: str
security_contact_email: str # Dedicated security notification address
security_contact_phone: str
# Technical network identifiers
autonomous_system_numbers: list[int] # BGP ASNs (if applicable)
ip_address_ranges: list[str] # CIDR notation (if applicable)
domain_names: list[str] # Primary domains operated
# Entity size
employee_count: Optional[int]
annual_turnover_eur: Optional[float]
# Multi-establishment flag
has_establishments_in_other_ms: bool = False
other_ms_countries: list[str] = field(default_factory=list) # ISO 3166-1 alpha-2
def entity_type(self) -> str:
return "essential" if self.annex == "I" else "important"
def requires_whois_compliance(self) -> bool:
"""Returns True if entity is subject to Art.19 DNS/WHOIS obligations."""
return self.sector in ("internet_exchange_points", "dns_service_providers",
"tld_name_registries", "domain_name_registration_services")
Registration Deadlines
Art.24(2) sets the initial registration deadline at 17 April 2025 for entities that were already in scope when NIS2 took effect (18 October 2024). For entities that become in-scope after transposition (due to size growth, new sector classification, or NCA determination), the deadline is within 3 months of becoming in-scope.
Updates to registration data must be submitted within 3 months of any change in the registered information.
What NCAs Do With Registration Data
Art.24(3) requires member states to compile and publish entity lists by 17 April 2027. This list is transmitted to:
- ENISA: for the EU-wide risk assessment under Art.22
- The Commission: for implementing acts under Art.21(5)
- CSIRTs: to match incoming incident reports to known entities
Critically, NCAs also use the registration to validate Art.23 incident reports — the entity_nca_registration_id in your report must match your Art.24 registration. Mismatches create processing delays.
Registration for Specific Entity Types
DNS Service Providers and TLD Registries (Art.24(4)): These entities must additionally provide the technical competent authority with:
- WHOIS database access point
- DNS resolution service endpoint
- Responsible disclosure contact for Art.12 CVD obligations
Public Administration Entities: Register with their member state's designated competent authority for public administration. Some member states (e.g., Germany's BSIG) have separate registration portals for federal vs. Länder authorities.
Third-Country Entities with EU Establishment (Art.18(3)): Register in the member state of their EU establishment, not the country of headquarters. If operating through multiple EU establishments, the member state of the establishment where the decision-making centre operates is the lead NCA jurisdiction.
Art.22–24 Compliance Checklist
| Article | Obligation | Implementation |
|---|---|---|
| Art.22 | Monitor ENISA + Cooperation Group supply chain risk assessments | Subscribe to ENISA newsletter + Cooperation Group published outputs |
| Art.22 | Update Art.21(2)(d) vendor contracts following critical supply chain findings | Quarterly vendor risk review cycle tied to Cooperation Group publication schedule |
| Art.22 | Track whether Commission implementing acts apply to your ICT vendor categories | Register for EUR-Lex alerts on NIS2 implementing acts |
| Art.23 | Define internal "significant incident" classification criteria | Map Art.23(3) criteria to your incident severity matrix |
| Art.23 | Implement T+24h early warning capability | Automated CSIRT notification on P1 incident creation |
| Art.23 | Implement T+72h notification workflow with required data fields | Incident management integration with national CSIRT portal |
| Art.23 | Implement T+1 month final report process | Post-incident review → final report generation |
| Art.23 | Separate Art.23 and GDPR Art.33 reporting streams | Dual-track incident response runbook |
| Art.23 | Train incident response team on "awareness" clock definition | IR runbook: awareness = first staff knowledge, not confirmed severity |
| Art.24 | Register with lead NCA before 17 April 2025 (or within 3 months of scope trigger) | NCA registration portal submission |
| Art.24 | Maintain registration data: notify changes within 3 months | Annual registration data review + change notification process |
| Art.24 | Include NCA registration ID in all Art.23 incident reports | IR tooling: auto-populate registration ID field |
Art.22–24 in the NIS2 Compliance Architecture
Annex I/II Entity (Essential or Important)
│
├── Art.24: Register with lead NCA
│ └── Provides: entity_id, sector, IP ranges, ASN, contacts
│
├── Art.22: Monitor EU supply chain risk assessments
│ └── Informs: Art.21(2)(d) supplier contracts + Art.21(5) implementing act compliance
│
└── Art.23: Report significant incidents
├── T+24h → Early Warning → CSIRT (+ CERT-EU for public admin)
├── T+72h → Notification → CSIRT + NCA
└── T+1mo → Final Report → CSIRT + NCA
└── Parallel: GDPR Art.33 → DPA (independent stream)
Next in the NIS2 Series
- Art.25–28: Security of network and information systems — specific obligations for DNS service providers, TLD registries, cloud computing services, and data centre service providers
- Art.29–30: Coordination with sector-specific legislation (DORA, AI Act, CRA) and supervisory interaction where multiple regulatory regimes apply
See Also
- NIS2 Art.17–21: Liability, Jurisdiction, DNS Data, Governance, and the 10 Risk Management Measures — Art.21(2)(d) supply chain obligation at entity level that Art.22 extends to EU level
- NIS2 Art.13–16: Union Crisis Response Plan, International Cooperation, Peer Reviews, ENISA Reporting — EU coordination bodies whose outputs feed into Art.22 risk assessments
- GDPR Art.33: Personal Data Breach Notification to the Supervisory Authority — parallel DPA notification stream alongside Art.23 CSIRT notification
- NIS2 Art.24: Registration Obligations — Essential and Important Entities — deep-dive on the Art.24 NCA registration process and deadline
- EU Incident Reporting: NIS2, GDPR, DORA Parallel Reporting — coordinating Art.23 with GDPR Art.33 and DORA Art.19 simultaneously