Guides · Updated 27 July 2026

How to produce an SBOM when you have no source code

Your ODM will not give you source. Annex I Part II still requires an SBOM. Here is how binary analysis actually works and what it can and cannot tell you.

The short answer

When you have no source code, the only way to produce an SBOM is to analyse the firmware binary itself: unpack the image to recover its filesystem, extract printable strings from every file inside, and match them against known version banners such as "BusyBox v1.36.1" or "OpenSSL 1.0.2n". This yields component names and, where the banner includes one, versions. Each identification should carry a confidence level and the evidence behind it, because a version you cannot point to in the image is not defensible to an auditor.

Why the usual tools do not work here

Syft, Trivy and Dependabot expect a repository and a package manifest. They read package.json, go.mod, requirements.txt. Embedded firmware has none of those for the parts that matter: a vendor HAL, an RTOS kernel, out-of-tree drivers, binary blobs, and an ODM rootfs image you received as a .bin file.

Annex I Part II point 1 requires an SBOM in a commonly used, machine-readable format covering at least the top-level dependencies. There is no exemption for firmware you did not compile.

The four steps of a binary scan

First, identify the container. A firmware image is usually a wrapper around one or more filesystems: squashfs is overwhelmingly the most common in consumer devices, with UBI, JFFS2, cramfs and plain tar or cpio also appearing. Some images are Intel HEX or Motorola S-record text rather than raw bytes, and have to be converted to a flat binary first. ELF images carry their own section table.

Second, unpack, and keep unpacking. A single image usually nests: a U-Boot header wrapping a gzip stream wrapping a squashfs wrapping another compressed archive. Vendors also ship mangled squashfs with non-standard compression that mainline unsquashfs refuses. Those need a patched extractor.

Third, extract strings from every file recovered. Most components identify themselves. A Linux kernel carries "Linux version 4.4.60". BusyBox carries "BusyBox v1.36.1". OpenSSL carries "OpenSSL 1.0.2n 7 Dec 2017". lwIP, mbed TLS, dropbear, U-Boot, hostapd, dnsmasq and dozens of others do the same.

Fourth, match those strings against a signature pack and record what matched, where. The file, the byte offset and the exact string are the evidence. Without them you have an assertion, not a finding.

Confidence, and why it must be on every row

Identifications are not equally strong, and a file that pretends they are falls apart under review. Three levels:

  • High: an unambiguous self-identifying banner containing the version. "BusyBox v1.36.1" is not ambiguous.
  • Medium: a version string that is almost certainly this component, but could be a reference inside unrelated text, or a version recovered from a path or filename rather than a banner.
  • Low: the component is clearly present but no version is recoverable. Report it as present with the version unknown.

Never guess a version. "OpenSSL is present, version unknown" is a line you can defend. "OpenSSL 1.0.2" when the image only said "OpenSSL" is a claim an auditor can break in one question, and it puts every other line under suspicion.

What binary analysis cannot tell you

Statically linked code frequently loses its banner entirely, so a library compiled into a monolithic binary may be invisible. Stripped binaries lose symbol names. Encrypted or signed-and-packed images cannot be unpacked without keys. Heavily compressed regions may not yield strings at all.

This is why the build-time path and the binary path belong together. Where you build from source, the manifest is ground truth. Where you do not, the image gives what it can. Merged, they cover more than either alone, and the merge should record which side saw what.

Formats to export in

CycloneDX and SPDX, both. Procurement teams ask for one or the other and rarely the same one twice. Confidence and evidence travel as CycloneDX properties and SPDX package comments, so the extra detail survives export without breaking schema validation.

Finding the component is only half of it

A component list is not a watch list. To monitor a component you need an identifier the vulnerability feeds use, which for firmware means a CPE. Several common components do not have a usable one. Realtek, MediaTek and Broadcom BSPs are catalogued in NVD per silicon part, not per SDK. A handful of small libraries are not in the dictionary at all.

A tool that silently skips those reports them as clean, and "we found nothing" reads exactly like "we did not look". List the unmatched components by name with the reason. It is the difference between a scan result and an audit finding.

Questions people actually ask

Can I produce a CRA-compliant SBOM without source code?

Yes. Binary analysis of the firmware image recovers component names and, where version banners are present, versions. Annex I Part II requires an SBOM in a machine-readable format covering at least top-level dependencies; it does not require that you compiled the code yourself.

What firmware formats can be analysed?

Raw .bin, Intel HEX, Motorola S-record, ELF, and container and filesystem formats including squashfs, UBI, JFFS2, cramfs, tar, cpio, zip, gzip, xz, LZMA, LZ4, zstd and U-Boot images. Nested combinations are normal and need recursive unpacking.

How accurate is binary SBOM analysis?

Accurate where components self-identify with a version banner, which covers most of a typical Linux-based firmware userland. Less so for statically linked, stripped or packed code. This is why every component should carry a confidence level and the evidence behind it rather than a bare assertion.