The CRA for ESP-IDF teams: real CPEs out of the box, and the parts they do not cover
A plain-English guide for teams shipping ESP-IDF firmware into the EU: what esp-idf-sbom already gives you, why the closed radio libraries and managed components still need attention, and what to do next.
By Declarable · Checked on 11 October 2026 · Guide version 1.3.0
If you ship a product built on Espressif's ESP-IDF and sell it in the EU, the Cyber Resilience Act (CRA, Regulation (EU) 2024/2847) applies to its firmware. ESP-IDF teams start further ahead than most: Espressif's SBOM tool already writes the identifiers that vulnerability databases match on. This guide covers what that tool gives you, what still needs your attention, and a short list of what to do. It is a documentation aid, not legal advice.
The dates and the duties, briefly
Article 14 reporting has applied since 11 September 2026, to products already on the market as well (Article 69(3)); most of the rest of the Regulation applies from 11 December 2027 (Article 71(2), EUR-Lex text checked 8 October 2026; the rules on notified bodies in Chapter IV have applied since 11 June 2026). The essential requirements are in Annex I; for firmware, the heaviest in our reading (argument) are the SBOM and vulnerability identification in Annex I, Part II(1), shipping without known exploitable vulnerabilities (Annex I, Part I(2)(a)), fixing without delay (Annex I, Part II(2)) and secure update distribution (Annex I, Part II(7)). Our guide "The CRA for Zephyr developers" walks through those requirements in more depth; this one stays on ESP-IDF.
What esp-idf-sbom gives you
Espressif's esp-idf-sbom tool creates SBOM files "in the Software Package Data Exchange (SPDX) and CycloneDX formats for applications generated by the Espressif IoT Development Framework". From a project you run idf.py sbom-create; standalone, esp-idf-sbom create reads the project_description.json the build writes into your build directory. Four output formats are listed: SPDX 2.2 tag-value (the default), SPDX 2.2 JSON, SPDX 3.0.1 JSON-LD and CycloneDX 1.6 JSON.
Its input is a set of manifest files. A component can describe itself in an sbom.yml file, in an sbom section of its idf_component.yml, or through sbom- variables in ESP-IDF's .gitmodules. The fields include version, supplier, cpe, purl, cve-exclude-list and virtpackages (for code that is not compiled, such as a library held in the chip's ROM).
The identifiers are already there. This is the part that sets ESP-IDF apart. ESP-IDF ships manifests with CPE names (the identifiers the US National Vulnerability Database, NVD, uses) for its main third-party code:
- FreeRTOS:
cpe:2.3:o:amazon:freertos:{}:..., with version 10.5.1 on the master branch; - lwIP:
cpe:2.3:a:lwip_project:lwip:{}:...; - Mbed TLS: two CPEs, one with the vendor
armand one withtrustedfirmware; - micro-ecc and protobuf-c, through
sbom-cpelines in.gitmodules.
The {} is the place for the component's version. Some manifests also carry decisions: the FreeRTOS one excludes CVE-2024-28115 with the reason "Affects only ARMv7-M MPU ports...".
A vulnerability check is built in. idf.py sbom-check looks up each component's CPE in the NVD. Through idf.py, the default is a local mirror of the NVD (about 900 MB of disk); --nvd-api queries the NVD online instead. The standalone esp-idf-sbom check defaults to the online API and takes --local-db for the mirror. Know which one your CI runs.
By comparison (argument, from our other platform guides): a Zephyr build has CPEs only for modules that declare them, and a Yocto build guesses them from recipe names unless each recipe sets CVE_PRODUCT. Real, curated CPEs out of the box are the exception among the three.
What still needs attention
The closed radio libraries
The Wi-Fi MAC, the PHY, the Bluetooth controller and the coexistence logic ship as precompiled libraries. ESP-IDF's .gitmodules pulls them from separate repositories: esp32-wifi-lib into components/esp_wifi/lib, esp-phy-lib into components/esp_phy/lib, esp-coex-lib into components/esp_coex/lib, and a Bluetooth library per chip family (such as esp32-bt-lib). Espressif describes the Wi-Fi repository as "binary libraries supporting the ESP32 series RF subsystems".
None of those submodule entries carries sbom- information, and the esp_wifi component has no sbom.yml. So, as far as we can see (argument: an inference from the manifests, not documented behaviour), these libraries appear in the SBOM inside their parent component with a guessed version and no CPE, and the NVD check cannot cover them.
Your source for them is Espressif itself. Its advisories page lists security advisories for chips and ESP-IDF, for example "AR2026-006 ... Security Advisory Concerning ECDSA Secure Boot Issue" in July 2026; the page says it is moving to Espressif's new documentation site. Watch it, and record which ESP-IDF release, and therefore which library versions, each firmware image was built from.
Managed components and their own cadence
Since ESP-IDF 5.0 several components, including mdns, esp_modem, libsodium, tinyusb, coap and expat, moved out of ESP-IDF into the ESP Component Registry. They arrive through the IDF Component Manager, declared in each component's idf_component.yml, resolved into a dependencies.lock file that records "the exact versions of the components selected" and a hash of each, and downloaded into managed_components/.
Each managed component has its own version number, separate from ESP-IDF's. That has two consequences:
- Updating ESP-IDF does not update them, and updating them does not need an ESP-IDF update. A fix for
coapreaches you only when you change the lock file (argument: follows from the separate versions). - Their SBOM entries depend on their manifests. esp-idf-sbom takes the version and URL from
idf_component.ymland derives a package URL from the commit; a CPE appears only if the component's own manifest declares one.
Espressif recommends committing dependencies.lock to version control when a project uses only Registry or Git dependencies. For the CRA, treat it as part of the release record.
Your ESP-IDF release's support window
Espressif supports each major and minor ESP-IDF release for 30 months from its first stable release: a 12-month service period, then an 18-month maintenance period in which "only bugfixes for high severity issues or security issues will be applied". After that it is end of life; Espressif published an end-of-life advisory for v5.2 in September 2026. Article 13(8) requires a support period of at least five years (shorter only where the product is expected to be in use for less), which will outlast any ESP-IDF release, so you will move releases during the product's life, or carry the fixes yourself. See "Choosing and changing your support period".
Components not in the binary
By default the SBOM includes configuration-only components and components not linked into the application, but does not link them as dependencies of the project, and esp-idf-sbom check only examines packages reachable from the project, so by default those components are not checked. If you scan component manifests directly (idf.py sbom-check without --file), keep that in mind: a match on a component that is not in the binary is a finding to close with a reason, not a fix to ship.
Where Declarable helps
Two of the gaps above are ones our import does something about. Because esp-idf-sbom has already done most of the identification work, the first rule in our ESP-IDF import is that an identifier you or a component's author declared (a CPE, or a package URL written in a manifest) is kept, and the import only fills in what was missing. One package URL is not treated that way: the one esp-idf-sbom derives from the ESP-IDF commit for a component inside the ESP-IDF tree says where the source sits, not what the code is. We set it aside, keep it on the component as a property, and let our row for that component apply. The same goes for a version that only repeats ESP-IDF's own: it is blanked and the component is flagged as having no version of its own.
A managed component gets an identity of its own. Declarable keeps a row for each Component Registry package we hold a row for, keyed on the namespaced name the Registry uses — chmorgan/esp-libhelix-mp3, a maintainer's GitHub handle and the component name — and records a package URL for it. Where no NVD entry exists for that code, the row's CPE field is empty rather than a made-up name, and the row carries a plain sentence saying what the component is and why there is no CPE; you can read that sentence on the component's public page. When you upload the build's dependencies.lock alongside the SBOM, the names it resolved are matched against those rows, which is one way a managed component that ships no sbom.yml of its own is identified (the lock file is the companion file to send; the upload does not recognise idf_component.yml as one). A component whose SBOM entry has no version takes the version the lock file resolved, whatever its source, and whether or not we hold a row for it; the component records that the version came from the lock file. For a package that came from the Registry, the fact that it came from the Registry is kept too. A package we hold no row for gets no identifier, because none is guessed from a name; it does get the lock file's version.
A closed radio library is flagged, not left out. The esp_wifi, esp_phy, esp_coex and Bluetooth components are matched on the component name esp-idf-sbom itself writes, and marked in the inventory as a precompiled vendor library, with Espressif as the supplier. They stay in the inventory, with no invented CPE, so the record says plainly that there is code in your image nobody outside Espressif can read — an answer you can give, rather than a silent gap. It does not give you their vulnerability history: Espressif's advisories, and the ESP-IDF release each image was built from, are still the only source for that.
What to do now
- Run
esp-idf-sbom create --output-file sbom.spdx build/project_description.jsonin CI for every release (the same tool asidf.py sbom-create; our CI page for ESP-IDF uses this form) and keepsbom.spdxwith the release, together withdependencies.lock. - Pick your output format. If a customer follows BSI TR-03183-2, use the SPDX 3.0.1 or CycloneDX 1.6 output rather than the 2.2 default.
- Run
idf.py sbom-checkon every release and on a schedule, and record a decision for each match: affected, not affected with the reason, or fixed. Thecve-exclude-listfield is one place to keep that reasoning in your own components. - Add
sbom.ymlwith a CPE or purl to your own components and to any vendored library. - List the radio libraries by hand with the ESP-IDF release they came from, and put Espressif's advisories on your watch list.
- Use the platform's update and boot protections. ESP-IDF provides Secure Boot v2, which verifies the app "on every boot but also on each over the air (OTA) update", flash encryption, and
esp_https_otafor updates over HTTPS; signed updates can be checked even without hardware secure boot. Our reading (argument): these support Annex I, Part I(2)(c) and Annex I, Part II(7). - Plan ESP-IDF upgrades against your support period, and be ready for Article 14 now.
What is settled and what is not
Settled (evidence): the CRA dates and requirements cited; what esp-idf-sbom produces and checks, per its README and the ESP-IDF documentation; ESP-IDF's support policy.
Not settled (as of 8 October 2026): no harmonised standard for the CRA is cited in the Official Journal, and no EU implementing act fixes an SBOM format. How esp-idf-sbom represents the closed radio libraries is our reading of its manifests, not something Espressif documents. We update this guide when any of these change.
Sources
- Regulation (EU) 2024/2847 (Cyber Resilience Act), Official Journal text on EUR-Lex: https://eur-lex.europa.eu/eli/reg/2024/2847/oj/eng · checked
- esp-idf-sbom README (Espressif): https://github.com/espressif/esp-idf-sbom · checked
- ESP-IDF documentation, SBOM generation and vulnerability check (idf.py sbom): https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/tools/idf-sbom.html · checked
- ESP-IDF FreeRTOS SBOM manifest (sbom.yml): https://raw.githubusercontent.com/espressif/esp-idf/master/components/freertos/FreeRTOS-Kernel/sbom.yml · checked
- Espressif lwIP fork, SBOM manifest at the commit ESP-IDF master pins: https://raw.githubusercontent.com/espressif/esp-lwip/fc73c67e5c37c27a319725a0aee974c087dbf1a6/sbom.yml · checked
- Espressif Mbed TLS fork, SBOM manifest at the commit ESP-IDF master pins: https://raw.githubusercontent.com/espressif/mbedtls/f5c91881be98b4da0bf57c217bf45b668fb84f9f/sbom.yml · checked
- ESP-IDF .gitmodules (submodules, sbom- variables, closed radio libraries): https://raw.githubusercontent.com/espressif/esp-idf/master/.gitmodules · checked
- esp32-wifi-lib repository (Espressif): https://github.com/espressif/esp32-wifi-lib · checked
- IDF Component Manager documentation: https://docs.espressif.com/projects/idf-component-manager/en/latest/ · checked
- IDF Component Manager, the dependencies.lock file: https://docs.espressif.com/projects/idf-component-manager/en/latest/reference/dependencies_lock.html · checked
- IDF Component Manager, component versioning: https://docs.espressif.com/projects/idf-component-manager/en/latest/reference/versioning.html · checked
- ESP-IDF 5.0 migration guide, components moved to the ESP Component Registry: https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32/migration-guides/release-5.x/5.0/removed-components.html · checked
- Espressif advisories (security and end-of-life): https://www.espressif.com/en/support/documents/advisories · checked
- ESP-IDF documentation, versions and support periods: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/versions.html · checked
- ESP-IDF documentation, Secure Boot v2: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/security/secure-boot-v2.html · checked
- ESP-IDF documentation, flash encryption: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/security/flash-encryption.html · checked
- ESP-IDF documentation, over the air updates (signed updates without hardware secure boot): https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/ota.html · checked
- ESP-IDF documentation, esp_https_ota: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/esp_https_ota.html · checked
- BSI TR-03183, Cyber Resilience Requirements (Part 2: SBOM, version 2.1.0): https://www.bsi.bund.de/dok/TR-03183 · checked
- ESP-IDF documentation, IDF Component Manager (managed_components directory): https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/tools/idf-component-manager.html · checked
Building on this platform? Places to go next:
- The ESP-IDF page
- SBOM check: Fix a Zephyr, Yocto or ESP-IDF SBOM and see its known vulnerabilities
Declarable prepares documentation for you to review. It is not legal advice and not certification. You sign your own declaration of conformity.