Skip to main content

Crate sbom_diff

Crate sbom_diff 

Source
Expand description

§sbom-diff

diff engine and cli for sbom comparison.

compares two software bills of materials and reports added, removed, and changed components. supports both cyclonedx and spdx formats.

§cli usage

# compare two sboms (auto-detects format)
sbom-diff old.json new.json

# read old sbom from stdin
cat old.json | sbom-diff - new.json

# markdown output for pr comments
sbom-diff old.json new.json -o markdown

# json output for tooling
sbom-diff old.json new.json -o json

# filter to specific fields
sbom-diff old.json new.json --only version,license

# license gating
sbom-diff old.json new.json --deny-license GPL-3.0-only
sbom-diff old.json new.json --allow-license MIT --allow-license Apache-2.0

§exit codes

codemeaning
0success (diff computed, no license violations)
1error (parse failure, file not found, etc.)
2license violation (when using --deny-license or --allow-license)

see the project readme for full cli documentation.

§library usage

use the Differ struct directly to integrate into your own tools:

use sbom_diff::{Differ, Diff, FieldChange, Field};
use sbom_model::Sbom;

fn compare(old: &Sbom, new: &Sbom) -> Diff {
    // compare all fields
    let diff = Differ::diff(old, new, None);

    // or filter to specific fields
    let diff = Differ::diff(old, new, Some(&[Field::Version, Field::License]));

    println!("added: {}", diff.added.len());
    println!("removed: {}", diff.removed.len());
    println!("changed: {}", diff.changed.len());

    for change in &diff.changed {
        println!("{}:", change.id);
        for field in &change.changes {
            match field {
                FieldChange::Version(old, new) => {
                    println!("  version: {} -> {}", old, new);
                }
                FieldChange::License(old, new) => {
                    println!("  license: {:?} -> {:?}", old, new);
                }
                _ => {}
            }
        }
    }

    diff
}

§renderers

built-in renderers for common output formats:

use sbom_diff::{Diff, renderer::{Renderer, TextRenderer, MarkdownRenderer, JsonRenderer}};
use std::io::stdout;

fn render(diff: &Diff) -> anyhow::Result<()> {
    let mut out = stdout().lock();

    // plain text (default)
    TextRenderer.render(diff, &mut out)?;

    // markdown with collapsible sections
    MarkdownRenderer.render(diff, &mut out)?;

    // json for machine consumption
    JsonRenderer.render(diff, &mut out)?;

    Ok(())
}

§how matching works

components are matched in two passes:

  1. by id: components with the same ComponentId (usually purl) are paired
  2. by identity: unmatched components are reconciled by name + ecosystem

this allows detecting version bumps even when the purl changes (e.g., pkg:npm/foo@1.0 vs pkg:npm/foo@2.0).

Modules§

renderer
Output renderers for displaying SBOM diffs.

Structs§

ComponentChange
A component that exists in both SBOMs with detected changes.
Diff
The result of comparing two SBOMs.
Differ
SBOM comparison engine.

Enums§

Field
Fields that can be compared and filtered.
FieldChange
A specific field that changed between two versions of a component.