Source vs Build SBOMs
Rust has excellent support for reproducible builds through Cargo, its package manager. The Cargo.lock file provides precise dependency information, making source SBOMs highly accurate.
The Rust ecosystem’s security focus means:
- Crates are immutable once published
Cargo.lockcontains exact versions and checksums- Reproducible builds are a first-class concern
For source SBOMs, you generate from Cargo.lock. For build SBOMs, you analyze the compiled binary (which embeds dependency information in debug builds).
Lockfile Deep Dive
Cargo.lock Structure
The Cargo.lock file contains precise dependency information:
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "actix-web"
version = "4.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e43428f1a9b00f007c853a2adf1e46700a2d81e1d498299a9d5198d417c8e030"
dependencies = [
"actix-codec",
"actix-http",
"actix-router",
]
[[package]]
name = "serde"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
Each package entry includes:
- name: Crate name
- version: Exact semantic version
- source: Where the crate comes from (usually crates.io)
- checksum: SHA-256 hash for integrity verification
- dependencies: Direct dependencies of this crate
Cargo.toml vs Cargo.lock
| File | Purpose | Commit to VCS? |
|---|---|---|
Cargo.toml | Dependency declarations (can use ranges) | Always |
Cargo.lock | Exact resolved versions | Yes for binaries/apps, Optional for libraries |
Important for SBOMs: Always use Cargo.lock for SBOM generation, as it contains the resolved versions.
Workspace Dependencies
Rust workspaces allow sharing dependencies across multiple crates:
my-project/
├── Cargo.toml # Workspace root
├── Cargo.lock # Shared lockfile
├── app/
│ └── Cargo.toml
├── lib-core/
│ └── Cargo.toml
└── lib-utils/
└── Cargo.toml
Workspace root Cargo.toml:
[workspace]
members = ["app", "lib-core", "lib-utils"]
[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.0", features = ["full"] }
Member crate Cargo.toml:
[package]
name = "app"
version = "0.1.0"
[dependencies]
serde = { workspace = true }
tokio = { workspace = true }
lib-core = { path = "../lib-core" }
For workspace SBOMs, generate from the root Cargo.lock which covers all members.
Build vs Runtime Dependencies
Cargo distinguishes between dependency types:
[dependencies]
serde = "1.0" # Runtime dependency
[dev-dependencies]
mockall = "0.12" # Test-only dependency
[build-dependencies]
cc = "1.0" # Build script dependency
SBOM considerations:
[dependencies]- Should always be in your SBOM[dev-dependencies]- Usually excluded from production SBOMs[build-dependencies]- Consider including if build-time supply chain matters
Generating an SBOM
SBOM generation is the first step in the SBOM lifecycle. After generation, you typically need to enrich your SBOM with package metadata and augment it with your organization’s details.
Using sbomify GitHub Action (Recommended)
The sbomify GitHub Action is a swiss army knife for SBOMs that automatically selects the best generation tool for your ecosystem, enriches the output with package metadata, and optionally augments it with your business information—all in one step.
For Rust, sbomify uses cdxgen under the hood with fallback to Trivy and Syft.
Standalone (no account needed):
- uses: sbomify/github-action@master
env:
LOCK_FILE: Cargo.lock
OUTPUT_FILE: sbom.cdx.json
COMPONENT_NAME: my-rust-app
COMPONENT_VERSION: ${{ github.ref_name }}
ENRICH: true
UPLOAD: false
Using github.ref_name automatically captures your git tag (e.g., v1.2.3) as the SBOM version. For rolling releases without tags, use github.sha instead. See our SBOM versioning guide for best practices.
With sbomify platform (adds augmentation and upload):
- uses: sbomify/github-action@master
env:
TOKEN: ${{ secrets.SBOMIFY_TOKEN }}
COMPONENT_ID: my-component-id
LOCK_FILE: Cargo.lock
OUTPUT_FILE: sbom.cdx.json
AUGMENT: true
ENRICH: true
Alternative Tools
If you prefer to run SBOM generation tools manually:
cargo-sbom (Rust-native):
cargo install cargo-sbom
cargo sbom > sbom.cdx.json
cargo-cyclonedx:
cargo install cargo-cyclonedx
cargo cyclonedx --format json > sbom.cdx.json
cdxgen:
npm install -g @cyclonedx/cdxgen
cdxgen -t rust -o sbom.cdx.json
Trivy:
trivy fs --format cyclonedx --output sbom.cdx.json .
When using these tools directly, you’ll need to handle enrichment and augmentation separately.
GitLab CI
generate-sbom:
image: sbomifyhub/sbomify-action
variables:
LOCK_FILE: Cargo.lock
OUTPUT_FILE: sbom.cdx.json
UPLOAD: "false"
ENRICH: "true"
script:
- /sbomify.sh
artifacts:
paths:
- sbom.cdx.json
Handling Special Cases
Git Dependencies
Cargo supports dependencies from Git:
[dependencies]
my-crate = { git = "https://github.com/example/my-crate", rev = "abc123" }
These appear in Cargo.lock with Git-specific information:
[[package]]
name = "my-crate"
version = "0.1.0"
source = "git+https://github.com/example/my-crate?rev=abc123#abc123def456"
SBOM tools should capture the Git commit hash as the version identifier.
Path Dependencies
Local path dependencies won’t have version information from crates.io:
[dependencies]
my-local-crate = { path = "../my-local-crate" }
These are useful for workspaces but require special handling in SBOMs.
Features
Cargo features affect what code is compiled:
[dependencies]
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"], default-features = false }
Features can enable additional dependencies, so your SBOM should reflect the features used.
Vendored Dependencies
For offline builds or additional supply chain control:
cargo vendor
This creates a vendor/ directory. Your .cargo/config.toml:
[source.crates-io]
replace-with = "vendored-sources"
[source.vendored-sources]
directory = "vendor"
Best Practices
- Always commit Cargo.lock - Essential for reproducible builds and accurate SBOMs
- Use exact versions in production - Pin versions in
Cargo.tomlfor applications - Audit regularly - Use
cargo auditalongside SBOM generation - Review dependencies - Use
cargo denyfor license and security policies - Consider vendoring - For high-security environments
- Document unsafe code - Note any crates using
unsafein your SBOM metadata
Security Tooling Integration
Rust’s security ecosystem complements SBOM generation:
# Install cargo-audit
cargo install cargo-audit
# Check for vulnerabilities
cargo audit
# Use RustSec advisory database
cargo audit --db ./advisory-db
Combine cargo audit results with your SBOM for comprehensive vulnerability tracking.
Further Resources
For more SBOM tools and resources, see our SBOM Resources page, which includes additional Rust-specific tools like CycloneDX Rust, sbom-rs, and sbom4rust.