2026-04-21·15 min read·

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 CategoryTypical Assessment FocusArt.21 Downstream Effect
Cloud computing servicesHyperscaler concentration risk, data residencyArt.21(2)(d) cloud supplier contracts
Content delivery networksBGP hijacking, single-AS concentrationArt.21(2)(a) network infrastructure measures
Domain Name System servicesResolver concentration, DNSSEC adoptionArt.21(2)(a) DNS resilience measures
Managed security servicesOutsourcer access scope, incident data exposureArt.21(2)(d)(e) supplier audits
Network equipmentFirmware supply chain, vendor country of originArt.21(2)(d) procurement criteria
Internet exchange pointsIXP concentration, peering resilienceArt.21(2)(a) connectivity redundancy
Software development toolsBuild pipeline integrity, dependency managementArt.21(2)(d) software supply chain

Art.22 Methodology and Criteria

The risk assessment methodology under Art.22(2) evaluates supply chains against:

  1. Market concentration — the share of essential/important entities dependent on a single supplier
  2. Dependency depth — whether the ICT service is deeply embedded and hard to replace
  3. Country-of-origin risks — vendor jurisdiction, ownership structures, legal obligations to share data
  4. Vulnerability surface — known vulnerability history, patch cadence, exploit frequency
  5. 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:

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:

FactorThreshold 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 scopeIncident affects multiple member states
Financial impactEstimated loss >€1M or >1% annual turnover
Reputational damageMedia coverage or regulatory attention
Data exposurePersonal data of >1,000 individuals at risk
Infrastructure impactAffects 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 StateCSIRT / Submission Portal
GermanyBSI: meldestelle.bsi.de (BSIG §8b)
FranceANSSI: cert.ssi.gouv.fr/rapporter
NetherlandsNCSC-NL: ncsc.nl/meldingen
AustriaCERT.at: cert.at/de/meldungen
BelgiumCCB: report.ccb.belgium.be
SwedenNCSC-SE: ncsc.se/rapportera
SpainCCN-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:

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:

DimensionNIS2 Art.23GDPR Art.33
Early warning24h to CSIRTNo equivalent
Notification72h to CSIRT/NCA72h to Data Protection Authority
Final report1 month to CSIRT/NCANo mandatory follow-up (but may be requested)
RecipientCSIRT + NCAData Protection Authority (DPA)
Data subject notificationNot required by NIS2Required by Art.34 if high risk
PenaltyArt.32(7)/(8) finesGDPR 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:

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:

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:

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

ArticleObligationImplementation
Art.22Monitor ENISA + Cooperation Group supply chain risk assessmentsSubscribe to ENISA newsletter + Cooperation Group published outputs
Art.22Update Art.21(2)(d) vendor contracts following critical supply chain findingsQuarterly vendor risk review cycle tied to Cooperation Group publication schedule
Art.22Track whether Commission implementing acts apply to your ICT vendor categoriesRegister for EUR-Lex alerts on NIS2 implementing acts
Art.23Define internal "significant incident" classification criteriaMap Art.23(3) criteria to your incident severity matrix
Art.23Implement T+24h early warning capabilityAutomated CSIRT notification on P1 incident creation
Art.23Implement T+72h notification workflow with required data fieldsIncident management integration with national CSIRT portal
Art.23Implement T+1 month final report processPost-incident review → final report generation
Art.23Separate Art.23 and GDPR Art.33 reporting streamsDual-track incident response runbook
Art.23Train incident response team on "awareness" clock definitionIR runbook: awareness = first staff knowledge, not confirmed severity
Art.24Register with lead NCA before 17 April 2025 (or within 3 months of scope trigger)NCA registration portal submission
Art.24Maintain registration data: notify changes within 3 monthsAnnual registration data review + change notification process
Art.24Include NCA registration ID in all Art.23 incident reportsIR 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


See Also