CRA Art.9: Due Diligence for Third-Party Components — SBOM, Open Source Integration, Supply Chain Obligations (Developer Guide 2026)
Post #463 in the sota.io EU Cyber Compliance Series
Modern software is assembled, not written from scratch. A typical web application bundles hundreds of npm packages. A firmware image includes third-party cryptographic libraries, RTOS components, and vendor SDKs. A mobile application links against platform frameworks and embedded analytics libraries. In every case, the final product carries not just the manufacturer's code but also the security posture — and vulnerabilities — of every component it integrates.
The EU Cyber Resilience Act (Regulation (EU) 2024/2847, "CRA") accounts for this reality in Article 9, which imposes a due diligence obligation on manufacturers when integrating third-party components — both commercial and open source — into products with digital elements.
Article 9 is not a checkbox. It is a process requirement: manufacturers must actively identify what they have integrated, monitor those components for vulnerabilities, and take action when problems appear. Failure to maintain this process is a CRA non-conformity, regardless of whether any specific component vulnerability has been exploited.
This guide covers:
- What Art.9 requires and who it applies to
- The SBOM obligation (software bill of materials)
- Open source components: the "reasonable care" standard
- Vulnerability monitoring for dependencies
- Integration with Art.10 (vulnerability handling) and Art.13 (manufacturer obligations)
- Practical implementation: tooling, process, documentation
What Article 9 Requires
CRA Art.9 states that manufacturers shall exercise due diligence when integrating components from third parties into their products with digital elements. This obligation operates at two levels:
1. Identification and documentation. Manufacturers must identify all third-party components integrated into their product and document them in a software bill of materials (SBOM). The SBOM must be machine-readable.
2. Vulnerability monitoring. When a third-party component contains a known vulnerability, the manufacturer cannot simply ship the product with that vulnerability present. Art.9(2) creates a direct link to Art.10 (vulnerability handling): if a manufacturer becomes aware that an integrated component has a vulnerability that affects the security of their product, they must treat it as a product vulnerability under Art.10.
3. Open source components. Art.9(3) addresses the specific situation of open source software. Manufacturers who integrate open source components must exercise reasonable care to verify that those components do not contain known vulnerabilities at the time of integration. For ongoing maintenance, manufacturers must monitor published vulnerability databases (CVEs, OSV, vendor advisories) for vulnerabilities in their open source dependencies.
The net effect: a manufacturer cannot outsource their vulnerability responsibility to their suppliers. If a third-party library has a known CVE, and the manufacturer ships that library in their product, the manufacturer is non-compliant under Art.9 — even if the library publisher has not yet issued a patch.
Who Art.9 Applies To
Art.9 applies to any manufacturer as defined under CRA Art.3(12): a natural or legal person who develops or manufactures products with digital elements, or who has products designed or manufactured and markets them under their own name or trademark.
This includes:
- Software companies shipping SaaS with downloadable clients, agents, or SDKs
- Hardware vendors shipping devices with firmware
- SDK/library publishers whose software is integrated by downstream manufacturers (though SDK publishers may also qualify as manufacturers in their own right)
- Open source project maintainers operating in a commercial context (subject to Art.16 carve-outs — see below)
Upstream suppliers (component manufacturers) who supply components to another manufacturer are not directly responsible for the integrating manufacturer's Art.9 compliance. However, component manufacturers face their own Art.9 obligations for the components they integrate. Supply chain compliance is recursive.
The SBOM Obligation
Art.9(1) requires manufacturers to document all third-party components in an SBOM in machine-readable format. This SBOM feeds directly into:
- Technical documentation required under Annex V (manufacturers must maintain a technical file including the SBOM)
- Vulnerability monitoring: the SBOM is the inventory against which CVEs and security advisories are checked
- Market surveillance: authorities may request the SBOM as part of conformity assessment review
- Authorised representatives: non-EU manufacturers' representatives (Art.12) must hold the technical documentation including the SBOM for 10 years
SBOM Format Requirements
CRA Art.9 requires machine-readable format but does not mandate a specific standard. The two dominant formats in practice are:
SPDX (Software Package Data Exchange) — ISO/IEC 5962:2021. Developed under Linux Foundation. Supported natively in many build tools. Recommended by ENISA for CRA compliance. Fields include: package name, version, license, download URL, checksum, and relationship descriptors.
CycloneDX — OWASP standard. JSON, XML, and Protobuf variants. Strong vulnerability management extensions (VEX support). Widely supported in the DevSecOps toolchain. ENISA also acknowledges CycloneDX as a conformant format.
For practical compliance, either format satisfies Art.9. Most organizations adopt the format their toolchain generates automatically (e.g., npm sbom generates SPDX; syft and cdxgen generate CycloneDX).
SBOM Depth Requirements
Art.9 does not specify whether the SBOM must be a shallow manifest (direct dependencies only) or a full transitive closure. ENISA's CRA implementation guidance suggests that a transitive SBOM — covering all dependencies including transitive ones — provides stronger conformity evidence, particularly for supply chain vulnerability management.
In practice, a shallow SBOM satisfies the literal Art.9 requirement; a transitive SBOM is a recommended best practice that reduces audit risk.
The Open Source Component Standard
Art.9(3) creates a specific standard for open source components: reasonable care at the time of integration.
"Reasonable care" is assessed against what a competent developer would do given the available information. In practice, this means:
At integration time:
- Query the NVD, OSV.dev, or GitHub Advisory Database for known CVEs on the component version being integrated
- Check the component's GitHub issues and changelog for security-relevant mentions
- Verify the component is actively maintained (last commit, release cadence, maintainer responsiveness)
- Check the component's own SBOM or dependency tree for nested vulnerabilities
During the product lifecycle:
- Monitor vulnerability feeds for new CVEs affecting integrated components
- Subscribe to security advisories from major component publishers
- Run automated dependency scanning as part of CI/CD (see tooling section below)
- Respond to new critical/high CVEs within the timeframe Art.10 establishes for vulnerability handling
The "reasonable care" standard for open source is generally interpreted as lower than the due diligence required for commercial components (where a supplier relationship exists and contractual SLAs can be negotiated). However, it is still a positive obligation — not simply an absence of negligence.
The Open Source Exception (Art.16)
Art.16 carves out obligations for open source software stewards — foundations, non-profit entities, and individuals publishing open source software without commercializing it. Open source stewards face lighter obligations (a "lightweight security policy" rather than full CRA compliance) and are explicitly exempted from certain Art.9 requirements as manufacturers.
This carve-out does not apply to manufacturers who integrate open source components from others. A commercial manufacturer integrating an MIT-licensed library remains fully bound by Art.9. The carve-out applies only to the publishers of that library if they meet the Art.16 steward definition.
Vulnerability Monitoring: Connecting Art.9 and Art.10
Art.9 creates the due diligence process; Art.10 governs what happens when a vulnerability is discovered. The two articles interact continuously:
- Art.9 requires maintaining an SBOM and monitoring components for vulnerabilities.
- When monitoring surfaces a CVE in an integrated component, Art.9(2) triggers Art.10(1): the manufacturer must "address" the vulnerability.
- Art.10 requires the manufacturer to develop and deploy a security update (where technically possible), within a timeframe commensurate with the risk — "without undue delay" for critical vulnerabilities.
- If the vulnerability is actively exploited, Art.11 requires notifying ENISA within 24 hours.
The critical design implication: manufacturers must have a process that connects SBOM-based component monitoring to their vulnerability handling workflow. A CVE alert that sits in a dependency scanning dashboard without triggering the Art.10 remediation process is a process failure with regulatory consequence.
Example Integration Flow
CI/CD pipeline:
→ build stage: generate SBOM (cyclonedx-npm / syft)
→ scan stage: check SBOM against OSV.dev / Grype / Snyk
→ if HIGH/CRITICAL CVE found: block build + open security ticket
→ security ticket → triage → patch or accept (with justification)
→ accepted vulnerabilities → VEX document (Art.10 evidence)
→ patched vulnerabilities → update SBOM + new release
This pipeline produces the artifacts needed for Art.9 compliance (SBOM, CVE check evidence) and Art.10 compliance (VEX documents, patch release records).
Practical Tooling
SBOM Generation
| Tool | Language/Ecosystem | Output Format | Notes |
|---|---|---|---|
npm sbom | Node.js | SPDX, CycloneDX | Built into npm ≥9.7 |
syft | Multi (Go, Python, Java, etc.) | SPDX, CycloneDX | Container and filesystem scanning |
cdxgen | Multi | CycloneDX | Strong Java, Python, Go support |
jake | Python | CycloneDX | Works with pip/poetry/pipenv |
cyclonedx-gomod | Go | CycloneDX | Go module support |
trivy sbom | Container images | SPDX, CycloneDX | Combined image SBOM + vulnerability scan |
Vulnerability Scanning Against SBOM
| Tool | Feeds | Notes |
|---|---|---|
| Grype | NVD, GitHub Advisory, OSV | Fast, works with SPDX/CycloneDX |
| OSV-Scanner | OSV.dev | Google-maintained, strong open source coverage |
| Snyk | Snyk DB (commercial), NVD | SaaS with IDE integration |
| OWASP Dependency-Check | NVD | Java-focused, widely used in enterprise |
| Trivy | NVD, GitHub Advisory, OSV, RHSA | Container + filesystem |
VEX Document Generation
VEX (Vulnerability Exploitability eXchange) documents are the Art.10 evidence record for known vulnerabilities that have been assessed and accepted. They document:
- The CVE identifier
- The product and component affected
- The assessment outcome (not affected / affected / fixed / under investigation)
- The justification (e.g., "component present but not used in code path", "mitigating control in place")
Tools: vexctl (CycloneDX VEX), parlay (SPDX VEX).
Documentation for the Technical File
Art.9 compliance requires maintaining documentation as part of the technical file (Annex V). At minimum, this includes:
- SBOM (machine-readable) — CycloneDX or SPDX, transitive preferred
- Vulnerability scan logs — timestamped records of dependency scans run against the current SBOM
- Triage records — for each HIGH/CRITICAL finding: accept/patch decision with justification
- VEX documents — for accepted known vulnerabilities
- Component selection rationale — for significant third-party components, a brief note on why this component was selected and what due diligence was performed at integration time
This documentation does not need to be elaborately formatted — it must be retrievable and accurate. A Git repository containing SBOM JSON files, scan output logs, and a structured markdown decision log satisfies the requirement.
Common Due Diligence Mistakes
1. Static SBOM, no ongoing monitoring. Many manufacturers generate an SBOM at release time but do not update it or re-scan between releases. Art.9 requires ongoing monitoring — a new CVE published against a component you integrated six months ago triggers Art.9(2) obligations today.
2. Transitive dependency blind spot. An application's direct dependencies may be clean while a transitive dependency carries a critical CVE. Shallow SBOMs miss this. Use tools that resolve the full dependency graph.
3. Container base image components omitted. The operating system layers and system libraries in a container image are third-party components for Art.9 purposes. Manufacturers shipping containerized products must include OS-level components in their SBOM and scan them accordingly.
4. Open source components treated as exempt. The Art.16 open source steward exemption does not apply to manufacturers who use open source. The integrating manufacturer remains responsible for due diligence on every open source component they ship.
5. VEX not maintained after initial assessment. A VEX document stating "not affected" for CVE-2024-XXXX is accurate at the time of writing but may become stale if the product changes or new exploit vectors are discovered. VEX documents require periodic review — especially for HIGH/CRITICAL findings.
Art.9 vs. Related Obligations
| Obligation | Article | Relationship to Art.9 |
|---|---|---|
| SBOM as part of technical documentation | Art.13(12), Annex V | Art.9 generates the SBOM; Art.13 requires it in the technical file |
| Vulnerability handling | Art.10 | Art.9 monitoring feeds Art.10 remediation process |
| Reporting actively exploited vulnerabilities | Art.11 | If a component CVE is actively exploited, Art.11 24h notification applies |
| EU Declaration of Conformity | Art.28 | Must cover Art.9 compliance as part of Annex I conformity |
| Importer obligations | Art.14 | Importers must verify that manufacturers have met Art.9 obligations before placing products on the market |
| Open source stewards | Art.16 | Stewards face lighter obligations; integrators of OSS remain bound by Art.9 |
Timeline and Deadlines
September 11, 2026 — CRA Art.9 vulnerability reporting obligations (Art.11) begin applying. Manufacturers must have their SBOM-to-vulnerability-monitoring pipeline operational.
December 11, 2027 — Full CRA compliance required. Art.9 due diligence and SBOM documentation must be in place for all products placed on the EU market.
For manufacturers starting today:
- Q2 2026: Implement SBOM generation in CI/CD for all products
- Q3 2026: Implement vulnerability scanning against SBOM; establish triage workflow; begin populating VEX documents
- Q4 2026: Audit technical documentation to verify Art.9 evidence package is complete
- Q1 2027: Internal conformity review; identify gaps; remediate
- Q3 2027: Final pre-deadline audit
Python: CRA Art.9 Due Diligence Checker
import json
from dataclasses import dataclass, field
from typing import Optional
from datetime import datetime, date
@dataclass
class ComponentRecord:
name: str
version: str
source: str # "commercial" | "open-source"
sbom_format: Optional[str] = None # "CycloneDX" | "SPDX" | None
last_scan_date: Optional[date] = None
known_cves: list[str] = field(default_factory=list)
vex_documented: bool = False
scan_tool: Optional[str] = None
@dataclass
class Art9ComplianceResult:
component: str
checks: dict[str, bool]
gaps: list[str]
compliant: bool
def check_art9_compliance(components: list[ComponentRecord]) -> list[Art9ComplianceResult]:
results = []
today = date.today()
for comp in components:
checks = {}
gaps = []
# Check 1: SBOM entry exists (Art.9(1))
checks["sbom_documented"] = comp.sbom_format is not None
if not checks["sbom_documented"]:
gaps.append(f"Component not documented in machine-readable SBOM (Art.9(1))")
# Check 2: Vulnerability scan performed (Art.9(1) + Art.9(3))
checks["scan_performed"] = comp.last_scan_date is not None
if not checks["scan_performed"]:
gaps.append("No vulnerability scan record (Art.9(3) reasonable care)")
# Check 3: Scan not stale (>90 days for ongoing monitoring)
if comp.last_scan_date:
days_since_scan = (today - comp.last_scan_date).days
checks["scan_current"] = days_since_scan <= 90
if not checks["scan_current"]:
gaps.append(f"Last scan {days_since_scan} days ago — ongoing monitoring required (Art.9(2))")
else:
checks["scan_current"] = False
# Check 4: Known CVEs have VEX documentation (Art.9(2) + Art.10)
if comp.known_cves:
checks["cves_addressed"] = comp.vex_documented
if not checks["cves_addressed"]:
gaps.append(
f"Known CVEs {comp.known_cves} not addressed — "
"VEX document or patch required (Art.9(2), Art.10)"
)
else:
checks["cves_addressed"] = True
compliant = all(checks.values())
results.append(Art9ComplianceResult(
component=f"{comp.name}@{comp.version}",
checks=checks,
gaps=gaps,
compliant=compliant
))
return results
# Example usage
components = [
ComponentRecord(
name="lodash",
version="4.17.21",
source="open-source",
sbom_format="CycloneDX",
last_scan_date=date(2026, 3, 15),
known_cves=["CVE-2021-23337"],
vex_documented=True, # Assessed: not exploitable in this code path
scan_tool="Grype"
),
ComponentRecord(
name="openssl",
version="3.0.7",
source="open-source",
sbom_format="SPDX",
last_scan_date=date(2025, 12, 1), # stale!
known_cves=[],
scan_tool="OSV-Scanner"
),
ComponentRecord(
name="vendor-sdk",
version="2.1.0",
source="commercial",
sbom_format=None, # missing SBOM entry
last_scan_date=None,
known_cves=["CVE-2025-11111"],
vex_documented=False
),
]
results = check_art9_compliance(components)
for r in results:
status = "COMPLIANT" if r.compliant else "NON-COMPLIANT"
print(f"\n{r.component}: {status}")
for gap in r.gaps:
print(f" ⚠ {gap}")
Checklist: CRA Art.9 Due Diligence
Use this checklist for each product with digital elements:
SBOM (Art.9(1))
- SBOM generated in machine-readable format (SPDX or CycloneDX)
- SBOM covers direct dependencies
- SBOM covers transitive dependencies (recommended)
- SBOM covers OS/container base image components (for containerized products)
- SBOM generated as part of CI/CD build process (not manually maintained)
- SBOM stored in technical documentation file (Annex V)
- SBOM updated on each release
Vulnerability Monitoring (Art.9(2), Art.9(3))
- Automated vulnerability scanning runs against SBOM on each build
- Scanning runs on a recurring schedule (at minimum monthly) independent of releases
- Scanning covers NVD + OSV.dev + GitHub Advisory + relevant vendor advisories
- HIGH/CRITICAL findings trigger triage within 72 hours
- Triage outcomes documented (patch plan or VEX acceptance)
Open Source Components (Art.9(3))
- At integration time: checked for known CVEs before merging
- Maintenance status verified (active maintainer, recent releases)
- Nested dependency vulnerabilities checked
- For unmaintained components: alternative assessed or fork plan documented
VEX Documentation (Art.10 connection)
- VEX documents generated for accepted known vulnerabilities
- VEX reviewed when product changes (new code paths, configuration)
- VEX reviewed when new exploit vectors published for documented CVEs
Process Documentation
- SBOM generation process documented in security policy
- Vulnerability monitoring cadence documented
- Triage criteria documented (CVSS threshold for escalation)
- Incident response process references Art.11 reporting for exploited CVEs
Conclusion
CRA Art.9 transforms third-party component integration from a development convenience into a documented compliance process. The core obligations — maintain a machine-readable SBOM, monitor integrated components for vulnerabilities, and treat component CVEs as product vulnerabilities — are achievable with modern DevSecOps tooling.
The challenge is operationalizing these checks at scale, especially for products with hundreds of transitive dependencies, and keeping the process running continuously rather than only at release time.
Manufacturers who build SBOM generation and vulnerability scanning into their CI/CD pipeline now, well ahead of the September 2026 and December 2027 deadlines, will enter the compliance period with evidence packages already assembled — rather than scrambling to reconstruct dependency histories retroactively.
This post is part of the sota.io EU Cyber Compliance Series. For related articles, see CRA Art.10: Vulnerability Handling Obligations, CRA Art.11: Reporting Obligations, CRA Art.12: Authorised Representatives, CRA Art.13: Manufacturer Obligations, and CRA Art.8: Open Source Software Stewards.