Skip to content

The Drift Log · Provenance, licensing and the paper trail

Automated SBOM Diffing to Detect License Drift After Refactors

5 November 2026 · 3 min read · 718 words · established

A wax seal resting on a page of hash digests

Prevent license drift by computing and diffing CycloneDX or SPDX SBOMs directly in CI before pull requests merge.

A developer cleans up a string parser during a scheduled refactor. They replace a buggy in-house regex with a small helper copied from an upstream repository, or they introduce a lightweight utility library to eliminate thirty lines of boilerplate. The unit tests pass. The PR reviewers review the logic, check test coverage, and hit merge.

Three months later, legal reviews an export manifest for enterprise procurement. That small helper was extracted from a project licensed under AGPL-3.0, or an upstream dependency silently changed its license from MIT to a restrictive server-side license during a minor version bump.

This is license drift. When your tracking depends on developer memory or manual spreadsheets, provenance for vendored code is just a folder of hope.

The fix is not to mandate more compliance meetings. The fix is to make your build system compute an SBOM before and after every branch modification, diff the two graphs, and fail the build on unapproved variance.

The mechanics of an automated SBOM diff

A Software Bill of Materials (SBOM) captures your component inventory, package URLs (pURLs), hashes, and declared SPDX licenses at a single point in time.

Running an SBOM generation tool once a quarter does not prevent contamination; it merely records it after the fact. To catch drift before merge, you must treat your SBOM like a schema migration or an API contract.

In your CI pipeline:

  1. Generate the baseline SBOM from the target branch (main or origin/main).
  2. Generate the candidate SBOM from the pull request commit.
  3. Compute an SBOM diff that isolates added, removed, or altered components.
  4. Evaluate every altered record against an allowed SPDX license policy.
## Generate candidate SBOM and diff against base
syft dir:. -o cyclonedx-json=candidate-sbom.json
sbom-diff base-sbom.json candidate-sbom.json --output-format json > diff-report.json

## Parse license drift in CI
jq '.components.added[] | select(.licenses[].expression | test("GPL|AGPL|SSPL"))' diff-report.json

If the diff detects an unapproved license expression or an unverified file modification inside a vendored directory, CI emits a compliance alert and halts the build.

[FAIL] License Drift Detected:
  Component: internal/vendor/fast-uri
  Status: MODIFIED
  Declared License: BSD-3-Clause -> AGPL-3.0-only
  Action: Merge blocked. Licensing must satisfy approved policy invariants.

Why package lockfile diffs fall short

Teams often assume that reviewing package-lock.json, go.sum, or Cargo.lock diffs in pull requests solves this problem. It does not, for two reasons:

First, lockfiles track package manager dependencies, not in-tree files. When an engineer drops an upstream file into src/vendor/ or pastes thirty lines of cryptographic hashing logic from another repository, lockfiles register nothing. A static directory scanner integrated into your SBOM generator will flag the foreign file hash.

Second, transitive license changes are frequently invisible in standard diffs. An existing direct dependency may update a sub-dependency whose license terms changed without altering package names. An automated SBOM diff resolves the full graph down to each transitive node, comparing the computed license expression of the candidate build against the baseline.

If you maintain internal forks or copied sources, you also need to ensure that the origin commits and license terms remain intact across refactors. Enforcing provenance for vendored code requires that an altered file hash triggers an immediate review of its license header and origin metadata.

How SHPBL handles licensing invariants

At SHPBL, every write an agent or developer attempts passes through a Build Intent gate. We do not inspect code after it lands; the license terms and invariants are resolved in code before a terminal state is returned.

The SHPBL library contains model-independent, catalogued components whose provenance and checksums are verified on every release. You can run our free repository evaluation to surface provenance gaps across your existing codebases. For teams building continuous compliance gates, our Practitioner tier ($39/month) runs the full certification gauntlet, while the Complete Master Library ($499) provides permanent ownership of the catalog with complete, repeatable provenance.

What to put in place on Monday

You do not need a multi-month vendor integration to stop license drift. Start with a mechanical check in your default pull request workflow:

  1. Standardise on an SBOM format. Pick CycloneDX JSON or SPDX 2.3 and add a generation step to your standard CI run.
  2. Cache the base SBOM artifact. On every merge to main, emit and store the canonical base-sbom.json.
  3. Run the diff on pull requests. Run a diff step on the PR candidate. If a newly introduced or modified component lacks an explicit SPDX identifier, or if the identifier matches a disallowed license list, fail the check.

License compliance is an invariant, not an audit exercise. Treat it like a failing unit test: break the build before the code lands.

Keep reading

Next in the log

The Strategic Master Library · written and reviewed under the house's own epistemic rules: nothing claimed that we cannot show.