SBOM Generation for Medical Device Software: Practical Setup by Stack
Practical SBOM generation instructions across common software stacks, from embedded C/C++ to cloud containers, with validation and quality gates for FDA submission.

At CyberMed, we help teams prepare their full cybersecurity package for submission, but one file we don't directly generate is the SBOM.
That's intentional. Your SBOM should be generated inside your regular build cycle so it captures exact dependency data from the repository and release artifact, not a manually reconstructed snapshot.

This tutorial gives practical SBOM generation instructions across common software stacks. If your team uses a different language, framework, or toolchain, reach out and we can help you map an SBOM process to your environment.
Purpose and scope
This guide covers SBOM generation for:
- Embedded software
- Mobile apps
- Cloud and SaMD workloads
- Common backend ecosystems
Regulatory objective is to maintain a machine-readable software inventory (CycloneDX or SPDX) that supports FDA premarket cybersecurity documentation expectations for cyber devices and supports IEC 62304 SOUP control evidence used in EU MDR technical documentation.
General operating rules
-
Automate at build time
- Generate SBOMs in the CI/CD Build phase so artifacts reflect exact release versions.
-
Generate a release SBOM from production/runtime dependencies
- For submission and released-product evidence, exclude dev/test-only packages.
- Keep a separate internal build/toolchain SBOM when needed for supply-chain investigations.
-
Include SOUP explicitly
- Third-party and proprietary components outside package managers still need declaration.
-
Use immutable inputs
- Build from lockfiles, pinned tool versions, and immutable image digests.
Toolchain setup by ecosystem
A) Embedded systems (C/C++)
Embedded projects often lack complete package-manager metadata. Filesystem scanning is one input, not the whole picture.
Use a combined method:
- build-system manifests (CMake/Conan/vcpkg/Yocto/Bazel, as applicable),
- source and filesystem scan,
- binary or artifact scan,
- manual SOUP declaration for vendor/toolchain components.
Tool: cdxgen (OWASP)
npm install -g @cyclonedx/cdxgen@<pinned-version>
# Scan source and include submodules/SDKs
cdxgen -t c -o sbom.json --recurse
B) Rust (safety-critical SaMD)
Rust lockfiles are a good fit for deterministic dependency capture.
Tool: cargo-cyclonedx
cargo install cargo-cyclonedx --version <pinned-version>
# Generate for all workspace members from lockfile-resolved dependencies
cargo cyclonedx --all --format json --output-file sbom.cdx.json
C) Node.js (web-based SaMD)
For release evidence, focus on runtime dependencies and lockfile-resolved versions.
Tool: @cyclonedx/cyclonedx-npm
npm install -g @cyclonedx/cyclonedx-npm@<pinned-version>
# Capture runtime dependencies
cyclonedx-npm --output-format JSON --output-file sbom.json --omit dev
If your version supports lockfile-only mode, enable it in CI.
D) Python (AI/ML components)
Prefer lockfile-driven environments so outputs are reproducible.
Tool: cyclonedx-python (cyclonedx-bom package)
pip install cyclonedx-bom==<pinned-version>
# Poetry workflow (recommended for deterministic builds)
cyclonedx-py poetry -o sbom.json
E) .NET (clinical enterprise software)
Generate at solution scope to capture cross-project dependencies.
Tool: dotnet-CycloneDX
dotnet tool install --global CycloneDX --version <pinned-version>
dotnet-CycloneDX MyMedicalApp.sln -o ./artifacts -f json
F) Mobile apps (Android and iOS)
Track platform SDK and mobile-library dependencies.
Android (Gradle CycloneDX plugin):
id 'org.cyclonedx.bom' version '1.8.1'
Run:
./gradlew cyclonedxBom
iOS (SwiftPM/CocoaPods):
- Use resolved dependency files (
Package.resolved,Podfile.lock) as the source of truth. - Generate from built app or archive artifacts when possible, not only raw source.
- If using Syft, scan built output and document known detection limits.
# Example: scan built artifact, not just repo root
syft /path/to/MyApp.app -o cyclonedx-json > sbom.json
Cloud and containerized environments
For cloud/SaMD workloads in containers, include OS-layer packages, not only app-level libraries.
Tool: Syft (Anchore)
# Scan immutable release image digest (not mutable tags)
syft <image_name>@sha256:<digest> -o spdx-json > sbom.spdx.json
Post-generation controls (do not skip)
Generating an SBOM file is step one. You still need validation and governance.
- Validate schema and quality fields
# CycloneDX validation example
cyclonedx-cli validate --input-file sbom.json
If you emit SPDX, run SPDX-compatible validation with your selected validator as part of CI.
- Enforce quality gates
Require at least:
- component name and version,
- supplier,
- cryptographic hashes,
- dependency relationships,
- identifiers (
purl, andcpewhere applicable).
- Centralize and monitor
Store SBOMs in a central system (for example, Dependency-Track) for ongoing vulnerability monitoring.
- Handle VEX
If a vulnerability exists in a component but is not reachable in your deployed architecture, document exploitability context in VEX.
Reference table
| Environment | Primary Tool | Target File |
|---|---|---|
| C/C++ | cdxgen | Makefile / CMakeLists.txt |
| Rust | cargo-cyclonedx | Cargo.lock |
| Node.js | cyclonedx-npm | package-lock.json |
| Python | cyclonedx-py | poetry.lock / requirements.txt |
| .NET | dotnet-cyclonedx | .csproj / .sln |
| Java | cyclonedx-maven | pom.xml |
| Go | cyclonedx-go | go.mod |
Manual and proprietary SOUP handling
When components are outside package managers (for example .lib, .dll, or vendor binaries), add them manually.
Capture at minimum:
- Supplier (organization/legal entity)
- Component name and version
- Identifier (
purland/orcpewhere applicable) - SHA-256 (or stronger) hash of shipped artifact
- Dependency scope and usage context
- Source/provenance reference (vendor advisory, release note, or controlled evidence)
Manual component template (manual-soup.json)
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"components": [
{
"bom-ref": "vendor-proprietary-algorithm-4.2.0",
"type": "library",
"supplier": {
"name": "Vendor Name"
},
"publisher": "Vendor Name",
"name": "Proprietary-Algorithm-Library",
"version": "4.2.0",
"purl": "pkg:generic/proprietary-algorithm-library@4.2.0",
"hashes": [
{
"alg": "SHA-256",
"content": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"scope": "required",
"externalReferences": [
{
"type": "website",
"url": "https://vendor.com/docs"
}
]
}
]
}
Then merge manual entries into your generated SBOM using your CycloneDX toolchain for the exact CLI version pinned in CI.
Implementation sequence for medtech teams
Use this rollout order:
- Select canonical SBOM format(s) per release stream (CycloneDX and/or SPDX).
- Generate SBOMs in CI from immutable inputs (lockfiles, image digests, pinned tools).
- Bind SBOM artifact to release artifact/version (build ID, commit SHA, provenance record).
- Produce release/runtime SBOM and retain internal build/toolchain SBOM as needed.
- Add manual SOUP declaration workflow with explicit ownership and review.
- Enforce validation + quality gates + central retention/monitoring.
- Integrate SBOM and VEX outputs into risk management and postmarket response.
Important limits to state internally
- An SBOM is an inventory. It doesn't prove exploitability by itself.
- Regenerate SBOMs on every release, hotfix, and critical dependency change.
- Normalize identifiers consistently (
purlfirst,cpewhere high-confidence).
Final takeaway
Keep SBOM generation operational and traceable.
If your team can show how SBOM artifacts map to release versions, vulnerability review, and risk decisions, reviewers can move through your package faster and postmarket response becomes easier to defend.
Need help with your full cybersecurity submission package? See how CyberSprint works →