CRA Art.14/16: Vulnerability Reporting to ENISA — 24-Hour Notification, CVD Policy, and the September 2026 Deadline
On 11 September 2026, the vulnerability reporting provisions of the EU Cyber Resilience Act (Regulation (EU) 2024/2847, "CRA") begin to apply. Any manufacturer placing a product with digital elements on the EU market — software, firmware, connected hardware, SaaS — must from that date:
- Notify ENISA of any actively exploited vulnerability within 24 hours (early warning)
- Submit a full notification within 72 hours with mitigation status
- Deliver a final report within 14 days after remediation
- Operate and publish a Coordinated Vulnerability Disclosure (CVD) policy
These are not aspirational guidelines. They are directly applicable legal obligations backed by the same penalty framework as the rest of the CRA: up to €15 million or 2.5% of worldwide annual turnover for non-compliance.
The CRA SBOM and vulnerability handling requirements (Art.13) that apply from December 2027 are widely discussed. The September 2026 reporting obligations are less understood — and the implementation clock is now under five months.
What Triggers the September 2026 Date?
The CRA entered into force on 10 December 2024. Art. 71(2) provides that Articles 14 and 15 — notification of actively exploited vulnerabilities and CVD obligations — apply from 21 months after entry into force. 21 months from 10 December 2024 = 11 September 2026.
The full CRA manufacturer obligations (security-by-design, SBOM, security updates) apply from 11 December 2027 (36 months). The notification provisions are an early subset deliberately brought forward to operationalise the ENISA vulnerability monitoring system before full enforcement.
Timeline summary:
- 11 September 2026: Art.14 (actively exploited vulnerability notifications to ENISA) + Art.15 (CVD policy obligation) apply
- 11 December 2027: All remaining manufacturer obligations apply (Art.13 SBOM, Art.11 security-by-design, Art.12 conformity marking)
If your organisation is planning a single CRA compliance sprint for "late 2027", the September 2026 notification obligations will hit first — and without preparation, they will hit hard.
Scope: Who Must Report to ENISA?
Art.14 reporting obligations apply to manufacturers — defined in Art.3(13) as any natural or legal person that develops or manufactures products with digital elements or has such products designed, developed, or manufactured and markets them under their name or trademark. This covers:
- Commercial software developers placing software products on the EU market
- SaaS and cloud service providers where the software component is the primary product
- Hardware manufacturers with embedded software (IoT, industrial, consumer electronics)
- Open-source developers who commercialise their software (revenue from the product or related services)
Not covered by Art.14:
- Pure open-source projects with no commercial dimension (Art.16 provides a lighter CVD-only regime for open-source software stewards)
- Importers and distributors (they have separate obligations under Art.17-20)
- Manufacturers of products exclusively for their own use (internal enterprise tooling not placed on the market)
The scope question is often where organisations get it wrong. If your SaaS product is the primary commercial offering — not just ancillary digital processing inside a physical product — you are a manufacturer under the CRA.
The 3-Stage ENISA Notification Process (Art.14)
Stage 1: 24-Hour Early Warning
Within 24 hours of becoming aware of an actively exploited vulnerability in your product, you must submit an early warning to ENISA via the Single Reporting Platform (SRP) specified in Art.14(8).
What is "actively exploited"? The CRA does not define this with the precision that security practitioners would prefer. The operative criterion is that the vulnerability is being exploited in the wild — not merely that a proof-of-concept exists. Indicators include:
- CISA KEV (Known Exploited Vulnerabilities) listing
- Confirmed exploitation reports from threat intelligence feeds (NVD, MITRE ATT&CK)
- Internal telemetry showing exploit payloads targeting the vulnerability in your systems
- CERT/national CSIRT advisories labelling the vulnerability as actively exploited
The 24-hour clock starts at manufacturer awareness — when your security team or monitoring systems first identify or receive credible notification that the vulnerability is being actively exploited. Building a documented awareness-and-escalation process is therefore a prerequisite: you need to prove when you became aware.
Early warning content (Art.14(3)): The early warning requires only basic information:
- Identification of the affected product and vulnerability
- Whether the vulnerability is being actively exploited
- No remediation information required at this stage
Stage 2: 72-Hour Full Notification
Within 72 hours of becoming aware, you must submit a full notification covering:
- Detailed description of the vulnerability (CVE reference where available)
- Products and versions affected
- Severity assessment (CVSS score)
- Whether a patch or mitigation is available
- Remediation or mitigation measures already taken
- Whether the vulnerability has been publicly disclosed
The 72-hour window is identical to NIS2 Art.23's significant incident notification timeline — this alignment is intentional. See the dual-reporting section below.
Stage 3: Final Report
Within 14 days of deploying a fix or mitigation, you must submit a final report including:
- Root cause analysis of the vulnerability
- Description of the vulnerability (if not yet publicly disclosed)
- Remediation measures applied
- Details of any coordinated disclosure with the reporting party
The final report is where ENISA builds its vulnerability intelligence database. This is the mechanism through which the CRA's notification regime feeds into ENISA's European vulnerability database (Art.12 EUVD, which went live in 2024 as the EU counterpart to NVD).
CVD Policy Obligation (Art.15)
Separately from the ENISA notification obligation, Art.15 requires manufacturers to establish and operate a Coordinated Vulnerability Disclosure policy and make it publicly accessible. This is the formal CVD programme requirement.
A compliant CVD policy under Art.15 must:
- Define the scope of products covered
- Provide a reporting channel (email, dedicated web form, security.txt)
- Specify the response timeline — how quickly you acknowledge receipt and when you commit to deliver a fix or assessment
- Define the handling process — triage, severity assessment, fix development, coordinated release
- State your disclosure timeline — the period after which you will publicly disclose the vulnerability regardless of patch availability
- Describe the coordination process with ENISA for actively exploited vulnerabilities
security.txt (RFC 9116): The practical standard for machine-readable CVD policy publication. CRA compliance does not mandate security.txt by name, but it is the de facto industry standard that ENISA's tooling and automated vulnerability management platforms consume.
# /.well-known/security.txt
Contact: mailto:security@yourcompany.com
Expires: 2027-01-01T00:00:00.000Z
Acknowledgments: https://yourcompany.com/security/hall-of-fame
Policy: https://yourcompany.com/security/cvd-policy
Preferred-Languages: en, de
Encryption: https://yourcompany.com/security/pgp-key.asc
CVD Timeline Standards: The industry baseline is 90 days from researcher notification to public disclosure (Google Project Zero standard), but many EU NCAs and ENISA guidance documents reference 45-90 days. Art.15 does not mandate a specific timeline — it requires that your published policy specifies one, and that you adhere to it.
ENISA Single Reporting Platform (SRP)
Art.14(8) mandates ENISA to establish a Single Reporting Platform for CRA vulnerability notifications. As of April 2026, ENISA has published technical guidance on the SRP and integration options.
Reporting channels:
- Web interface: Portal-based submission for individual notifications
- API integration: Structured reporting for automated notifications (recommended for organisations with volume)
- MISP integration: ENISA has indicated MISP (Malware Information Sharing Platform) compatibility for structured threat intelligence sharing
What ENISA does with notifications:
- Routes notifications to relevant national CSIRTs (NIS2 Art.12 coordination network)
- Feeds non-sensitive aggregated data into the EUVD (European Vulnerability Database)
- Triggers coordinated response for cross-border vulnerabilities
Notification confidentiality: Art.14(9) provides that information submitted to the SRP may be treated as confidential. ENISA is required to ensure that disclosure of notifications does not compromise product security or enable exploitation. Manufacturers can request that specific technical details remain restricted pending patch availability.
Python Implementation: VulnerabilityReporter
The following Python implementation provides a complete vulnerability tracking and notification pipeline that integrates with the ENISA SRP API spec.
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from enum import Enum
from typing import Optional
import json
import hashlib
class ExploitStatus(Enum):
THEORETICAL = "theoretical" # PoC exists, no confirmed exploitation
ACTIVE = "active" # Confirmed active exploitation
WIDESPREAD = "widespread" # Mass exploitation observed
class NotificationStage(Enum):
PENDING = "pending"
EARLY_WARNING_SENT = "early_warning_sent" # 24h deadline
FULL_NOTIFICATION_SENT = "full_notification_sent" # 72h deadline
FINAL_REPORT_SENT = "final_report_sent" # 14d after fix
@dataclass
class CRAVulnerability:
cve_id: str # CVE-YYYY-NNNNN or internal ID
product_name: str
product_versions: list[str]
cvss_score: float
cvss_vector: str
exploit_status: ExploitStatus
description: str
discovered_at: datetime
reporter: Optional[str] = None # Researcher/reporter name if externally reported
fix_available: bool = False
fix_deployed_at: Optional[datetime] = None
public_disclosure_at: Optional[datetime] = None
notification_stage: NotificationStage = NotificationStage.PENDING
@property
def requires_enisa_notification(self) -> bool:
return self.exploit_status in (ExploitStatus.ACTIVE, ExploitStatus.WIDESPREAD)
@property
def early_warning_deadline(self) -> datetime:
return self.discovered_at + timedelta(hours=24)
@property
def full_notification_deadline(self) -> datetime:
return self.discovered_at + timedelta(hours=72)
@property
def final_report_deadline(self) -> Optional[datetime]:
if self.fix_deployed_at:
return self.fix_deployed_at + timedelta(days=14)
return None
@property
def is_early_warning_overdue(self) -> bool:
return (
self.requires_enisa_notification
and datetime.utcnow() > self.early_warning_deadline
and self.notification_stage == NotificationStage.PENDING
)
@property
def severity(self) -> str:
if self.cvss_score >= 9.0: return "CRITICAL"
if self.cvss_score >= 7.0: return "HIGH"
if self.cvss_score >= 4.0: return "MEDIUM"
return "LOW"
@dataclass
class CVDPolicy:
product_scope: list[str]
contact_email: str
pgp_key_url: str
policy_url: str
acknowledgment_hours: int = 48 # SLA: acknowledge reporter within N hours
assessment_days: int = 14 # SLA: triage and severity assessment
fix_target_days: int = 90 # Standard 90-day disclosure window
coordinated_disclosure: bool = True
def to_security_txt(self, domain: str, expires: datetime) -> str:
lines = [
f"Contact: mailto:{self.contact_email}",
f"Expires: {expires.strftime('%Y-%m-%dT%H:%M:%S.000Z')}",
f"Policy: {self.policy_url}",
f"Encryption: {self.pgp_key_url}",
"Preferred-Languages: en",
]
return "\n".join(lines)
def to_enisa_format(self) -> dict:
return {
"scope": self.product_scope,
"contact": {"email": self.contact_email},
"sla": {
"acknowledgment_hours": self.acknowledgment_hours,
"assessment_days": self.assessment_days,
"disclosure_days": self.fix_target_days,
},
"coordinated_disclosure": self.coordinated_disclosure,
}
class CRAVulnerabilityReporter:
def __init__(self, manufacturer_id: str, enisa_api_key: str, cvd_policy: CVDPolicy):
self.manufacturer_id = manufacturer_id
self.enisa_api_key = enisa_api_key
self.cvd_policy = cvd_policy
self.vulnerabilities: list[CRAVulnerability] = []
def register_vulnerability(self, vuln: CRAVulnerability) -> str:
vuln_id = hashlib.sha256(
f"{vuln.cve_id}{vuln.discovered_at.isoformat()}".encode()
).hexdigest()[:12]
self.vulnerabilities.append(vuln)
return vuln_id
def build_early_warning_payload(self, vuln: CRAVulnerability) -> dict:
return {
"notification_type": "early_warning",
"manufacturer_id": self.manufacturer_id,
"product": {
"name": vuln.product_name,
"affected_versions": vuln.product_versions,
},
"vulnerability": {
"id": vuln.cve_id,
"exploit_status": vuln.exploit_status.value,
"severity": vuln.severity,
},
"discovered_at": vuln.discovered_at.isoformat(),
"submitted_at": datetime.utcnow().isoformat(),
}
def build_full_notification_payload(self, vuln: CRAVulnerability) -> dict:
return {
"notification_type": "full_notification",
"manufacturer_id": self.manufacturer_id,
"product": {
"name": vuln.product_name,
"affected_versions": vuln.product_versions,
},
"vulnerability": {
"id": vuln.cve_id,
"description": vuln.description,
"cvss_score": vuln.cvss_score,
"cvss_vector": vuln.cvss_vector,
"exploit_status": vuln.exploit_status.value,
"severity": vuln.severity,
},
"mitigation": {
"fix_available": vuln.fix_available,
"publicly_disclosed": vuln.public_disclosure_at is not None,
},
"discovered_at": vuln.discovered_at.isoformat(),
"submitted_at": datetime.utcnow().isoformat(),
}
def build_final_report_payload(self, vuln: CRAVulnerability) -> dict:
return {
"notification_type": "final_report",
"manufacturer_id": self.manufacturer_id,
"product": {
"name": vuln.product_name,
"affected_versions": vuln.product_versions,
},
"vulnerability": {
"id": vuln.cve_id,
"description": vuln.description,
"cvss_score": vuln.cvss_score,
"cvss_vector": vuln.cvss_vector,
},
"remediation": {
"fix_deployed_at": vuln.fix_deployed_at.isoformat() if vuln.fix_deployed_at else None,
"public_disclosure_at": vuln.public_disclosure_at.isoformat() if vuln.public_disclosure_at else None,
"reporter": vuln.reporter,
},
"submitted_at": datetime.utcnow().isoformat(),
}
def compliance_dashboard(self) -> dict:
active_vulns = [v for v in self.vulnerabilities if v.requires_enisa_notification]
overdue_early_warning = [v for v in active_vulns if v.is_early_warning_overdue]
return {
"total_vulnerabilities": len(self.vulnerabilities),
"requiring_enisa_notification": len(active_vulns),
"overdue_early_warning": len(overdue_early_warning),
"overdue_ids": [v.cve_id for v in overdue_early_warning],
"compliance_status": "NON_COMPLIANT" if overdue_early_warning else "COMPLIANT",
}
CRA × NIS2 Dual-Reporting Overlap
Organisations subject to both CRA and NIS2 face overlapping notification obligations for the same security incident. This is not an accident — the EU deliberately designed these as complementary regimes — but the reporting timelines and recipients differ.
| Obligation | CRA Art.14 | NIS2 Art.23 |
|---|---|---|
| Trigger | Actively exploited vulnerability in your product | Significant incident affecting your NIS2-covered services |
| Early warning | 24 hours to ENISA | 24 hours to CSIRT/NCA |
| Full notification | 72 hours to ENISA | 72 hours to CSIRT/NCA |
| Final report | 14 days post-fix to ENISA | 1 month post-incident to CSIRT/NCA |
| Recipient | ENISA directly | National CSIRT / NCA |
| Applies to | Manufacturers (product makers) | Essential and important entities |
Key implication: A SaaS manufacturer that is also an important entity under NIS2 must file parallel reports with different recipients. The 24-hour clock starts simultaneously for both obligations. Building a single incident response process that feeds both reporting workflows — ENISA for CRA and national CSIRT for NIS2 — is the practical approach.
The Python CRAVulnerabilityReporter above can be extended with a NIS2IncidentReporter alongside it, sharing the incident metadata but targeting different endpoints and using different payload formats.
Integration with Your SBOM Pipeline (Art.13)
The CRA's notification obligations (Art.14, September 2026) and SBOM requirements (Art.13, December 2027) are designed to work together. An SBOM-first approach makes Art.14 compliance operationally feasible:
-
SBOM as vulnerability inventory baseline: If you have a machine-readable CycloneDX or SPDX SBOM, vulnerability scanners (Grype, Trivy, OWASP Dependency-Check) can automatically identify when a component vulnerability becomes actively exploited (by cross-referencing CISA KEV or NVD exploit flags)
-
Automated ENISA notification trigger: When a KEV listing matches a component in your SBOM, the 24-hour clock starts. Automated SBOM scanning + KEV feed polling can detect this and trigger the notification workflow without manual discovery delay
-
Audit trail integration: SBOM version history (git-tracked CycloneDX files) provides the artefact chain that proves which versions were affected and when the vulnerability was introduced — critical for the final report root cause analysis
Recommended pipeline:
SBOM (CycloneDX) → Daily Grype scan → KEV feed check → CRA notification trigger
↘ CISA KEV match → 24h early warning
↘ Full notification (72h)
↘ Fix deployment → Final report (14d)
September 2026 Readiness: 25-Item Checklist
Governance (Items 1-5)
- 1. Designated security contact with authority to submit ENISA notifications
- 2. Escalation process documented: how fast can you reach the security contact 24/7?
- 3. Awareness timeline documented: what constitutes "becoming aware" of active exploitation?
- 4. Legal review of CRA Art.14 scope for all products (PDEs vs. ancillary digital elements)
- 5. Management approval of CVD policy and ENISA notification SOP
CVD Policy (Items 6-11)
- 6. CVD policy published at
/securityor equivalent - 7.
/.well-known/security.txtdeployed and valid (RFC 9116) - 8. Disclosure timeline defined in policy (recommended: 90 days from researcher notification)
- 9. Acknowledgment SLA defined and monitored (recommended: 48 hours)
- 10. Researcher acknowledgment/hall-of-fame page or process defined
- 11. CVD policy reviewed by legal for Art.15 compliance
Notification Process (Items 12-18)
- 12. ENISA SRP account registered (manufacturer account)
- 13. Early warning template created (covers Art.14(3) minimum content)
- 14. Full notification template created (covers Art.14(4) full content)
- 15. Final report template created (covers Art.14(7) post-fix content)
- 16. 24h / 72h / 14d deadline tracking integrated into incident management system
- 17. Runbook documented: step-by-step from awareness to ENISA submission
- 18. Test submission to ENISA SRP sandbox completed (if available)
Technical Pipeline (Items 19-23)
- 19. SBOM in CycloneDX or SPDX format generated for each release
- 20. Automated vulnerability scanner (Grype/Trivy) integrated into CI/CD
- 21. CISA KEV feed polling configured (daily check against SBOM components)
- 22. Alert rule: any KEV match → PagerDuty/OpsGenie trigger → security contact notified
- 23. NIS2 dual-reporting: same incident → ENISA (CRA) + national CSIRT (NIS2) parallel workflow
Audit Readiness (Items 24-25)
- 24. Notification records retained for minimum 5 years (all three stages per vulnerability)
- 25. Tabletop exercise conducted: simulated actively exploited vulnerability, full notification drill
12-Week Implementation Timeline (April → June 2026)
| Week | Activity | Owner |
|---|---|---|
| W1-2 | Scope review: which products are PDEs? Legal sign-off | Legal + Product |
| W3-4 | CVD policy drafted, security.txt deployed | Security lead |
| W5-6 | ENISA SRP account registered, notification templates created | Security lead |
| W7-8 | SBOM pipeline + automated scanner integrated into CI/CD | DevOps |
| W9 | KEV feed polling + alerting configured | DevOps |
| W10 | NIS2 dual-reporting workflow documented | Security + Compliance |
| W11 | Tabletop exercise: simulated CVE exploitation drill | All teams |
| W12 | Gap close, management sign-off, documentation complete | Management |
This 12-week timeline delivers June 2026 readiness — three months ahead of the September deadline. The buffer is intentional: NCAs beginning NIS2 audit seasons in June 2026 will look for CRA preparedness as a leading indicator.
Infrastructure Jurisdiction and Notification Scope
One aspect of CRA Art.14 that receives little coverage: the regulation imposes obligations on manufacturers wherever they are established, but the ENISA notification system is designed to route information to national CSIRTs in the member states where affected products are distributed.
If your software is distributed EU-wide (which most SaaS products are), ENISA may route your notification to multiple national CSIRTs simultaneously. This is not a burden to manufacturers — it is the mechanism through which the EU coordinates cross-border vulnerability response. But it means your final report may be reviewed by security authorities in Germany, France, the Netherlands, and Austria concurrently.
From a data residency standpoint: the CRA vulnerability notification data submitted to ENISA is processed by an EU agency operating under EU law. ENISA is headquartered in Athens, with offices in Heraklion. The notification data remains within EU legal jurisdiction — it is not subject to US cloud jurisdiction (CLOUD Act) regardless of where you host your product.
For infrastructure teams concerned about where vulnerability data goes: an ENISA notification submitted from an EU-hosted SaaS (such as sota.io) stays within EU legal jurisdiction end-to-end. The same notification submitted from a US-hosted service is also sent to ENISA (an EU agency), but the cloud infrastructure processing the notification before submission may be subject to CLOUD Act disclosure requests.
Common Compliance Failures to Avoid
Failure 1: "We'll wait for the SRP to launch before building the process" The ENISA SRP is available and accepting test submissions. Building the notification workflow against a "future API" is a planning failure that will compress your implementation window.
Failure 2: "We don't need a CVD policy because we have a security team" Art.15 requires a published CVD policy. Having a security team that handles vulnerability reports internally does not satisfy the publication requirement.
Failure 3: "Our SBOM is in Excel" Excel SBOMs cannot be ingested by automated vulnerability scanners or KEV feed correlation tools. The ENISA reporting pipeline requires programmatic detection — human-readable-only SBOMs create a monitoring gap.
Failure 4: "We submitted the 24-hour early warning — we're done" The three-stage notification is mandatory. Submitting only the early warning and not the full 72-hour notification is partial compliance that NCAs treat as a reporting failure.
Failure 5: "NIS2 already covers this" CRA and NIS2 have different recipients (ENISA vs. national CSIRT) and different triggers (actively exploited product vulnerability vs. significant service incident). They can overlap but are legally separate obligations. NIS2 compliance does not satisfy CRA Art.14.
See Also
- EU Cyber Resilience Act: SBOM Requirements and Vulnerability Handling Developer Guide — Art.13 SBOM + Art.11 security-by-design (applies December 2027)
- DORA + CRA Dual Compliance: Fintech Manufacturer Guide — CRA × DORA overlap for financial sector software manufacturers
- NIS2 Art.20: Management Body Cybersecurity Obligations — Board approval and oversight duties for NIS2 + CRA governance
- NIS2 Art.21(2)(b): Incident Handling and Response — NIS2 Art.23 notification process (dual-reporting with CRA Art.14)