Concept · 9 min read

The dependency version did not change. Why did its hash change?

A manifest diff, an ordinary SBOM component diff, and an integrity-drift check answer three different questions. Treating them as interchangeable leaves a blind spot: a component can keep the same name and version while its bytes change.

The three layers

LayerPrimary questionStrongest signalImportant blind spot
Manifest or lockfile diffWhat dependency resolution did the developer request or record?Direct and resolved version changesDoes not prove what entered the built artifact
Ordinary SBOM component diffWhat components does each build report?Added, removed, and version-changed componentsA same-name, same-version replacement can look unchanged
Hash / integrity driftDid the recorded bytes change without the component version changing?Unexpected same-version hash changeSignals investigation; it does not by itself prove malicious intent

These layers complement one another. sbomlyze does not replace a package manager, an SBOM generator, signature verification, or a vulnerability scanner. It reviews the evidence produced by the build and classifies what changed between two points in time.

1 · Manifest diff: developer intent

- github.com/google/uuid v1.5.0
+ github.com/google/uuid v1.6.0

That is an excellent review surface. But the manifest describes dependency resolution, not necessarily the final artifact. It may omit operating-system packages, copied binaries, vendored files, build-stage tools, generated assets, or components introduced by the container base image.

2 · Ordinary SBOM component diff: build inventory

Comparing two inventories reveals changes a manifest misses: a new transitive package, an OS package added by a base-image update, a component removed from the final artifact, a resolved version that differs from the source declaration, or a license and supplier change.

The naive implementation is a JSON diff, which produces noise from timestamps, serial numbers, generated identifiers, and ordering. sbomlyze matches component identity using stable identifiers when available — Package URLs and CPEs, with format-specific references and names as fallbacks — so it can compare SPDX, CycloneDX, and Syft evidence without identical layouts.

If a package is called example at version 1.4.2 in both SBOMs, a name-and-version diff calls it unchanged — even if the component bytes are different.

3 · Integrity drift: same identity and version, different bytes

Integrity drift is the narrow condition where sbomlyze matches a component in both SBOMs, sees the same version, and finds that its recorded hash set changed. The version says consumers should be receiving the same release, while the hash says the recorded content is not the same. Those two facts disagree, so a reviewer should establish why before merging.

Possible explanations
A package or artifact was replaced without a version bump.
A dependency mirror or registry served different content.
The build is not reproducible and embeds timestamps or environment data.
The SBOM generator changed what bytes it hashes.
Platform, architecture, or build flags changed between the baseline and head.
The trusted baseline was generated from a different stage of the build.

Some explanations are benign. The point of the check is not to label every hash change an attack. It is to prevent a silent mismatch from passing as “no dependency change.”

Make the signal trustworthy

Hash comparison is only as stable as the evidence around it. Before making the check required:

01 Generate baseline and head SBOMs at the same lifecycle stage.
02 Pin the generator and its configuration.
03 Keep operating system, architecture, and build flags comparable.
04 Prefer cryptographic component hashes emitted by the generator; do not synthesise them from names and versions.
05 Investigate one expected rebuild to identify legitimate nondeterminism.
06 Update the baseline only after the change has been reviewed and accepted.

A practical review rule

Manifest changed, component version changed: review the intended upgrade.
Manifest did not change, SBOM components changed: identify the build or transitive source of the inventory change.
Component version did not change, component hash changed: stop and explain the content mismatch before merging.

That last case is small, specific, and easy to miss. Giving it its own policy signal turns an ambiguous SBOM diff into a review decision.

Add the check to CI See the blocked pull request