2026-04-16·17 min read·

CRA Art.13: Manufacturer Obligations — Security-by-Design, SBOM, and 10-Year Update Support (Developer Guide 2026)

Post #400 in the sota.io EU Cyber Compliance Series

The EU Cyber Resilience Act (Regulation (EU) 2024/2847, "CRA") entered into force on 10 December 2024. For most manufacturers, the critical compliance deadline is 11 December 2027 — 36 months after entry into force — when Article 13's full manufacturer obligations begin to apply.

Article 13 is the operational core of the CRA. It translates the Annex I essential requirements into concrete legal obligations binding on every manufacturer who places a product with digital elements on the EU market. Understanding Art.13 is the prerequisite for building a compliant development lifecycle.

Two related deadlines arrived earlier:

This guide focuses exclusively on the December 2027 obligations.

What Is a "Product with Digital Elements"?

Art.3(1) defines a product with digital elements as any software or hardware product and its remote data processing solutions, including software or hardware components placed on the market separately.

In scope:

Out of scope (Art.2(2)):

The SaaS question is frequently misunderstood. If your cloud service involves remote data processing that is necessary for the product to function — and you market that as the product — you are a manufacturer under the CRA. The Art.2(3) carve-out for "online services" is narrower than it appears; it covers incidental online connectivity, not products whose primary value is the software service itself.

The Art.13 Obligation Matrix

Art.13 imposes obligations across four phases of the product lifecycle:

Phase 1: Design and Development (Art.13(1)–(2))

Manufacturers must ensure that products are designed and developed in accordance with the essential requirements in Annex I, Part I. The key Art.13(1) obligations are:

1. Security-by-Design (Annex I, §1)

Products must be designed, developed, and produced to ensure an appropriate level of cybersecurity based on the risks. This means:

There is no single prescribed methodology, but ETSI EN 303 645 (IoT), IEC 62443 (industrial), and OWASP SAMM are referenced in the ENISA CRA implementation guidance as appropriate frameworks.

2. No Known Exploitable Vulnerabilities at Release (Annex I, §2)

This is the most operationally demanding requirement: at the time of placing on the market, the product must be free of known exploitable vulnerabilities. This means:

"Known" means known to you or objectively discoverable via public vulnerability databases. You cannot ship with a CVE in your SBOM that has a CVSS score ≥7.0 and claim you were unaware.

3. Default Secure Configuration (Annex I, §3)

Products must ship with secure default settings. Concrete requirements:

4. Protection of Data in Transit and at Rest (Annex I, §4–5)

5. Access Control and Privilege Separation (Annex I, §6)

Phase 2: Placing on the Market (Art.13(3)–(7))

SBOM Generation (Art.13(3))

When placing a product on the market, manufacturers must identify and document components contained in the product. This is operationalised as a Software Bill of Materials (SBOM) in machine-readable format.

The CRA does not mandate a specific SBOM format, but the ENISA technical guidance and CEN/CENELEC standardisation work under Art.25 point to SPDX (ISO/IEC 5962:2021) and CycloneDX as the reference formats. Both are acceptable; CycloneDX v1.5+ has stronger support for vulnerability data correlation via the VEX (Vulnerability Exploitability eXchange) component.

Your SBOM must include at minimum:

For the SBOM generation pipeline, see our detailed guide: EU CRA SBOM and Vulnerability Handling

No Known Exploitable Vulnerabilities — Confirmation (Art.13(5))

Before placing on the market, manufacturers must confirm that vulnerabilities in the product have been addressed. This confirmation is part of the technical documentation required for conformity assessment.

Security Update Delivery Mechanism (Art.13(7))

Manufacturers must ensure that security updates can be deployed. Requirements:

Phase 3: Security Update Support Period (Art.13(8)–(9))

Minimum Support Duration

This is the provision that most significantly changes the commercial model for software products. Art.13(8) requires manufacturers to provide security updates for a period that is:

"adequate to ensure compliance with the essential requirements set out in Annex I, Part I, and at least five years, taking into account the expected lifetime of the product"

In practice:

For software products with traditional annual release cycles, "5 years" is achievable. For firmware shipped in hardware products — where the hardware has a 10–15 year service life — you must plan security update delivery for the full device lifecycle.

Support Period Transparency (Art.13(9))

Manufacturers must inform users about the support period:

Phase 4: Post-Market Obligations (Art.13(10)–(14))

Vulnerability Management (Art.13(10))

Manufacturers must establish a vulnerability management policy covering:

Security Update Characteristics (Art.13(11))

Security updates must be:

End of Life (Art.13(14))

At end of support, manufacturers must:

Conformity Assessment: Class A vs Class B (Annex II)

Not all products undergo the same conformity assessment process. The CRA creates two categories:

Class A Products — Self-Assessment (Module A)

The majority of products are Class A. These undergo manufacturer self-assessment (Annex VIII, Module A):

Class A products include: general-purpose software, productivity applications, standard IoT consumer products, most web applications, standard operating system components.

Class B Products — Third-Party Assessment (Modules B+C or H)

Higher-risk products are classified as Class B in Annex II and require third-party conformity assessment by a Notified Body. The Class B list includes:

If your product falls into Class B, you need to budget for Notified Body fees (typically €15,000–€80,000 depending on scope and body) and allow 3–6 months for the assessment process.

Class B assessment paths:

The Critical Classification Question

Before December 2027, every manufacturer must classify their products. The question is not just "is my product software" but specifically:

  1. Is it a product with digital elements (Art.3(1))? → Yes = CRA applies
  2. Does it appear in Annex II (Class A or Class B list)? → Class B = third-party assessment
  3. Is it a critical product under the Network and Information Security delegated acts (Art.6(3)/(4))? → May require stricter Class B assessment
  4. Is it covered by a sector-specific regulation (MDR, IVR, aviation, automotive)? → Check alignment provisions

Technical Documentation (Annex VII)

For conformity assessment, manufacturers must compile technical documentation that includes:

  1. Product description: Architecture, design principles, purpose, intended user
  2. Risk assessment: Threat model, attack scenarios, risk treatment decisions
  3. Essential requirements checklist: How each Annex I requirement is met
  4. SBOM: Machine-readable, including all components and their known vulnerabilities
  5. Secure development lifecycle documentation: Processes, tools, testing evidence
  6. Vulnerability management policy: CVD process, escalation paths, remediation timelines
  7. Security update delivery mechanism: Technical description of the update channel
  8. Test reports: Penetration testing, static analysis, fuzzing results (where applicable)
  9. EU Declaration of Conformity (Art.13(16) + Annex IV)
  10. Support period commitment: Explicit statement of security update duration

This documentation must be retained for 10 years after the product is placed on the market or the last unit is supplied.

Python CRAManufacturerChecker

The following implementation provides a framework for automated CRA Art.13 pre-release checks:

from dataclasses import dataclass, field
from enum import Enum
from datetime import date, timedelta
import json
from typing import Optional


class ProductClass(Enum):
    CLASS_A = "class_a"  # self-assessment
    CLASS_B = "class_b"  # third-party assessment required


class ComplianceStatus(Enum):
    COMPLIANT = "compliant"
    NON_COMPLIANT = "non_compliant"
    NEEDS_REVIEW = "needs_review"


@dataclass
class SBOMComponent:
    name: str
    version: str
    purl: str
    known_cves: list[str] = field(default_factory=list)
    max_cvss: float = 0.0
    license: str = ""


@dataclass
class CRAProduct:
    name: str
    version: str
    release_date: date
    product_class: ProductClass
    expected_lifetime_years: int
    components: list[SBOMComponent] = field(default_factory=list)
    has_default_password_policy: bool = False
    has_secure_defaults: bool = False
    has_update_mechanism: bool = False
    has_cvd_policy: bool = False
    has_access_controls: bool = False
    has_data_protection: bool = False
    technical_docs_complete: bool = False


class CRAManufacturerChecker:
    """Validates CRA Art.13 manufacturer obligations before market placement."""

    CRITICAL_CVSS_THRESHOLD = 7.0
    MIN_SUPPORT_YEARS = 5

    def __init__(self, product: CRAProduct):
        self.product = product
        self.findings: list[dict] = []

    def check_no_known_vulnerabilities(self) -> ComplianceStatus:
        """Annex I §2: No known exploitable vulnerabilities at release."""
        critical_components = [
            c for c in self.product.components
            if c.max_cvss >= self.CRITICAL_CVSS_THRESHOLD
        ]
        if critical_components:
            self.findings.append({
                "check": "no_known_vulnerabilities",
                "status": "non_compliant",
                "details": f"{len(critical_components)} components with CVSS ≥ {self.CRITICAL_CVSS_THRESHOLD}",
                "components": [c.name for c in critical_components],
                "action": "Patch or replace components before release"
            })
            return ComplianceStatus.NON_COMPLIANT

        unresolved_cve_components = [
            c for c in self.product.components if c.known_cves
        ]
        if unresolved_cve_components:
            self.findings.append({
                "check": "no_known_vulnerabilities",
                "status": "needs_review",
                "details": f"{len(unresolved_cve_components)} components with CVEs below critical threshold",
                "action": "Review and document risk acceptance for non-critical CVEs"
            })
            return ComplianceStatus.NEEDS_REVIEW

        self.findings.append({
            "check": "no_known_vulnerabilities",
            "status": "compliant",
            "details": "No known exploitable vulnerabilities in SBOM"
        })
        return ComplianceStatus.COMPLIANT

    def check_secure_defaults(self) -> ComplianceStatus:
        """Annex I §3: Default secure configuration."""
        checks = {
            "default_password_policy": self.product.has_default_password_policy,
            "secure_defaults": self.product.has_secure_defaults,
        }
        failed = [k for k, v in checks.items() if not v]
        if failed:
            self.findings.append({
                "check": "secure_defaults",
                "status": "non_compliant",
                "missing": failed,
                "action": "Implement secure default configuration before release"
            })
            return ComplianceStatus.NON_COMPLIANT
        return ComplianceStatus.COMPLIANT

    def check_update_mechanism(self) -> ComplianceStatus:
        """Art.13(7): Security update delivery mechanism."""
        if not self.product.has_update_mechanism:
            self.findings.append({
                "check": "update_mechanism",
                "status": "non_compliant",
                "action": "Implement mechanism to receive and apply security updates"
            })
            return ComplianceStatus.NON_COMPLIANT
        return ComplianceStatus.COMPLIANT

    def check_support_period(self) -> ComplianceStatus:
        """Art.13(8): Minimum security update support period."""
        required_years = max(
            self.MIN_SUPPORT_YEARS,
            self.product.expected_lifetime_years
        )
        eol_date = self.product.release_date + timedelta(days=required_years * 365)
        self.findings.append({
            "check": "support_period",
            "status": "compliant",
            "required_years": required_years,
            "eol_date": eol_date.isoformat(),
            "action": f"Document EOL commitment: security updates until {eol_date}"
        })
        return ComplianceStatus.COMPLIANT

    def check_cvd_policy(self) -> ComplianceStatus:
        """Art.15: CVD policy (already required from September 2026)."""
        if not self.product.has_cvd_policy:
            self.findings.append({
                "check": "cvd_policy",
                "status": "non_compliant",
                "action": "Implement CVD policy and security.txt (RFC 9116) before Sept 2026"
            })
            return ComplianceStatus.NON_COMPLIANT
        return ComplianceStatus.COMPLIANT

    def check_technical_documentation(self) -> ComplianceStatus:
        """Annex VII: Technical documentation completeness."""
        if not self.product.technical_docs_complete:
            self.findings.append({
                "check": "technical_documentation",
                "status": "needs_review",
                "action": "Complete Annex VII technical documentation for conformity assessment"
            })
            return ComplianceStatus.NEEDS_REVIEW
        return ComplianceStatus.COMPLIANT

    def check_conformity_path(self) -> dict:
        """Determine required conformity assessment path."""
        if self.product.product_class == ProductClass.CLASS_B:
            return {
                "class": "B",
                "assessment": "Third-party Notified Body required",
                "modules": "Module B+C (type examination) or Module H (full QA)",
                "estimated_cost": "€15,000–€80,000",
                "estimated_duration": "3–6 months",
                "action": "Engage Notified Body NOW — assessment takes months"
            }
        return {
            "class": "A",
            "assessment": "Self-assessment (Module A)",
            "modules": "Annex VIII",
            "action": "Prepare Annex VII technical documentation and EU Declaration of Conformity"
        }

    def run_all_checks(self) -> dict:
        """Run complete Art.13 compliance assessment."""
        results = {
            "product": self.product.name,
            "version": self.product.version,
            "assessment_date": date.today().isoformat(),
            "checks": {
                "no_known_vulnerabilities": self.check_no_known_vulnerabilities().value,
                "secure_defaults": self.check_secure_defaults().value,
                "update_mechanism": self.check_update_mechanism().value,
                "support_period": self.check_support_period().value,
                "cvd_policy": self.check_cvd_policy().value,
                "technical_documentation": self.check_technical_documentation().value,
            },
            "conformity_path": self.check_conformity_path(),
            "findings": self.findings,
        }

        non_compliant = sum(
            1 for v in results["checks"].values() if v == "non_compliant"
        )
        needs_review = sum(
            1 for v in results["checks"].values() if v == "needs_review"
        )

        if non_compliant > 0:
            results["overall_status"] = "BLOCKED — not ready for market placement"
        elif needs_review > 0:
            results["overall_status"] = "CONDITIONAL — review required before placement"
        else:
            results["overall_status"] = "READY — proceed to conformity assessment"

        return results


# Example usage
if __name__ == "__main__":
    product = CRAProduct(
        name="MySecureAPI",
        version="2.1.0",
        release_date=date(2027, 10, 1),
        product_class=ProductClass.CLASS_A,
        expected_lifetime_years=5,
        components=[
            SBOMComponent(
                name="express",
                version="4.18.2",
                purl="pkg:npm/express@4.18.2",
                known_cves=[],
                max_cvss=0.0,
                license="MIT"
            ),
        ],
        has_default_password_policy=True,
        has_secure_defaults=True,
        has_update_mechanism=True,
        has_cvd_policy=True,
        has_access_controls=True,
        has_data_protection=True,
        technical_docs_complete=False,
    )

    checker = CRAManufacturerChecker(product)
    results = checker.run_all_checks()
    print(json.dumps(results, indent=2))

The Art.13 × SBOM Connection

Art.13(3) mandates SBOM generation, but the SBOM obligation is the tip of the iceberg. The SBOM is what makes the "no known vulnerabilities" requirement (Annex I §2) verifiable — for both you and market surveillance authorities.

The operational pipeline is:

Code Repository → SBOM Generation (SPDX/CycloneDX) → CVE Correlation
       ↓                                                      ↓
  Release Gate ← CVSS ≥ 7.0? BLOCK ←─── Vulnerability Status Check
       ↓
  Release Approved → SBOM attached to technical documentation
       ↓
  Post-release: Monitor CVE feeds → New CVE in shipped SBOM → Art.13(10) response

Tools for the pipeline:

For detailed SBOM implementation guidance, see: EU CRA SBOM and Vulnerability Handling Developer Guide

The December 2027 Readiness Timeline

Working backwards from 11 December 2027:

MilestoneDeadlineAction
Product classification completeNowClass A or B? Involves Notified Body?
CVD policy + security.txt live11 Sept 2026Art.15 already applies — if not done, you are already non-compliant
ENISA SRP registration11 Sept 2026Art.14 early warning notification system
Secure SDL process documentedQ1 2027Threat modelling, SAST/DAST, dependency scanning in CI
SBOM pipeline operationalQ2 2027SPDX/CycloneDX generation in every release
Notified Body engaged (Class B only)Q2 20273–6 months lead time — engage early
Technical documentation draft (Annex VII)Q3 2027Internal review and legal sign-off
Conformity assessment completeOct 2027Buffer for remediation
EU Declaration of Conformity signedNov 2027
CE marking affixedNov 2027
CRA Art.13 compliance11 Dec 2027

30-Item CRA Art.13 Compliance Checklist

Design and Development:

Market Placement:

Vulnerability Management:

Post-Market:

CRA Art.13 × NIS2 Art.21 Overlap

If your organisation also falls under NIS2 (essential or important entity), there is significant overlap between CRA Art.13 manufacturer obligations and NIS2 Art.21 security measures:

ObligationCRA Art.13NIS2 Art.21
Vulnerability managementAnnex I §8 + Art.13(10)Art.21(2)(e)
Supply chain securityAnnex I §3 (component security)Art.21(2)(d)
Access controlAnnex I §6Art.21(2)(i)
Incident handlingArt.14 notificationsArt.21(2)(b) + Art.23
Security update managementArt.13(7)–(11)Art.21(2)(e)
Business continuityAnnex I §9Art.21(2)(c)

For dual-regulation entities, a single compliant SDL process with documented evidence satisfies both regimes. The key is documenting which evidence satisfies which requirement — NCA auditors for NIS2 and market surveillance authorities for CRA may review the same technical documentation from different angles.

See Also