NIS2 Art.37–40: Criminal Sanctions, NCA Confidentiality, Peer Reviews, and EU-CyCLONe — Developer Guide (2026)
Articles 33–36 defined the administrative enforcement framework — fines, supervisory orders, reactive and proactive NCA powers. Articles 37–40 close out the directive's enforcement and cooperation chapters with four functionally distinct mechanisms:
- Art.37: Member State discretion to criminalise NIS2 violations — creating personal criminal liability for CTOs, CISOs, and executives in some jurisdictions
- Art.38: Mandatory confidentiality and data protection obligations on NCAs — governing what supervisory information can and cannot be disclosed
- Art.39: ENISA-led peer reviews of national cybersecurity strategies and implementation quality
- Art.40: EU-CyCLONe — the operational crisis coordination layer sitting above the CSIRT Network during large-scale cross-border incidents
For developers and compliance engineers, Art.37 is the most operationally significant: it means the same NIS2 violation can be purely administrative in one Member State and criminally prosecutable in another.
Art.37: Criminal Sanctions — Member State Discretion and Personal Liability
The Architecture of Art.37
Art.37 does not mandate criminal sanctions — it explicitly grants Member States discretion to impose them. The article reads: "Member States may lay down rules on criminal sanctions applicable to infringements of national provisions adopted pursuant to this Directive."
This discretionary structure creates a highly fragmented criminal liability landscape across the EU:
| Member State | Criminal Sanctions Approach | Key Legislation |
|---|---|---|
| Germany | BSIG-neu §38 — criminal liability for management failures in critical infrastructure | IT-Sicherheitsgesetz 3.0 (in progress) |
| France | Parquet national cyber (PNC) — dedicated cybercrime prosecutors, active NIS2 referral pipeline | Code pénal, ANSSI referral protocol |
| Netherlands | NCSC referral to Openbaar Ministerie for severe NIS2 violations | Wet beveiliging netwerk- en informatiesystemen 2 |
| Austria | Cybersecurity-Gesetz with criminal overlay for intentional violations | KSG + StGB overlap |
| Sweden | NCSC referral mechanism for criminal prosecution of intentional or reckless violations | NIS2-implementeringslagen |
What Constitutes a Criminalised NIS2 Violation?
Member States that have enacted criminal sanctions typically focus on:
- Intentional obstruction of NCA supervision — refusing access to systems or documents during an authorised audit (Art.32/33 supervisory actions)
- Deliberate failure to report significant incidents — knowingly suppressing Art.23 notifications
- Falsification of compliance documentation — submitting inaccurate information to NCAs
- Gross negligence in essential infrastructure — reckless failure to implement Art.21 minimum measures that causes actual harm
from dataclasses import dataclass
from enum import Enum
from typing import Optional
class SanctionType(Enum):
ADMINISTRATIVE_ONLY = "administrative_only"
CRIMINAL_AVAILABLE = "criminal_available"
CRIMINAL_MANDATORY = "criminal_mandatory"
class MemberState(Enum):
DE = "Germany"
FR = "France"
NL = "Netherlands"
AT = "Austria"
SE = "Sweden"
IT = "Italy"
ES = "Spain"
PL = "Poland"
@dataclass
class CriminalLiabilityProfile:
member_state: MemberState
sanction_type: SanctionType
personal_liability_scope: str
referral_mechanism: str
max_imprisonment_years: Optional[int]
def is_criminal_exposure_possible(self) -> bool:
return self.sanction_type != SanctionType.ADMINISTRATIVE_ONLY
def requires_intent(self) -> bool:
"""Criminal liability typically requires intent or gross negligence."""
return True
MEMBER_STATE_PROFILES = {
MemberState.DE: CriminalLiabilityProfile(
member_state=MemberState.DE,
sanction_type=SanctionType.CRIMINAL_AVAILABLE,
personal_liability_scope="Management board members, CISO for knowingly failing Art.21 measures in KRITIS",
referral_mechanism="BSIG §38: BSI referral to Staatsanwaltschaft",
max_imprisonment_years=3,
),
MemberState.FR: CriminalLiabilityProfile(
member_state=MemberState.FR,
sanction_type=SanctionType.CRIMINAL_AVAILABLE,
personal_liability_scope="Dirigeants (CEOs, CTOs) for intentional obstruction or negligent exposure of OES/DSP",
referral_mechanism="ANSSI referral to Parquet National Cyber",
max_imprisonment_years=5,
),
MemberState.NL: CriminalLiabilityProfile(
member_state=MemberState.NL,
sanction_type=SanctionType.CRIMINAL_AVAILABLE,
personal_liability_scope="Directors for willful obstruction of NCSC supervision",
referral_mechanism="NCSC → Openbaar Ministerie referral",
max_imprisonment_years=2,
),
}
def assess_criminal_exposure(
member_state: MemberState,
violation_type: str,
intent_present: bool,
is_management: bool,
) -> dict:
profile = MEMBER_STATE_PROFILES.get(member_state)
if not profile:
return {
"criminal_risk": "unknown",
"recommendation": f"Verify NIS2 criminal sanctions implementation in {member_state.value}",
}
if profile.sanction_type == SanctionType.ADMINISTRATIVE_ONLY:
return {"criminal_risk": "none", "sanction_type": "administrative_only"}
criminal_risk = "low"
if intent_present and is_management:
criminal_risk = "high"
elif intent_present or (is_management and "obstruction" in violation_type.lower()):
criminal_risk = "medium"
return {
"criminal_risk": criminal_risk,
"member_state": member_state.value,
"referral_mechanism": profile.referral_mechanism,
"max_imprisonment_years": profile.max_imprisonment_years,
"personal_liability_scope": profile.personal_liability_scope,
"recommendation": (
"Engage criminal defence counsel alongside regulatory counsel"
if criminal_risk == "high"
else "Monitor NCA investigation — criminal referral possible if evidence of intent emerges"
),
}
The Art.37 / Art.20 Personal Liability Intersection
Art.37 criminal exposure is highest where Art.20 management accountability has already been triggered. If an NCA finds that:
- Management was aware of a cybersecurity risk (evidenced by board minutes, risk assessments, audit findings)
- Management approved a decision not to remediate (resource allocation decisions)
- A significant incident subsequently occurred
...then an Art.23 failure-to-notify or Art.21 non-compliance finding creates a documented chain from management knowledge to material harm — which is precisely the factual pattern that criminal prosecutors seek.
Practice implication: Board-level documentation of cybersecurity risk decisions creates a record that can be used both to demonstrate Art.20 compliance and as evidence in criminal proceedings. The Art.20 governance documentation that protects the company from fines also creates a paper trail for individual liability.
Art.38: Confidentiality and Data Protection in NCA Supervision
What Art.38 Requires of NCAs
Art.38 imposes three distinct obligations on NCAs, single points of contact, and CSIRTs when they handle information obtained through NIS2 supervisory activities:
- Confidentiality of supervisory information — information obtained in the exercise of NIS2 functions must not be disclosed beyond what is necessary for the legitimate supervisory purpose
- Purpose limitation — supervisory information may only be used for the purposes for which it was collected
- Data protection compliance — processing of personal data in NIS2 supervision must comply with GDPR (for natural persons) and national implementing legislation
from dataclasses import dataclass, field
from enum import Enum
from datetime import datetime
from typing import List, Optional
class InformationCategory(Enum):
TECHNICAL_VULNERABILITY = "technical_vulnerability"
INCIDENT_REPORT = "incident_report"
SECURITY_AUDIT_FINDING = "security_audit_finding"
COMMERCIAL_SECRET = "commercial_secret"
PERSONAL_DATA = "personal_data"
NCA_INTERNAL_ANALYSIS = "nca_internal_analysis"
class DisclosurePermission(Enum):
PERMITTED = "permitted"
CONDITIONAL = "conditional"
PROHIBITED = "prohibited"
@dataclass
class SupervisoryInformation:
category: InformationCategory
contains_personal_data: bool
is_commercially_sensitive: bool
source_entity: str
obtained_via: str
obtained_at: datetime = field(default_factory=datetime.now)
def assess_disclosure_permission(
self, recipient_type: str, disclosure_purpose: str
) -> DisclosurePermission:
"""
Art.38 confidentiality assessment: can this information be disclosed?
"""
prohibited_categories = {
InformationCategory.COMMERCIAL_SECRET,
InformationCategory.TECHNICAL_VULNERABILITY,
}
if self.category in prohibited_categories:
return DisclosurePermission.PROHIBITED
if self.contains_personal_data:
# Requires GDPR legal basis for disclosure
if recipient_type not in ("other_nca", "csirt", "competent_authority"):
return DisclosurePermission.PROHIBITED
return DisclosurePermission.CONDITIONAL
# Incident reports may be shared within NIS2 cooperation framework
if (
self.category == InformationCategory.INCIDENT_REPORT
and recipient_type in ("other_nca", "enisa", "csirt_network")
):
return DisclosurePermission.CONDITIONAL
if recipient_type == "public":
return DisclosurePermission.PROHIBITED
return DisclosurePermission.PERMITTED
def can_share_with_law_enforcement(self) -> bool:
"""Art.38(4): NCAs may share with law enforcement for criminal proceedings."""
return self.category == InformationCategory.INCIDENT_REPORT
class Art38ComplianceChecker:
def __init__(self, nca_jurisdiction: str):
self.jurisdiction = nca_jurisdiction
self.disclosure_log: List[dict] = []
def validate_disclosure_request(
self,
information: SupervisoryInformation,
recipient: str,
purpose: str,
) -> dict:
permission = information.assess_disclosure_permission(recipient, purpose)
result = {
"permitted": permission == DisclosurePermission.PERMITTED,
"conditional": permission == DisclosurePermission.CONDITIONAL,
"prohibited": permission == DisclosurePermission.PROHIBITED,
"basis": self._determine_legal_basis(information, recipient, purpose),
"conditions": self._get_conditions(information, permission, recipient),
}
self.disclosure_log.append({
"timestamp": datetime.now().isoformat(),
"information_category": information.category.value,
"recipient": recipient,
"decision": permission.value,
})
return result
def _determine_legal_basis(
self, info: SupervisoryInformation, recipient: str, purpose: str
) -> Optional[str]:
if info.contains_personal_data:
return "GDPR Art.6(1)(e) — public task in NIS2 supervisory function"
if recipient in ("other_nca", "enisa"):
return "NIS2 Art.38(3) — NCA cooperation mechanism"
if recipient == "law_enforcement" and purpose == "criminal_investigation":
return "NIS2 Art.38(4) — criminal proceedings exception"
return None
def _get_conditions(
self,
info: SupervisoryInformation,
permission: DisclosurePermission,
recipient: str,
) -> List[str]:
if permission == DisclosurePermission.PROHIBITED:
return ["Disclosure not permitted under Art.38"]
conditions = []
if info.contains_personal_data:
conditions.append("GDPR data minimisation — share only necessary fields")
conditions.append("Document GDPR legal basis in disclosure record")
if permission == DisclosurePermission.CONDITIONAL:
conditions.append("Recipient must be bound by equivalent confidentiality obligations")
conditions.append("Purpose limitation must be documented before disclosure")
return conditions
The Art.38 / GDPR Intersection for Developers
When NCAs process personal data obtained during NIS2 supervision (e.g., names of individuals named in incident reports, technical staff contact details from Art.23 notifications), GDPR applies in full. For developers building systems that interact with NIS2 supervisory processes:
- Art.23 notifications containing personal data (incident responders' details, affected users' data) are covered by Art.38 — the NCA cannot forward them to third parties without legal basis
- API access logs submitted to NCAs as evidence of a security breach are supervisory information — they cannot be used for unrelated regulatory purposes
- Security audit reports submitted to satisfy Art.32 proactive supervision are commercially sensitive — the NCA cannot publish them without the entity's consent
Art.39: Peer Reviews of National Cybersecurity Strategies
What Art.39 Creates
Art.39 establishes a voluntary peer review mechanism for EU Member States' national cybersecurity strategies, capabilities, and NIS2 implementation quality. ENISA organises the reviews, which are conducted by cybersecurity experts from other Member States and ENISA staff.
The peer review framework has two levels:
- National cybersecurity strategy review — assessing whether Art.7 national strategies are effective and coherently implemented
- Implementation quality review — examining how NCAs are applying NIS2 in practice, including supervisory consistency, sector coverage, and cross-border cooperation
from dataclasses import dataclass
from typing import List
from enum import Enum
class ReviewScope(Enum):
NATIONAL_STRATEGY = "national_strategy"
NCA_IMPLEMENTATION = "nca_implementation"
CSIRT_CAPABILITIES = "csirt_capabilities"
SECTOR_COVERAGE = "sector_coverage"
CROSS_BORDER_COOPERATION = "cross_border_cooperation"
class ReviewFinding(Enum):
GOOD_PRACTICE = "good_practice"
IMPROVEMENT_RECOMMENDED = "improvement_recommended"
GAP_IDENTIFIED = "gap_identified"
BEST_PRACTICE_CANDIDATE = "best_practice_candidate"
@dataclass
class PeerReviewResult:
reviewed_member_state: str
reviewing_experts_from: List[str]
review_scope: List[ReviewScope]
findings: List[dict]
good_practices_identified: List[str]
recommendations: List[str]
review_year: int
def has_cross_border_gaps(self) -> bool:
return any(
f.get("scope") == ReviewScope.CROSS_BORDER_COOPERATION.value
and f.get("finding") == ReviewFinding.GAP_IDENTIFIED.value
for f in self.findings
)
def generate_improvement_roadmap(self) -> List[dict]:
return [
{
"recommendation": rec,
"priority": "high" if "CSIRT" in rec or "cross-border" in rec.lower() else "medium",
"responsible_body": "NCA + ENISA support",
}
for rec in self.recommendations
]
Why Art.39 Peer Reviews Matter for Developers
Peer reviews have indirect but real effects on the regulatory environment that developers operate in:
Supervisory consistency pressure: When a peer review identifies that an NCA is applying NIS2 inconsistently (e.g., granting exemptions to certain sectors that other NCAs do not), ENISA publishes the findings. This creates political pressure toward harmonisation — potentially tightening enforcement in historically lenient jurisdictions.
Good practice diffusion: Art.39(6) requires ENISA to share good practices identified in peer reviews across all Member States. A German NCA practice of requiring specific API security standards in Art.21 implementation could become a de-facto EU standard through this mechanism before formal technical guidelines are issued.
Investment in cybersecurity infrastructure: Peer reviews frequently identify gaps in national CSIRT capabilities, threat intelligence sharing, and sector-specific oversight. Countries that receive peer review findings typically increase investment in these areas — affecting the operational environment that regulated entities work within.
Art.40: EU-CyCLONe — Cyber Crisis Liaison Organisation Network
The Three-Layer Crisis Coordination Architecture
NIS2 creates a three-layer structure for EU-level cybersecurity coordination:
┌─────────────────────────────────────────────────────────────┐
│ COOPERATION GROUP (Art.14) │
│ Strategic / Policy / Long-term threat landscape │
│ Members: NCAs, Commission, ENISA (observer) │
│ Meets: Regular intervals, not crisis-responsive │
├─────────────────────────────────────────────────────────────┤
│ CSIRT NETWORK (Art.15) │
│ Technical / Operational / Incident response │
│ Members: National CSIRTs + CERT-EU │
│ Meets: Regular + ad-hoc for significant incidents │
├─────────────────────────────────────────────────────────────┤
│ EU-CyCLONe (Art.40) │
│ Crisis management / Large-scale cross-border incidents │
│ Members: National cyber crisis authorities │
│ Meets: Crisis-triggered + at least 1x/year exercise │
└─────────────────────────────────────────────────────────────┘
EU-CyCLONe (formerly IICB-coordinated, now a formal NIS2 body) fills the gap between technical CSIRT coordination and strategic policy response. It activates when an incident or threat exceeds what a single Member State or the CSIRT Network can handle operationally.
Art.40 Activation Criteria
EU-CyCLONe activates for large-scale cybersecurity incidents or crises — defined in the NIS2 implementing framework as incidents that:
- Affect at least three EU Member States simultaneously
- Have actual or potential significant impact on critical infrastructure, the internal market, or public security
- Exceed the technical response capacity of the affected Member States' individual CSIRTs
- Require coordinated political and operational response (not just technical mitigation)
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import List, Optional
class CrisisLevel(Enum):
NATIONAL = "national" # Handled by Member State NCA/CSIRT alone
BILATERAL = "bilateral" # Two-Member-State coordination via CSIRT Network
EU_CYCLONE = "eu_cyclone" # Large-scale, 3+ Member States, Art.40 activation
INTEGRATED_CRISIS = "integrated_crisis" # EU-CyCLONe + Council Crisis Mechanism
@dataclass
class CyberCrisisEvent:
name: str
affected_member_states: List[str]
sectors_affected: List[str]
estimated_impact: str
detected_at: datetime = field(default_factory=datetime.now)
csirt_capacity_exceeded: bool = False
political_coordination_required: bool = False
def classify_crisis_level(self) -> CrisisLevel:
if len(self.affected_member_states) < 2:
return CrisisLevel.NATIONAL
if len(self.affected_member_states) == 2 and not self.csirt_capacity_exceeded:
return CrisisLevel.BILATERAL
if len(self.affected_member_states) >= 3 or self.csirt_capacity_exceeded:
if self.political_coordination_required:
return CrisisLevel.INTEGRATED_CRISIS
return CrisisLevel.EU_CYCLONE
return CrisisLevel.BILATERAL
def requires_eu_cyclone_activation(self) -> bool:
level = self.classify_crisis_level()
return level in (CrisisLevel.EU_CYCLONE, CrisisLevel.INTEGRATED_CRISIS)
def get_activation_rationale(self) -> str:
if self.requires_eu_cyclone_activation():
return (
f"EU-CyCLONe activation required: "
f"{len(self.affected_member_states)} Member States affected "
f"({'CSIRT capacity exceeded' if self.csirt_capacity_exceeded else '3+ MS threshold met'})"
)
return f"Crisis level {self.classify_crisis_level().value} — EU-CyCLONe not required"
@dataclass
class EUCyCLONeResponse:
crisis: CyberCrisisEvent
coordinating_presidency: str
enisa_support_requested: bool
commission_briefed: bool
situational_awareness_reports: List[dict] = field(default_factory=list)
activated_at: Optional[datetime] = None
def activate(self) -> dict:
self.activated_at = datetime.now()
return {
"activation_time": self.activated_at.isoformat(),
"crisis": self.crisis.name,
"affected_ms": self.crisis.affected_member_states,
"coordinating_presidency": self.coordinating_presidency,
"enisa_support": self.enisa_support_requested,
"next_step": "Situational awareness report within 4h of activation",
}
def add_situational_report(self, content: str, authors: List[str]) -> None:
self.situational_awareness_reports.append({
"timestamp": datetime.now().isoformat(),
"content": content,
"authors": authors,
"report_number": len(self.situational_awareness_reports) + 1,
})
# Example: Ransomware campaign across 5 EU energy sector operators
energy_ransomware_crisis = CyberCrisisEvent(
name="Ransomware campaign: EU energy grid operators",
affected_member_states=["DE", "FR", "NL", "BE", "AT"],
sectors_affected=["energy", "electricity_distribution"],
estimated_impact="Potential disruption to distribution networks in 5 Member States",
csirt_capacity_exceeded=True,
political_coordination_required=True,
)
print(energy_ransomware_crisis.classify_crisis_level()) # INTEGRATED_CRISIS
print(energy_ransomware_crisis.get_activation_rationale())
EU-CyCLONe and the Developer's Crisis Response Obligations
For developers building systems in Art.3 covered entities, EU-CyCLONe activation has concrete operational implications:
Increased NCA contact tempo: During an EU-CyCLONe event, NCAs will contact affected covered entities more frequently and with shorter response windows. An Art.23 "significant incident" notification that normally has a 72-hour initial report window may be accelerated by NCA request during a declared crisis.
Mandatory technical cooperation with CSIRTs: EU-CyCLONe coordinates CSIRT assistance. If your sector is affected, your national CSIRT may request:
- Real-time indicator-of-compromise (IoC) feeds from your security tooling
- Access to network logs to support threat actor attribution
- Deployment of CSIRT-provided detection signatures in your environment
Cross-border incident linkage: EU-CyCLONe's situational awareness function actively identifies whether individual incidents reported to NCAs are part of a coordinated campaign. If your incident is linked to a larger EU-CyCLONe event, your NCA may share your incident report details with other Member State NCAs under Art.38 confidentiality constraints.
Putting Art.37–40 Together: The Enforcement Architecture Complete
NIS2 ENFORCEMENT & COOPERATION COMPLETE ARCHITECTURE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
INCIDENT OCCURS
│
├─→ Art.23 Notification ──→ NCA / CSIRT receives
│ │
│ ┌───────────────┴────────────────┐
│ │ │
│ Administrative Criminal
│ Enforcement Referral
│ (Art.32/33) (Art.37)
│ │ │
│ ┌─────────┴─────────┐ National prosecutor
│ │ │ (DE: Staatsanwaltschaft
│ Essential Important FR: Parquet National Cyber
│ Entity Entity NL: Openbaar Ministerie)
│ Art.32 Art.33 │
│ (proactive) (reactive) │
│ │ │ │
│ Art.35 fines Art.36 fines Art.37 criminal
│ €10M/2% €7M/1.4% proceedings
│ │ │
│ └────────┬──────────┘
│ │
│ Art.38 confidentiality applies
│ (NCA cannot disclose supervisory
│ data beyond legitimate purpose)
│
LARGE-SCALE
CROSS-BORDER
INCIDENT?
│
└─→ 3+ Member States? → EU-CyCLONe (Art.40) activates
+ Cooperation Group (Art.14)
+ CSIRT Network (Art.15)
+ ENISA support (Art.39 peer review data)
Developer Compliance Checklist: Art.37–40
Art.37 — Criminal Sanctions Preparedness
- Identify which Member States you operate in that have enacted Art.37 criminal sanctions
- Map Art.20 management accountability documentation to criminal liability risk (board minutes, risk registers)
- Ensure incident response protocols include legal counsel notification alongside regulatory notification
- Distinguish between negligent, reckless, and intentional violation — criminal threshold typically requires intent or gross negligence
Art.38 — Confidentiality in NCA Interactions
- When submitting Art.23 notifications, mark commercially sensitive information explicitly
- Request NCA confirmation that submitted data will not be shared beyond the supervisory purpose
- If your data is personal data (staff names, affected user details): ensure GDPR Art.6(1)(e) basis is clear
- For Art.32 audit submissions: ask NCA about their disclosure policy before submitting detailed technical documentation
Art.39 — Peer Reviews (Indirect Impact)
- Monitor ENISA peer review publications — good practices become de-facto standards
- Track whether your Member State's NCA is subject to a current peer review — increased scrutiny possible
- Review ENISA's published peer review findings for sector-specific recommendations affecting your compliance scope
Art.40 — EU-CyCLONe Crisis Readiness
- Identify your incident response procedure for EU-CyCLONe escalation scenarios
- Ensure CSIRT contact points are documented and current (accelerated notification timelines apply)
- Prepare IoC sharing capabilities — EU-CyCLONe events trigger CSIRT requests for technical data
- Crisis communication chain: who decides to cooperate with cross-border CSIRT information requests?
Summary
Articles 37–40 complete NIS2's enforcement architecture by addressing the dimensions that purely administrative frameworks leave open:
- Art.37 adds personal criminal accountability to the corporate fine framework — but only where Member States choose to exercise their discretion, creating a fragmented EU criminal liability landscape that multi-jurisdictional operators must navigate separately in each country.
- Art.38 protects entities from NCA overreach by constraining how supervisory information can be used and shared — giving entities legitimate grounds to request confidentiality commitments before submitting detailed technical documentation.
- Art.39 creates a continuous improvement feedback loop at the national strategy level — peer reviews drive convergence in NCA practice that eventually affects the regulatory environment all covered entities operate within.
- Art.40 formalises the EU-CyCLONe crisis coordination layer — the operational escalation path for incidents that exceed individual Member State response capacity.
For the average developer maintaining a covered service, Art.37 is the high-priority article: criminal exposure risk should be assessed for each jurisdiction of operation, and the Art.20 governance documentation that protects the company is the same documentation that documents individual management knowledge.
This post is part of the NIS2 Directive series covering all articles with developer-focused Python implementations.