CRA SBOM Requirements: What the Law Asks For and What It Doesn’t
Last reviewed: 26 October 2026. General information, not legal advice.
If your app or connected product falls under the EU’s Cyber Resilience Act (CRA), you will need a software bill of materials (SBOM). That is a machine-readable list of the components inside it. It must be in “a commonly used and machine-readable format covering at the very least the top-level dependencies” (Annex I, Part II, point 1). That applies to products placed on the market from 11 December 2027, and to earlier ones substantially modified from then (Articles 69(2) and 71(2)).
The CRA names no format and doesn’t make you publish the SBOM. For a field-by-field list, Germany’s BSI publishes a voluntary guideline, TR-03183-2, which we map below to CycloneDX and SPDX with its exceptions. We also tested what SBOMs generated from mobile lockfiles carry. On 25 October 2026, SBOMs we generated from 11 open-source iOS and Android app repositories named no licence or creator for any of 1,236 packages.
| Question | What the text says | Where |
|---|---|---|
| Is an SBOM mandatory? | Yes, for a product with digital elements in the CRA’s scope, as part of the vulnerability handling requirements | Article 2; Annex I, Part II, point 1 |
| Which format? | “a commonly used and machine-readable format”; none is named | Annex I, Part II, point 1 |
| How deep? | “at the very least the top-level dependencies” | Annex I, Part II, point 1 |
| Must it be public? | No. Annex II only asks where users can find it if the manufacturer decides to make it available | Annex II, point 9; recital 77 |
| Who can ask for it? | Where applicable, a market surveillance authority, on a reasoned request where it needs the SBOM to check compliance with Annex I; and authorities, for a Union-wide dependency assessment that ADCO (the administrative cooperation group the CRA sets up) decides to run | Annex VII, point 8; Article 13(25) |
| From when? | 11 December 2027, for products placed on the market from then, and earlier ones substantially modified from then | Articles 69(2) and 71(2) |
| Could the rules get more specific? | Yes: the Commission “may” specify format and elements by implementing act | Article 13(24) |
What does the CRA actually say about SBOMs?
All quotations are from Regulation (EU) 2024/2847.
- Who it covers. Products with digital elements whose “intended purpose or reasonably foreseeable use” includes “a direct or indirect logical or physical data connection to a device or network” (Article 2(1)), minus the exclusions in the rest of Article 2, such as products covered by Regulation (EU) 2017/745 on medical devices (Article 2(2)).
- The definition. An SBOM is “a formal record containing details and supply chain relationships of components included in the software elements of a product with digital elements” (Article 3(39)).
- The obligation. Manufacturers shall “identify and document vulnerabilities and components contained in products with digital elements, including by drawing up a software bill of materials in a commonly used and machine-readable format covering at the very least the top-level dependencies of the products” (Annex I, Part II, point 1).
- Where it lives. The technical documentation contains, “as applicable to the relevant product with digital elements” (Annex VII, opening words), information on the vulnerability handling processes, “including the software bill of materials, the coordinated vulnerability disclosure policy, evidence of the provision of a contact address for the reporting of the vulnerabilities” (Annex VII, point 2(b)). It is “drawn up before the product with digital elements is placed on the market” and “continuously updated, where appropriate, at least during the support period” (Article 31(2)). You keep it at the disposal of market surveillance authorities “for at least 10 years after the product with digital elements has been placed on the market or for the support period, whichever is longer” (Article 13(13)).
- Who sees it. Annex VII, point 8 lists “where applicable, the software bill of materials, further to a reasoned request from a market surveillance authority provided that it is necessary in order for that authority to be able to check compliance with the essential cybersecurity requirements set out in Annex I”. Separately, “ADCO may decide to conduct a Union wide dependency assessment for specific categories of products with digital elements”, and for that purpose “market surveillance authorities may request manufacturers of such categories of products with digital elements to provide the relevant software bills of materials” (Article 13(25)).
- What may come. “The Commission may, by means of implementing acts taking into account European or international standards and best practices, specify the format and elements of the software bill of materials referred to in Part II, point (1), of Annex I” (Article 13(24)).
What does the CRA not require?
| Common misreading | What the text says |
|---|---|
| “You must publish your SBOM.” | No such duty. Annex II, point 9 covers the case where “the manufacturer decides to make available the software bill of materials to the user”. Recital 77 says manufacturers “should not be obliged to make the SBOM public” (recitals explain the law but aren’t binding). |
| “It must be SPDX or CycloneDX.” | No format is named, only “commonly used and machine-readable” (Annex I, Part II, point 1). SPDX and CycloneDX are the two formats TR-03183-2 accepts. |
| “It must list every transitive dependency.” | The CRA floor is “at the very least the top-level dependencies”. The CRA doesn’t define “top-level”. TR-03183-2 asks for more (below). |
| “It must include vulnerabilities.” | The SBOM sits next to vulnerability handling, but the text doesn’t ask the SBOM itself to carry vulnerability data. TR-03183-2 says an SBOM “MUST NOT contain vulnerability information” (section 3.1). |
| “It’s required now.” | The SBOM is an Annex I requirement, which applies from 11 December 2027 (Article 71(2)). Article 14 reporting has applied since 11 September 2026. |
Is there an official CRA SBOM format or field list?
Not in the CRA. Unless the Commission uses its Article 13(24) power, the regulation’s own words are the whole legal requirement. The CRA doesn’t refer to TR-03183-2 or any other national guideline by name.
A detailed public field list is BSI TR-03183-2, from Germany’s Federal Office for Information Security. BSI’s page says it “is meant to be a guide and is not binding or mandatory”. As of 25 October 2026, the current version on BSI’s list is 2.1.0, dated 20 August 2025. Watch the file names: on that list, the PDF whose file name ends “v2_2_0” is labelled “Version 1.1 (outdated)”.
What version 2.1.0 asks for, on top of the CRA, if you follow it:
- Format: JSON or XML, valid CycloneDX “version 1.6 or higher” or SPDX “version 3.0.1 or higher” (section 4).
- One SBOM per version: “A separate SBOM MUST be generated for each software version” (section 3.1).
- Depth: recursive dependency resolution “at least for each component included in the scope of delivery”, “at least up to and including the first component that is outside the scope of delivery” (section 5.1). That first outside component only has to be identified (below).
- Build data: the SBOM “MUST contain the same information as available during the build process or equivalent information where the build process does not exist” (section 5.1).
- No vulnerabilities: an SBOM “MUST NOT contain vulnerability information” (section 3.1). The TR points to CSAF and VEX for that (Introduction and section 8.1.14).
- Which version of the TR: to claim conformance you use the most recent version; the one before it “MAY” be used for up to six months after a new one is issued (section 7).
Which SBOM fields are required for each component?
Not every component needs every field. TR-03183-2 sets a different list for each kind of entry, and the order for deciding which applies is referenced, then identified, then fully described (section 5.1).
| Kind of entry | When it applies | Fields the TR asks for |
|---|---|---|
| Fully described (sections 5.1, 5.2.2, 8.3.4) | Every component in the SBOM that isn’t one of the kinds below, which covers every other component in the scope of delivery: parts “delivered with the software product” (section 8.1.11). For an app, in our reading, what you bundle into the build | All 10 required fields in the table below, plus the additional fields where they exist and the format can carry them |
| Identified (section 3.2.4) | The first component outside the scope of delivery on each path (section 5.1). For an app, in our reading, the operating system you run on | Creator, name, version, and other unique identifiers where they exist (section 5.2.4) |
| Referenced (section 3.2.5) | A component covered by another SBOM you reference instead of merging, allowed “if and only if” that SBOM complies with the TR (section 5.1) | Creator, name, version, and the reference. Anything else “SHOULD NOT be recorded”, except fields the format needs for a valid SBOM |
| Logical (sections 3.2.2, 8.1.3) | An abstraction over several components, such as “an operating system, application, framework, container” | Creator, name, version, dependencies, distribution licences; and, where they exist, other unique identifiers, original licences, effective licence and security.txt URL (sections 5.2.4, 5.2.5). Filename, hash and file properties not required |
The conditions and exceptions that change what a fully described component needs:
- Assembly. “Information that is not available due to the way a component is assembled has to be omitted even if it is specified as a required data field, e.g. the hash or filename of the component” (section 3.2.1).
- What counts as a component. A logical component, “a single executable file or an archive file” MUST be one; “any other file MAY” (section 3.2.1). A configuration file is “Not required to be listed in an SBOM” (Table 7, section 8.1.7). For compiled code, the component “used during the execution of the linker MUST be listed”, statically or dynamically linked (section 3.2.1).
- The app itself. “The primary component, i.e. the product itself”, is a component too (section 3.2.1).
- Fallbacks. Creator is an email address, or a URL “If no email address is available”. If a component has no name, use “the actual filename”; if it has no version, the file’s modification date (sections 5.2.1 and 5.2.2).
- Duplicates. Multiple instances of a component with different meta-information “MUST be listed separately” (section 5.1).
- Additional and optional fields. Additional fields “MUST additionally be provided, if it exists and fulfils the requirements of an SBOM format specification for the specific data field” (section 5.2.4; section 5.2.3 sets the same condition for the SBOM-URI). Optional fields “MAY” be added under the same condition (section 5.2.5).
- Licences. An SPDX licence identifier or expression, not pasted licence text. Where SPDX has none, a
LicenseRef-scancode-identifier from the ScanCode LicenseDB, and failing that one under the inventorying organisation’s ownLicenseRef-prefix (section 6.1).
Where does each field go in CycloneDX and SPDX?
Field locations for a fully described component are from the TR’s mapping tables (section 8.2), for CycloneDX 1.6 and SPDX 3.0.1 JSON. In CycloneDX the app itself sits in metadata.component, not components[]. Three SPDX cells follow SPDX 3.0.1’s own spelling (see its external reference types) rather than the TR’s example literally: the TR writes the deployable-form URI as a "binaryArtifact" key on a software_File and the source type as SourceArtifact, where SPDX 3.0.1 has the external reference types binaryArtifact and sourceArtifact. And the TR writes the external identifier type packageURL, which SPDX 3.0.1 spells packageUrl.
| Field | Tier | CycloneDX 1.6 | SPDX 3.0.1 |
|---|---|---|---|
| Creator of the SBOM (email, or URL if no email) | Required | metadata.manufacturer url or contact[].email |
CreationInfo.createdBy → Person or Organization with an email or urlScheme external identifier |
| Timestamp | Required | metadata.timestamp |
CreationInfo.created |
| Component creator (email, or URL if no email) | Required | components[].manufacturer url or contact[].email |
software_Package.originatedBy |
| Component name | Required | components[].name |
software_Package.name |
| Component version | Required | components[].version |
software_Package.software_packageVersion |
| Filename of the component | Required | property bsi:component:filename |
software_File.name, linked by hasDistributionArtifact |
| Dependencies, with completeness stated | Required | dependencies[].dependsOn (a component with none still gets an empty entry), plus compositions[].aggregate |
Relationship contains or dependsOn, with completeness |
| Distribution licences | Required | licenses[].expression with "acknowledgement": "concluded" |
hasConcludedLicense → simpleLicensing_LicenseExpression |
| SHA-512 hash of the deployable component | Required | externalReferences[type=distribution].hashes, alg SHA-512 |
software_File.verifiedUsing, algorithm sha512 |
| Executable, archive and structured properties | Required | properties bsi:component:executable, bsi:component:archive, bsi:component:structured |
software_File.software_additionalPurpose: executable, archive, and container if structured or firmware if not |
| SBOM-URI | Additional | serialNumber |
software_Sbom spdxId |
| Source code URI | Additional | externalReferences[type=source-distribution].url |
externalRef type sourceArtifact |
| URI of the deployable form | Additional | externalReferences[type=distribution].url |
externalRef with type binaryArtifact |
| Other unique identifiers (CPE, purl) | Additional | cpe, swid.tagId or purl |
externalIdentifier types cpe22, cpe23, swid, packageUrl |
| Original licences | Additional | licenses[].expression with "acknowledgement": "declared" |
hasDeclaredLicense |
| Effective licence | Optional | property bsi:component:effectiveLicense |
Relationship other, comment hasEffectiveLicense |
| Hash of the source code | Optional | externalReferences[type=source-distribution].hashes |
source software_SoftwareArtifact.verifiedUsing |
| URL of the component creator’s security.txt | Optional | externalReferences[type=rfc-9116].url |
external reference securityOther |
The bsi:component:* properties come from BSI’s own CycloneDX property taxonomy, because CycloneDX has no native field for them (section 8.2). The taxonomy spells one of them bsi:component:effectiveLicence, while the TR’s mapping table writes bsi:component:effectiveLicense, so check which spelling your tools expect.
The contact address that goes with your SBOM
Annex VII, point 2(b) lists the SBOM in the same breath as “evidence of the provision of a contact address for the reporting of the vulnerabilities”: your own contact. TR-03183-2’s optional security.txt field points the other way, to each component creator’s contact (section 5.2.5). That is whom you’d tell about a flaw in a component integrated in your product, which Article 13(6) requires you to report to “the person or entity manufacturing or maintaining the component”.
We measure the first kind. On our monthly measurement of European app publishers, 12.1% of small publishers in European App Store charts (373 of 3,082; small means under 1,000 App Store ratings across their charting apps) published any way to report a security vulnerability as of 26 September 2026. The rate was still 12.1% at the 3 October 2026 measurement.
What can an SBOM generated from your lockfiles carry?
In our test, SBOMs generated from mobile lockfiles gave each package a name, a version and identifiers (a package URL, or purl, and a CPE), and no other TR-03183-2 field. All four kinds of entry in the component table above include the component creator, and none of the 1,236 packages had one. Against the 10 required fields for a fully described component (section 5.2.2), they carried 2: name and version.
We ran this on 25 October 2026. We pinned the default-branch commit of 18 public open-source iOS and Android app repositories, ran an open-source generator, syft 1.52.0 on default settings, over their committed lockfiles, and checked its output field by field. 11 of the 18 had lockfiles to scan; the other 7 (6 Android, 1 iOS) had none. The reasons are structural, so the result describes the method, not these projects (none of which published these SBOMs; we generated them).
| Required field (fully described) | In the generated SBOMs | Why |
|---|---|---|
| Filename, executable, archive, structured properties | Never | CycloneDX has no native field for these, so TR-03183-2 maps them to BSI taxonomy properties there (section 8.2). The generator doesn’t write them: the string bsi:component doesn’t occur in its program file. |
| SHA-512 of the deployable component | Never | TR-03183-2 means the hash of the component “as a file on a mass storage device” (section 5.2.2). A lockfile scan has no built file to hash. |
| Distribution licence, component creator | Never | None of the 56 lockfiles contains a licence or author field (we searched their text). |
| Dependencies | Dropped | The Podfile.lock lists each pod’s dependencies and the top-level ones, but the generator wrote no dependency entries for any SBOM. Package.resolved is a flat list of pins, and a Gradle lockfile line is “a single dependency in the group:artifact:version format” (Gradle documentation). |
| Name, version | Always | Every package had both, plus a purl and a CPE (TR-03183-2’s additional “other unique identifiers”). |
That’s 1,236 package entries across 11 SBOMs (each package counted once per SBOM). In our sample, every Package.resolved pin recorded its source repository URL, but the generator folded it into the purl rather than the “source code URI” field. At the SBOM level, all 11 had a timestamp and a serial number (the TR’s SBOM-URI), and none named an SBOM creator or said whether its dependency list was complete.
Could the empty file fields be allowed? Section 3.2.1 omits information “not available due to the way a component is assembled”, naming the hash and filename. But the same section says the component “used during the execution of the linker MUST be listed”, and section 5.1 asks for what is “available during the build process”. In our reading, that exception can’t cover a field that is empty only because no build was run, so you need a build to fill these.
- Method. We downloaded every Package.resolved, Podfile.lock and Gradle lockfile at the pinned commit: 56, after excluding folders whose names start with test, sample, example, fixture or demo. The generator wrote CycloneDX 1.7 JSON (TR-03183-2 accepts “version 1.6 or higher”; we checked its 1.6 field locations). We removed 2 entries that were a lockfile comment line. A hand-built SBOM with every field present scored on all of them, which shows the check finds fields that exist.
- What the generator read. 53 of the 56 lockfiles. It only reads Gradle lockfiles named gradle.lockfile, so it skipped one buildscript-gradle.lockfile (136 build-script classpath entries) and two empty settings-gradle.lockfile files.
- Limits. One generator on default settings, without enrichment options and without builds. The sample is uneven: one Android app repository accounts for 835 of the 1,236 packages. The point is what this method can and can’t produce, not a share of projects that pass.
- Scope. Lockfiles came from the whole repository, not only the shipped app. Here that includes folders such as BuildTools and SnapshotTestingSupport, a macOS app project, a second iOS app in the same repository, and Gradle configurations used only for builds or tests. Every SBOM scored the same on every field, so the field result holds. But the package count isn’t a count of components shipped in one app.
- Android. 6 of the 8 Android repositories had no Gradle lockfile anywhere in the repository tree (no file named gradle.lockfile, and no other .lockfile under a Gradle path), so there was nothing to scan. On iOS, 9 of 10 had at least one lockfile.
In our reading, an SBOM like these can be argued to meet the CRA’s minimum wording, but without a dependency graph it can’t show which entries are your top-level dependencies.
What do SPM, CocoaPods and Gradle leave out?
- Gradle. Locking “is enabled per dependency configuration” (Gradle documentation), so it’s off until you turn it on (see the Android result above). Where it’s on, each line gives coordinates, a version and the configurations that use it. Check that your generator reads every lockfile your build writes, not just gradle.lockfile.
- CocoaPods. Podfile.lock “tracks the version of each Pod that was installed” (CocoaPods guide), and the one in our sample also held the pod dependency graph. Check that your generator keeps that graph: ours didn’t.
- Swift Package Manager. In our sample, every Package.resolved pin recorded its source repository URL. If you want it in TR-03183-2’s “source code URI” field, check that your generator puts it there.
- Anything your package manager doesn’t manage. A lockfile scan only sees what is in the lockfiles. Check by hand for a binary framework or Android library added directly to the project, and for SDKs that bundle their own dependencies.
- The app itself. Check what your generator records as the SBOM’s subject, and set the primary component (
metadata.component) to your app’s real name and version.
Our notes on mobile SBOMs cover three problems we found by checking SBOMs from our own CI against the lockfiles they came from, and the Cyber Resilience Act for app teams guide covers the rest of the CRA.
SBOM self-check: copy, paste, tick
CRA (Regulation (EU) 2024/2847)
[ ] Our product is in the CRA's scope (Article 2)
[ ] We have an SBOM for each such product we place on the EU market
(Annex I, Part II, point 1)
[ ] It is in a commonly used, machine-readable format (e.g. CycloneDX or SPDX)
[ ] It covers at least our top-level dependencies (Annex I, Part II, point 1)
[ ] It is in the product's technical documentation (Annex VII, point 2(b))
[ ] The technical documentation it sits in is "continuously updated, where
appropriate, at least during the support period" (Article 31(2)) and kept
at least 10 years or the support period, whichever is longer
(Article 13(13))
[ ] We can hand it to a market surveillance authority on request
(Annex VII, point 8; Article 13(25))
[ ] If we make it available to users, our user information says where
(Annex II, point 9)
BSI TR-03183-2 v2.1.0 (voluntary guideline, stricter than the CRA)
[ ] CycloneDX 1.6+ or SPDX 3.0.1+, JSON or XML (section 4)
[ ] One SBOM per app version (section 3.1)
[ ] SBOM creator (email, or URL if no email) and timestamp (section 5.2.1)
[ ] Primary component = the app, with its real name and version (3.2.1)
[ ] Each component in what we ship (unless referenced or logical), fully
described: creator, name, version, filename, dependencies, distribution
licence, SHA-512 of the deployable file, executable/archive/structured
properties (5.2.2), leaving out what is "not available due to the way a
component is assembled" (3.2.1)
[ ] Resolved down each path to the first component outside what we ship,
which needs at least creator, name, version and another identifier
such as a purl or CPE if one exists (5.1, 3.2.4)
[ ] A component covered by a TR-compliant SBOM we reference: creator, name,
version and the reference (3.2.5)
[ ] Completeness of each dependency list stated; the app's own entry lists
our top-level dependencies (5.2.2)
[ ] Where they exist and the format can carry them: source URI, deployable
URI, purl or CPE, original licences (5.2.4)
[ ] Licences as SPDX IDs or expressions, or LicenseRef- where SPDX has none;
licence text never in place of an identifier (6.1)
[ ] No vulnerability data in the SBOM itself; use CSAF or VEX (3.1)
If you’d rather not assemble this yourself, SecureByDesignLabs helps app teams turn obligations like this into evidence they can file: a gap report that traces every obligation to its article or annex, a register of the third-party components you ship, and the documents the regulation expects you to keep.
Frequently asked questions
Is an SBOM mandatory under the CRA?
Yes, for a product with digital elements in the CRA’s scope (Article 2), placed on the market from 11 December 2027, and for earlier ones substantially modified from then (Articles 69(2) and 71(2)). Annex I, Part II, point 1 requires it, and Annex VII, point 2(b) puts it in the technical documentation.
Do I have to publish my SBOM or give it to customers?
The CRA doesn’t require either. If you decide to make it available to users, Annex II, point 9 asks you to say where it can be accessed.
Which SBOM format does the CRA require?
None by name: “a commonly used and machine-readable format” (Annex I, Part II, point 1). The Commission may specify the format and elements by implementing act (Article 13(24)). CycloneDX and SPDX are the two formats TR-03183-2 accepts.
Does the CRA SBOM need transitive dependencies?
The CRA’s floor is “at the very least the top-level dependencies”. TR-03183-2 goes further: it asks you to resolve everything in the scope of delivery, down to the first component outside it, which only has to be identified (section 5.1).
Can a regulator ask for my SBOM?
Yes. Where applicable, a market surveillance authority can ask on a reasoned request where it needs the SBOM to check compliance with Annex I (Annex VII, point 8). Authorities may also request SBOMs for a Union-wide dependency assessment of specific product categories, if ADCO decides to run one (Article 13(25)).
Is BSI TR-03183-2 mandatory?
No. BSI’s own page says it “is meant to be a guide and is not binding or mandatory”. It isn’t part of the CRA, which doesn’t refer to it. It’s a technical guideline from Germany’s Federal Office for Information Security (BSI), useful as a detailed field list while an Article 13(24) implementing act remains a possibility rather than a rule.
Quotations are from Regulation (EU) 2024/2847 as published in the Official Journal (© European Union); only the electronic edition of the Official Journal is authentic. TR-03183-2 quotations are from BSI TR-03183-2 version 2.1.0 (© Federal Office for Information Security). This is general information, not legal advice for your situation.
Lockfile figures: SecureByDesignLabs analysis of 18 public open-source app repositories on 25 October 2026; method above. This page is the study’s publication.
Responses