Apache-2.0 Single Go binary CycloneDX · SPDX · Syft

git diff for your SBOM.

Compare two Software Bills of Materials and see what changed between builds, versions, and releases.

sbomlyze compares component hashes, not only version strings. When an attacker swaps a package without bumping its version, sbomlyze flags it. Generators and vulnerability scanners miss this.

Install sbomlyze Gate a pull request
$ brew install rezmoss/sbomlyze/sbomlyze
sbomlyze baseline.json build.json
Drift Summary:
  Version drift:   58 components
  Integrity drift: 1 component  (hash changed, version did not)
  Metadata drift:  2 components

Key Findings:
  Attack surface: +5 packages (7.0%), +120 files (3.2%)
  2 version downgrades: openssl 3.1.4→3.0.2, curl 8.5.0→8.4.0
  56 upgrades (2 major, 12 minor, 42 patch) of 65 shared
  New ecosystem: golang (8 packages)

~ Changed (58):
  ~ nginx
      version: 1.29.4-r1 -> 1.27.3-r1
  ~ suspicious-pkg  [INTEGRITY]
      hash[SHA256]: abc123 -> def456

!! Policy Violations (1):
  [deny_integrity_drift] hash changed without version change
exit status 1
3
SBOM formats parsed and converted
7
Output formats, from text to SARIF
14
Policy rules for CI gating
26
Minimum-element checks across NTIA, CISA, BSI
The blind spot

Three comparisons. Three different questions.

A component can keep the same name and version while its bytes change. Treating these layers as interchangeable leaves that case unreviewed.

Layer 01
Manifest or lockfile diff
What dependency resolution did the developer request?
Blind spot: does not prove what entered the built artifact.
Layer 02
Ordinary SBOM component diff
What components does each build report?
Blind spot: a same-name, same-version replacement looks unchanged.
Layer 03 · sbomlyze
Hash / integrity drift
Did the recorded bytes change without the component version changing?
Signals investigation; it does not by itself prove malicious intent.
component: pkg:golang/example/project@1.4.2
version:   1.4.2 -> 1.4.2
hash:      c4e3...aa89 -> 345c...fa36
verdict:   integrity drift
Read the full analysis →
Four surfaces, one binary

Review in the terminal, the browser, or the pull request.

sbomlyze detecting tamper drift in a pull request
GitHub Action
Compares the head SBOM against a git, release, artifact, URL, or file baseline. Writes a Job Summary, enforces policy, uploads SARIF, maintains one PR comment.
uses: rezmoss/sbomlyze@v0.5.3
sbomlyze web UI showing components, details, and statistics panes
Web UI explorer
Drag-and-drop up to 500 MB. Dependency tree, deep search across raw JSON, statistics dashboard, and a full filesystem browser with layer filtering.
sbomlyze -web --port 8080
sbomlyze interactive terminal explorer searching and inspecting components
Terminal explorer (TUI)
Keyboard navigation, deep search with /, package-type filters, component detail, and raw JSON with syntax highlighting.
sbomlyze image.json -i
Pipe it anywhere
Read either side of a diff from standard input. Run it right after your generator, with no temporary file.
syft image:tag -o cyclonedx-json \
  | sbomlyze - --compliance

syft image:tag -o cyclonedx-json \
  | sbomlyze baseline.json -

sbomlyze convert cdx.json --to spdx
sbomlyze before.json after.json --format sarif
Where it fits

Many tools generate SBOMs. Few compare them.

Generators make SBOMs and scanners find CVEs. sbomlyze tells you what changed between two SBOMs and whether to trust it.

Capabilitysbomlyzecyclonedx-clisbomqssyft / trivy
SBOM-to-SBOM diffyesbasic
Integrity / tamper drift (hash changed without version)yes
Dependency-graph diff + transitive depth riskyes
NTIA / CISA / BSI compliance scoringyesyes
Format conversion (Syft / CycloneDX / SPDX)yesyespartial
TUI + Web UI explorersyes
Policy gate + SARIF / JUnit / Markdown / HTML / Patchyespartialpartialpartial
Capabilities

What it does with two documents.

01
Drift classification
Version drift, integrity drift, and metadata drift are separated, not merged into one changed count.
02
Dependency graph diff
Added and removed edges, new transitive reachability, the exact path to each new dependency, and its depth.
03
Depth risk summary
Depth 1 is a choice you made. Depth 3+ is flagged for careful review.
04
Strong identity matching
PURL → CPE → BOM-ref → namespace/name precedence, so cross-format diffs stay meaningful.
05
Compliance scoring
NTIA, CISA 2025, and BSI TR-03183 minimum elements with per-check pass/fail detail.
06
Duplicate & collision detection
Multiple versions of one package, name mismatches, and same-version hash conflicts.
07
Format conversion
Any input format to CycloneDX 1.5, SPDX 2.3, or Syft JSON, preserving PURLs, hashes, and relationships.
08
Statistics mode
One document in, auto-detected key findings out: ecosystem dominance, licence profile, coverage warnings.
09
Tolerant parsing
Continue on malformed input with structured warnings, or use --strict to fail closed.
Output

One diff, seven ways to read it.

Every format is produced from the same analysis, so the terminal, the code-scanning alert, and the report you email to an auditor never disagree.

text json sarif junit markdown html patch
.github/sbom-policy.json
{
  "deny_integrity_drift": true,
  "deny_licenses": ["GPL-3.0", "AGPL-3.0"],
  "require_licenses": true,
  "max_depth": 3,
  "min_overall_compliance": 80
}

Reproduce the blocked pull request.

Three public demo repositories carry five reproducible review scenarios each, with public workflow runs and SARIF evidence.