Parts 1 and 2 of this series showed just how easy it is to automate unit testing and hardware-in-the-loop (HIL) testing with Dojo Five EmbedOps, a build integration and automation platform for commercial embedded and IoT device development teams. Both articles provided practical guides for using EmbedOps to quickly build the typical tests that a developer might conduct for a Bluetooth Low Energy (BLE)-enabled sensor, such as finding overflow bugs or verifying over-the-air (OTA) device firmware updates (DFUs).
The aim of these exercises was to demonstrate the effectiveness of unit and HIL testing for reducing downtime, addressing issues, and ensuring that devices don’t become bricked in the field, all issues that can come at great economic and reputational cost to manufacturers.
To recap, unit testing verifies the performance of small sections of code in isolation to make sure they function as intended. HIL testing builds on this by running the complete software stack on its target hardware to confirm that the system performs as intended and meets its specifications.
However, unit and HIL testing are still limited in that they both rely on specific inputs and expected outputs to pass preconceived tests. As such, they still leave gaps in performance assessment for anything that was not explicitly tested.
In the real world, devices and their stacks operate as part of a much wider ecosystem of third-party solutions and external software dependencies, such as operating systems (OSs), libraries, protocols, and drivers. These components may contain documented security vulnerabilities, and if these vulnerabilities are not mitigated before releasing products to market, they may be exploited in the field by attackers.
The Real Danger of Unaddressed Vulnerabilities
Take, for example, a device that passes all unit and HIL tests but happens to use a slightly dated BLE stack. Despite everything seeming fine on the bench, it is deployed while containing a previously documented stack-based buffer overflow flaw that handles incoming pairing requests. This was missed by unit and HIL tests because they never used the obscure sequence of packets needed to trigger the known overflow event.
Later, a hacker discovers that a whole range of products use this BLE stack, and that this vulnerability makes affected systems remotely exploitable without physical access or prior pairing. By getting near to a device and sending the correct packets, such as by driving by a smart factory with a BLE-enabled laptop, they can now expose the flaw and remotely execute arbitrary code on a device’s processor.
When the news breaks, manufacturers face recalling devices due to security concerns and suffering financial losses through regulatory fines and/or physical replacement of devices. After all, attackers may have disabled OTA updates so that devices remain under their control for nefarious means, rather than simply bricking them. Understandably, customer trust is lost, and manufacturer reputation is severely damaged.
There is a simple solution to reducing the risk of this scenario: CVE (Common Vulnerabilities and Exposures) scanning during development.
Identifying Possible Targets through CVE Scanning
The CVE Program offers a comprehensive list with unique identifiers for known cybersecurity vulnerabilities and exposures in software and hardware. With more than 300,000 records to date, it provides a vital resource for ensuring embedded products are considered safe within the current understanding of the cybersecurity landscape and are therefore ready to be deployed from a security perspective.
CVE scanning is the process of looking for publicly disclosed issues with anything that was not developed in-house, such as third-party, open-source or commercially available components of the firmware or OS. In principle, CVE scanning is a simple process: The first step is to create a software bill of materials (SBOM) for a device. The software components listed in the software bill of materials can then be compared against public vulnerability databases to find issues that are not self-evident from unit and HIL tests.
In this way, CVE scanning offers proactive risk mitigation against known cybersecurity weaknesses to ensure compliance with modern regulations like the Cyber Resilience Act. While vulnerability scanning has been done manually in the past, automating scans through software composition analysis (SCA) is now the standard due to the inherent complexity and depth of the task. This greatly accelerates the process of checking vast public vulnerability databases, reducing the chance of errors or missed dependencies so that developers can be confident in the readiness of their builds for the real world.
CVE Scanning with EmbedOps
Much like for unit and HIL testing, EmbedOps provides the tools and services to conduct automated CVE scanning. The result is a streamlined approach to security patching so that devices are not deployed while still containing known vulnerabilities.
EmbedOps offers flexible operation with continuous integration (CI) and continuous delivery (CD) platforms like GitHub, GitLab and Bitbucket for easy adoption and automatic SBOM generation during the CVE scan build process. Once an SBOM is available, EmbedOps automatically scans it against public vulnerability databases and generates a report of known security flaws to address. This report includes the vulnerabilities for each software component in a build as well as the severity rating of each vulnerability, which can be used to track security posture and improve product reliability as development continues.
By integrating CVE scanning as part of the standardized testing process using EmbedOps, developers gain the following benefits:
- Faster, continuous CVE monitoring: Scans on every build ensure vulnerabilities are caught as soon as they are reported to databases.
- Easier scalability and risk prioritization: Common Vulnerability Scoring System (CVSS) scores help developers shift focus where it is needed when patching vulnerabilities.
- Easier compliance: Automated report generation allows developers to show when scans were conducted and where vulnerabilities were found and fixed to meet regulatory requirements.
- Optional backstop function: EmbedOps can be configured with security gates in the CI/CD pipeline to prevent vulnerable builds from accidentally being deployed.
With EmbedOps, embedded teams can focus on fixing vulnerability issues as soon as they arise during development, rather than having to implement routine scanning efforts or finding them out the hard way through customer complaints. EmbedOps’ automated CVE scanning and reporting saves valuable time and provides devices with the best start before sending them into the field.
This ease of use is best demonstrated with another example based on a Nordic nRF54 development kit running Zephyr RTOS. The following demo builds on the HIL testing framework set up in part 2 but does not require the device under test (DUT) to be plugged in. Nevertheless, it is recommended that developers first complete the HIL testing demo as this populates the repository with the files and folders necessary for setting up CVE scanning.
Modifying the HIL testing framework for CVE scanning
To begin, fork the previous HIL test repository so that a clean copy is retained for later use. In the ci folder of the clone, modify the pipeline.sh and zephyr.sh files so that they read as shown below. This will remove the HIL tests and add functions for SBOM generation and reporting to the EmbedOps platform.
pipeline.sh
#! /bin/bash
set -eo pipefail
SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)"
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
cd $REPO_ROOT
jq -r '.steps[]' ci/pipeline.json | while read script; do
bash -c "$script"
done
zephyr.sh
#!/usr/bin/env bash
set -eo pipefail
if [ "$CI" = "true" ]; then
set -x
# trust the current directory in CI
git config --global --add safe.directory "$(pwd)"
fi
SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)"
MANIFEST_DIR="$(dirname "$SCRIPT_DIR")"
cd "$MANIFEST_DIR"
west spdx --init -d build/app
BUILD_COMMAND="west build --build-dir build app --board nrf54l15dk/nrf54l15/cpuapp -p always -DCONFIG_BUILD_OUTPUT_META=y"
script -q -c "$BUILD_COMMAND" build.log < /dev/null
west spdx -d build/app
# report to EmbedOps platform
# set EMBEDOPS_API_REPO_KEY as Repo Variable in your provider's CI to capture a history of reports.
# If you have access, this key can be found at https://app.embedops.io/app/manage/repos/<Embedops Repo UUID>
set +e
EMBEDOPS_COMPILER=GCC eo report sbom build/app/spdx/app.spdx
EMBEDOPS_COMPILER=GCC eo report sbom build/app/spdx/zephyr.spdx
EMBEDOPS_COMPILER=GCC eo report sbom build/app/spdx/build.spdx
EMBEDOPS_COMPILER=GCC eo report sbom build/app/spdx/modules-deps.spdx
set -e
Next, head to the app folder within the main directory and modify the prj.conf file to add the CONFIG_BUILD_OUTPUT_META command. This will enable creation of a meta file that the SPDX generator will use to build an SBOM.
prj.conf
# General
CONFIG_INIT_STACKS=y
# Enable MCUmgr and dependencies.
CONFIG_NET_BUF=y
CONFIG_ZCBOR=y
CONFIG_CRC=y
CONFIG_MCUMGR=y
CONFIG_STREAM_FLASH=y
CONFIG_FLASH_MAP=y
# Some command handlers require a large stack.
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
CONFIG_MAIN_STACK_SIZE=4096
# Ensure an MCUboot-compatible binary is generated.
CONFIG_BOOTLOADER_MCUBOOT=y
# Enable flash operations.
CONFIG_FLASH=y
# Enable most core commands.
CONFIG_IMG_MANAGER=y
CONFIG_MCUMGR_GRP_IMG=y
# Enable logging
CONFIG_LOG=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_LOG_BACKEND_UART=n
# Enable the BLE stack with GATT Client configuration
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_GATT_CLIENT=y
CONFIG_BT_ATT_PREPARE_COUNT=2
CONFIG_BT_DEVICE_NAME="EmbedOps DFU Demo Device"
# Disable BLE Security/Authentication
CONFIG_BT_SMP=n
CONFIG_BT_USER_DATA_LEN_UPDATE=y
CONFIG_BT_USER_PHY_UPDATE=y
CONFIG_BT_GAP_AUTO_UPDATE_CONN_PARAMS=n
# Allow for large Bluetooth data packets.
CONFIG_BT_L2CAP_TX_MTU=400
CONFIG_BT_BUF_ACL_TX_SIZE=502
CONFIG_BT_BUF_ACL_RX_SIZE=502
# Enable the Bluetooth mcumgr transport (unauthenticated).
CONFIG_MCUMGR_TRANSPORT_BT=y
CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL=y
# Enable the Shell mcumgr transport.
CONFIG_BASE64=y
CONFIG_SHELL=y
CONFIG_SHELL_BACKEND_DUMMY=y
CONFIG_SHELL_BACKEND_SERIAL=n
CONFIG_MCUMGR_TRANSPORT_SHELL=y
CONFIG_MCUMGR_GRP_SHELL=y
# Enable the NUS service
CONFIG_BT_NUS=y
# Enable the Device Information Service
CONFIG_BT_DIS=y
CONFIG_BT_DIS_PNP=n
CONFIG_BT_DIS_MODEL="EmbedOps DFU Demo Device"
CONFIG_BT_DIS_MANUF="Dojo Five"
CONFIG_BT_DIS_SERIAL_NUMBER=y
CONFIG_BT_DIS_FW_REV=y
CONFIG_BT_DIS_HW_REV=y
CONFIG_BT_DIS_SW_REV=y
CONFIG_BT_DIS_SERIAL_NUMBER_STR="EmbedOps Serial"
# Below is setup to let DIS information be read from settings
CONFIG_SETTINGS_RUNTIME=y
CONFIG_SETTINGS=y
CONFIG_SETTINGS_NONE=y
CONFIG_BT_DIS_SETTINGS=y
CONFIG_BT_DIS_STR_MAX=64
# Enable DK LED and Buttons library
CONFIG_DK_LIBRARY=y
# Serial configs
CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_UART_LINE_CTRL=y
# These configurations are not being used in sdk-nrf or sdk-zephyr
CONFIG_BT_LOG_LEVEL_ERR=y
CONFIG_MCUMGR_TRANSPORT_LOG_LEVEL_DBG=y
CONFIG_MCUMGR_LOG_LEVEL_DBG=y
CONFIG_BUILD_OUTPUT_META=y
The repository is now fully prepped for setting up CVE scanning.
Initialize the build directory for SPDX by running the west spdx —init -d build/app command in the EmbedOps command line interface (CLI). Since this demo uses a multi-domain build, build/app specifies the application environment that we are interested in, rather than the bootloader.
Finally, the following commands are used to build the project:
west build --build-dir build app --board nrf54l15dk/nrf54l15/cpuapp -p
always --sysbuild -DCONFIG_BUILD_OUTPUT_META=y
Generating an SBOM and reviewing CVE scan results
Once the project is built, an SBOM can be generated using the west spdx -d build/app command, which will create several files of interest:
- zephyr.spdx
- modules-deps.spdx
- app.spdx
- build.spdx
This SBOM can then be reported to EmbedOps using:
set +e
EMBEDOPS_COMPILER=GCC eo report sbom build/app/spdx/app.spdx
EMBEDOPS_COMPILER=GCC eo report sbom build/app/spdx/zephyr.spdx
EMBEDOPS_COMPILER=GCC eo report sbom build/app/spdx/build.spdx
EMBEDOPS_COMPILER=GCC eo report sbom build/app/spdx/modules-deps.spdx
set -e
The eo open command will then open a browser page to the EmbedOps dashboard, where the results of the CVE scan can be reviewed by heading to the Binary Analysis section under the Security tab. The report should look similar to the following:

Figure 1: The results of a CVE scan as displayed in the EmbedOps web dashboard. (Image source: Dojo Five)
Here, two known CVEs have been identified within the SBOM, both ranked with medium severity. The middle column of the table displays the CVSS score, which enables developers to quickly gauge the risk factor at a more granular level. In this case, CVE-2025-54764 should probably be addressed first as it has the higher score.
Conclusion
While unit tests and HIL tests validate how a system runs in real-life scenarios, CVE scanning closes the gap with external dependencies to ensure that a system’s foundations are not riddled with cracks. Dojo Five EmbedOps automates all three of these workloads, simplifying setup, CI/CD integration, and report generation so that embedded teams can be confident in device reliability and compliance before releasing their products into the world.
BLE stacks such as those operating on Nordic nRF54 devices provide a high-value CVE scanning case study that demonstrates the importance of firmware teams having visibility into risks imposed by external dependencies. By combining unit and HIL testing with CVE scanning, teams can ensure devices function as intended while preventing known vulnerabilities in external dependencies from being exploited by hackers, enabling safer, more secure devices that protect both end users and manufacturer reputation.
Head to the Dojo Five website or EmbedOps Docs to learn more about CVE scanning, and sign up to EmbedOps today to give these exciting demos a try.


