← Back to License Drift Check

How a dependency's license change reaches your lockfile

Nobody chooses to accept a relicense. It arrives as a version bump: an automated PR from Dependabot or Renovate, or a routine npm update, carrying release notes about performance and bug fixes — and, unannounced in the diff a reviewer actually reads, a different license field in the new version's own package.json.

Why Dependabot and Renovate don't catch it

Both tools solve a different problem well: they watch a registry for a newer version and open a pull request with the version delta, changelog, and a compatibility signal. Neither diffs the license field between the version you have and the version they're proposing — it isn't part of what either was built to compare. A license change rides along inside an otherwise-routine version PR with nothing calling it out.

Four times this actually happened

ProjectFromToDate
MongoDB ServerAGPL-3.0SSPL-1.02018-10-16
Elasticsearch & KibanaApache-2.0SSPL-1.0 / Elastic License2021-01-14
TerraformMPL-2.0BUSL-1.12023-08-10
RedisBSD-3-ClauseSSPLv1 / RSALv22024-03-20

Each of these is a widely used dependency, not an obscure one. The Terraform change alone drove the OpenTF Manifesto to more than 33,000 GitHub stars within a month and produced OpenTofu; the Redis change produced Valkey, forked and backed by the Linux Foundation within days. None of that visibility retroactively protected the projects that had already merged the version bump before anyone noticed.

Generating a snapshot per ecosystem

Node / npm — reads each installed package's own package.json, no network call:

node cli.mjs snapshot --out baseline.json

Python — pip-licenses already emits JSON; this reshapes its field names to match:

pip-licenses --format=json | \
  jq '[.[] | {name: .Name, version: .Version, license: .License}]' \
  > baseline.json

Go — go-licenses emits a header-less import path,license URL,license name CSV, which the tool reads directly (the last column is used as the license):

go-licenses csv ./... > baseline.csv

Rust — cargo-license's JSON already uses name/version/license:

cargo license --json > baseline.json

Wiring it into CI as a gate

Commit a reviewed baseline snapshot once, then compare against it on every PR that touches the lockfile:

- name: Check for dependency license drift
  run: |
    npm ci
    node ventures/license-drift-check/cli.mjs snapshot --out /tmp/current.json
    node ventures/license-drift-check/cli.mjs diff license-baseline.json /tmp/current.json

The command exits non-zero the moment any package's license changed since the baseline, and 0 on a clean run or a change you've explicitly accepted with --ignore. Nothing here decides whether a given change is acceptable — it makes the change visible in the one place code review already happens, instead of silent inside a version bump.

What the family label does and doesn't mean

Each license is bucketed into permissive, copyleft, or source-available by matching well-known keywords in the license string — a skim aid, not a legal determination. The one mix-up worth naming explicitly: colloquial "BSL" almost always means the source-available Business Source License (SPDX BUSL-1.1, the one Terraform, Sentry, and CockroachDB use) — not the fully permissive Boost Software License (SPDX BSL-1.0). The tool disambiguates on the version digit; read the actual license text before relying on either label.

Common questions

Is colloquial "BSL" the same license as SPDX BSL-1.0?

No, and mixing them up runs in both directions. Colloquial "BSL" almost always means the source-available Business Source License (SPDX BUSL-1.1), the one Terraform, Sentry, and CockroachDB use. SPDX BSL-1.0 is the unrelated, fully permissive Boost Software License.

Does this tool decide whether a license change is compliant or safe to accept?

No. It reports that a package's license string differs from a snapshot you captured earlier, and buckets the before and after into a public, well-known family as a skim aid. Whether a given change is acceptable for your project is a decision for you or your counsel, not this tool.

Can I use this if my project isn't on npm?

Yes. The diff engine reads a generic JSON array of {name, version, license} objects, or CSV with a name/version/license header, so pip-licenses, go-licenses, and cargo-license output all work with the commands above — only the snapshot command that scans node_modules directly is npm-specific.