Documents
SBOM Generation And Vulnerability Scanning
SBOM Generation And Vulnerability Scanning
Type
Topic
Status
Published
Created
Mar 3, 2026
Updated
Mar 3, 2026
Created by
Dosu Bot
Updated by
Dosu Bot

SBOM Generation and Vulnerability Scanning#

Software Bill of Materials (SBOM) generation and vulnerability scanning form a critical security architecture for Rust projects. An SBOM provides a machine-readable inventory of all software components and dependencies, typically in CycloneDX or SPDX format. When paired with automated vulnerability scanning, SBOMs enable continuous monitoring of supply chain security risks and compliance verification. For Rust projects, the choice of SBOM generation tooling directly impacts scan accuracy and operational effectiveness.

The architectural decision to use cargo-cyclonedx over generic scanners like Syft represents a durable approach to avoiding false positives in Rust projects. Syft, Anchore's general-purpose filesystem scanner, produces false positives by scanning all Cargo.lock files it discovers, including stale versions in build artifact directories like megalinter-reports/updated_sources/. In the gold_digger project, Syft flagged vulnerable versions of time@0.3.43, bytes@1.10.1, lru@0.12.5, and tokio-tar@0.3.1 that had already been updated in the actual project Cargo.lock to time@0.3.47, bytes@1.11.1, lru@0.16.3, and astral-tokio-tar@0.5.6. Syft also picked up pip vulnerabilities from mise's Python virtual environment. cargo-cyclonedx correctly produced zero false positives because it reads only the project's canonical Cargo.lock file, understanding Rust's dependency resolution semantics.

This article documents two complementary approaches: the cargo-cyclonedx + Grype pattern for cross-language vulnerability scanning with SARIF upload to GitHub Security tab, and the Rust-native cargo-audit + cargo-deny approach. Both strategies provide value in different contexts, with the former offering multi-ecosystem coverage and GitHub integration, while the latter focuses on Rust-specific tooling and policy enforcement.

SBOM Generation Strategies for Rust Projects#

cargo-cyclonedx Approach#

cargo-cyclonedx is a Rust-native tool that reads dependency information directly from Cargo.lock files, understanding Cargo's dependency resolution semantics. The tool generates CycloneDX format SBOMs that accurately represent the project's actual dependency tree. In the libmagic-rs project, cargo-cyclonedx is enabled in the dist-workspace.toml configuration and integrated into the cargo-dist release workflow, automatically generating SBOMs for each release artifact.

The tool reads Cargo's dependency resolution including features, platform-specific dependencies, and version constraints. This semantic understanding prevents false positives that occur when generic scanners process Rust lock files without comprehending the relationship between workspace members, optional dependencies, and build-time versus runtime requirements.

Syft Limitations for Rust Projects#

Syft is a general-purpose filesystem scanner that examines all files matching specific patterns. For Rust projects, this broad approach creates false positives because Syft scans every Cargo.lock file it discovers, including stale versions in build artifact directories, test fixtures, and tool-installed dependencies.

In the gold_digger project, Syft scanned the megalinter-reports/updated_sources/ directory and flagged outdated dependency versions that no longer existed in the project's actual dependency tree. The scanner also detected pip vulnerabilities from mise's Python virtual environment, which had no relevance to the Rust project's security posture. Generic scanners lack the semantic knowledge to distinguish between a project's canonical Cargo.lock and artifacts from build tools, linters, or development utilities.

Why Language-Specific Tooling Matters#

Rust dependency resolution implements complex semantics around features, workspace dependencies, target-specific requirements, and version constraints. Generic filesystem scanners treat Cargo.lock as an opaque data file, missing critical context about which dependencies actually compile into the final artifact.

False positives erode trust in security tooling and create alert fatigue. When developers repeatedly investigate phantom vulnerabilities that don't exist in the actual build, they become desensitized to security warnings. Language-specific tooling like cargo-cyclonedx eliminates this category of false positives by using the same dependency resolution logic as the Rust compiler itself.

Vulnerability Scanning Approaches#

Grype Integration Pattern#

Grype is Anchore's vulnerability scanner that consumes SBOM files from multiple formats including CycloneDX. The tool queries vulnerability databases across multiple ecosystems, providing cross-language coverage beyond Rust-specific sources like the RustSec Advisory Database.

The gold_digger project implements the cargo-cyclonedx + Grype workflow pattern:

# Step 1: Generate SBOM from Cargo.lock
cargo cyclonedx --format json

# Step 2: Scan SBOM with Grype
grype sbom:gold_digger.cdx.json --fail-on critical -o sarif

This two-step process separates SBOM generation from vulnerability scanning. The generated CycloneDX SBOM (gold_digger.cdx.json) serves as input to Grype, which outputs results in SARIF format for upload to GitHub Security tab. The --fail-on critical flag causes the scanner to exit with a non-zero status when critical vulnerabilities are detected, failing the CI build.

Rust-Native Alternative Approach#

The libmagic-rs project uses Rust-native vulnerability scanning tools instead of Grype. This approach provides two complementary security checks:

cargo-audit scans dependencies against the RustSec Advisory Database, a curated collection of security advisories for Rust crates. The tool runs daily at midnight UTC and automatically creates GitHub issues for discovered vulnerabilities.

cargo-deny enforces dependency policies beyond vulnerability scanning, including license compliance verification, banned crate detection, and source repository validation. The tool runs daily at 6 AM UTC to catch policy violations.

The trade-off between these approaches balances ecosystem coverage against integration depth. Grype provides multi-language vulnerability detection valuable for projects with diverse dependencies, while cargo-audit and cargo-deny offer deeper integration with Rust ecosystem practices and tooling.

SARIF Upload to GitHub Security Tab#

SARIF Format Overview#

Static Analysis Results Interchange Format (SARIF) is an OASIS standard for representing static analysis tool output. The JSON-based format provides a unified structure for security findings, enabling consistent presentation across different analysis tools in GitHub's Code Scanning dashboard.

SARIF files contain structured information about detected issues including severity levels, affected code locations, remediation guidance, and tool-specific metadata. This standardization allows GitHub Security tab to aggregate findings from multiple scanners—CodeQL, Grype, Semgrep, and others—into a single interface.

GitHub Security Integration#

Uploading SARIF results to GitHub Security tab requires security-events: write permission in the workflow file. The github/codeql-action/upload-sarif action handles the upload process, processing SARIF files and creating security alerts visible to repository maintainers.

In the gold_digger workflow, Grype generates SARIF output with the -o sarif flag. The workflow then uploads this file to GitHub Security tab, where findings appear alongside results from other security scanners. The libmagic-rs project demonstrates SARIF upload with OpenSSF Scorecard, which generates SARIF format results for weekly supply-chain security assessments.

Workflow Implementation Example#

- name: Run Grype scan
  run: grype sbom:project.cdx.json --fail-on critical -o sarif > results.sarif

- name: Upload SARIF to GitHub Security
  uses: github/codeql-action/upload-sarif@v4
  if: always()
  with:
    sarif_file: results.sarif

The if: always() condition ensures SARIF upload occurs even when the scan step fails, making vulnerability findings visible in GitHub Security tab regardless of CI status.

Architectural Decision Context#

When to Choose cargo-cyclonedx + Grype#

This pattern suits Rust projects with complex build pipelines that generate multiple Cargo.lock artifacts in subdirectories, test fixtures, or build output directories. The explicit separation between SBOM generation (cargo-cyclonedx) and vulnerability scanning (Grype) prevents the false positives that occur when scanners like Syft examine all lock files indiscriminately.

Organizations requiring cross-language vulnerability scanning benefit from Grype's multi-ecosystem database coverage. Projects with C bindings, JavaScript build tools, or Python utilities can scan all dependencies through a single tool, consolidating vulnerability management across language boundaries.

The SARIF output format integrates directly with GitHub Security tab, providing centralized visibility into security findings. The gold_digger project adopted this pattern explicitly to eliminate false positives while maintaining CI integration, representing a durable architectural decision about security tooling rather than a temporary workaround.

When Rust-Native Tools May Suffice#

Projects with a single canonical Cargo.lock file and minimal build complexity can use cargo-audit and cargo-deny effectively. These tools integrate deeply with Cargo's dependency resolution, understanding workspace semantics and optional dependencies without external SBOM generation.

The RustSec Advisory Database maintained by the Rust community receives rapid updates for Rust ecosystem vulnerabilities. cargo-deny provides policy enforcement beyond vulnerability scanning, including license compliance verification, banned crate detection, and source repository validation. The libmagic-rs project demonstrates this approach, running daily automated security checks without external scanning tools.

The trade-off centers on ecosystem breadth versus integration depth. Projects exclusively using Rust dependencies gain little from cross-language scanners, while polyglot codebases require broader coverage than Rust-specific tools provide.

Layered Defense Strategy#

Both approaches complement each other in comprehensive security programs. SBOM generation via cargo-cyclonedx provides value regardless of scanner choice, enabling post-deployment vulnerability scanning and supply chain audit trails. SBOMs included in release artifacts allow downstream consumers to audit dependencies without source code access.

Running multiple scanning approaches catches different vulnerability classes. RustSec Advisory Database receives community contributions focused on Rust-specific issues, while broader databases like Grype's include vulnerabilities across ecosystems. Policy enforcement tools like cargo-deny catch compliance issues that vulnerability scanners miss entirely, such as license violations or banned dependency usage.

Supply Chain Security Integration#

Build Provenance and SBOM Distribution#

SBOM artifacts are included in release packages alongside binaries and source archives. This distribution model enables downstream consumers to audit dependencies without requiring access to build environments or source repositories. Organizations can scan received SBOMs against their internal vulnerability databases, verifying that software supply chains meet security requirements before deployment.

Sigstore attestations provide cryptographic verification of SBOM authenticity. The libmagic-rs project signs SBOMs using keyless signing, binding them to the GitHub Actions workflow identity that generated them. Consumers can verify that SBOMs originated from official release builds rather than tampered versions.

Post-Deployment Scanning#

cargo-auditable embeds dependency metadata directly into compiled binaries, enabling post-deployment vulnerability scanning without source code access:

cargo audit bin rmagic

This capability supports security operations teams scanning production deployments where source code or build artifacts are unavailable. The embedded metadata provides the same dependency information as build-time SBOMs, allowing cargo audit to check deployed binaries against the RustSec Advisory Database.

SBOM files enable external scanning tools like Grype to perform post-release vulnerability assessment. Organizations receiving software packages can scan included SBOMs with their preferred tools, independent of the vendor's scanning choices. This separation between SBOM generation and vulnerability scanning supports diverse security tooling strategies across software supply chains.

Continuous Security Monitoring#

The gold_digger project runs Grype in CI on every commit and pull request, providing immediate feedback on newly introduced vulnerabilities. The libmagic-rs project implements a multi-schedule security workflow: daily cargo-audit at midnight UTC checks for new advisories in existing dependencies, while daily cargo-deny at 6 AM UTC enforces policy compliance.

This multi-schedule approach balances comprehensive coverage with CI resource usage. Scheduled workflows detect vulnerabilities disclosed after code merges, catching security issues that were not present during initial CI checks. The staggered timing prevents all security checks from running simultaneously, distributing computational load across the day.

Usage and Examples#

Basic SBOM Generation#

Install and run cargo-cyclonedx to generate a CycloneDX SBOM for the current project:

# Install cargo-cyclonedx
cargo install cargo-cyclonedx

# Generate SBOM in JSON format
cargo cyclonedx --format json

# Output: Creates bom.json or <project_name>.cdx.json

The generated SBOM contains dependency information including package names, versions, licenses, and dependency relationships. This machine-readable format supports automated vulnerability scanning and compliance verification.

Grype Vulnerability Scanning#

Scan a generated SBOM file with Grype to detect known vulnerabilities:

# Basic SBOM scan
grype sbom:gold_digger.cdx.json

# Fail CI on critical vulnerabilities with SARIF output
grype sbom:gold_digger.cdx.json --fail-on critical -o sarif > grype-results.sarif

The --fail-on critical flag causes Grype to exit with status code 1 when critical vulnerabilities are detected, failing the CI build. The -o sarif option generates output compatible with GitHub Security tab upload.

CI Workflow Integration#

Complete workflow implementing cargo-cyclonedx + Grype scanning with GitHub Security integration:

name: Security Scanning
on: [push, pull_request]

jobs:
  vulnerability-scan:
    runs-on: ubuntu-latest
    permissions:
      security-events: write
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Install cargo-cyclonedx
        run: cargo install cargo-cyclonedx

      - name: Generate SBOM
        run: cargo cyclonedx --format json

      - name: Install Grype
        uses: anchore/scan-action/download-grype@v4

      - name: Scan with Grype
        run: grype sbom:project_name.cdx.json --fail-on critical -o sarif > grype-results.sarif

      - name: Upload SARIF to GitHub Security
        uses: github/codeql-action/upload-sarif@v4
        if: always()
        with:
          sarif_file: grype-results.sarif

The if: always() condition ensures SARIF upload occurs even when the scan fails, making findings visible in GitHub Security tab.

Rust-Native Security Scanning#

Alternative workflow using cargo-audit for Rust-specific vulnerability detection:

name: Security Audit
on:
  schedule:
    - cron: "0 0 * * *" # Daily at midnight UTC
  push:

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Run cargo audit
        uses: rustsec/audit-check@v2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

This workflow runs daily to catch newly disclosed vulnerabilities in existing dependencies, automatically creating GitHub issues for discovered advisories.

Grype Configuration#

Create a .grype.yaml configuration file to customize Grype behavior:

# Fail CI on critical vulnerabilities
fail-on: critical

# Output format for GitHub Security integration
output: sarif

# Scan all layers (relevant for container images)
scope: all-layers

# Exclude specific vulnerabilities (use sparingly)
ignore:
  - vulnerability: CVE-2023-12345
    reason: "False positive - dependency not used in production"

Configuration files provide consistent scanning behavior across different environments and team members.

Relevant Code Files#

gold_digger Repository (Grype Pattern)#

The gold_digger project implements the cargo-cyclonedx + Grype vulnerability scanning pattern. Key files include:

FileDescription
.github/workflows/security.ymlCI workflow implementing cargo-cyclonedx SBOM generation and Grype scanning
.grype.yamlGrype vulnerability scanner configuration file
mise.tomlTool version management (context for false positive scenario with Python venv)
Cargo.lockProject dependency lock file (canonical source of dependency truth)

libmagic-rs Repository (Rust-Native Pattern)#

The libmagic-rs project demonstrates the Rust-native security scanning approach using cargo-audit and cargo-deny:

FileDescriptionURL
dist-workspace.tomlcargo-dist configuration enabling cargo-cyclonedx SBOM generationView
.github/workflows/audit.ymlDaily cargo-audit vulnerability scanning workflowView
.github/workflows/security.ymlDaily cargo-deny policy enforcement workflowView
.github/workflows/scorecard.ymlWeekly OpenSSF Scorecard with SARIF uploadView
deny.tomlcargo-deny policy configuration for dependency managementView
docs/src/release-verification.mdDocumentation on SBOM generation and verificationView
docs/src/security-assurance.mdComprehensive security assurance caseView

Complementary Security Practices#

Dependency Policy Enforcement: cargo-deny enforces policies beyond vulnerability scanning, including license compliance verification, banned crate detection, and source repository validation. The tool catches compliance issues that vulnerability scanners miss entirely.

Supply Chain Attestation: Sigstore keyless signing and SLSA provenance provide cryptographic verification of build artifacts. These attestations bind SBOMs to the workflow identity that generated them, preventing tampering and establishing trust in software supply chains.

Binary Scanning: cargo-auditable embeds dependency metadata directly into compiled binaries, enabling post-deployment vulnerability scanning without source code access. This capability supports security operations teams auditing production deployments.

Automated Dependency Updates: Dependabot and Renovate automatically create pull requests for dependency updates, reducing the window of exposure to known vulnerabilities. These tools integrate with vulnerability scanning workflows to prioritize security-related updates.

Ecosystem Tooling Alternatives#

SBOM Generators: Syft provides multi-language scanning but produces false positives for Rust projects. cargo-sbom and cyclonedx-rust-cargo offer alternative Rust-native SBOM generation approaches with different feature sets.

Vulnerability Scanners: cargo-audit queries the RustSec Advisory Database for Rust-specific vulnerabilities. Trivy and Snyk provide multi-language scanning similar to Grype, with different database coverage and integration options.

Policy Enforcement: cargo-vet implements supply chain auditing through manual code review records. cargo-geiger detects unsafe code usage, complementing vulnerability scanning with memory safety analysis.

Standards and Formats#

SBOM Standards: CycloneDX and SPDX (Software Package Data Exchange) represent competing standards for bill of materials representation. CycloneDX focuses on security use cases, while SPDX emphasizes license compliance.

SARIF: Static Analysis Results Interchange Format enables unified security reporting across different analysis tools. The OASIS standard supports GitHub Security tab integration and other security platforms.

SLSA: Supply-chain Levels for Software Artifacts framework defines requirements for build integrity and provenance. SLSA attestations provide cryptographic evidence that artifacts were built according to defined security practices.