You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The parser already extracts a license for every component, stores it on Component.license, and then diff()silently discards it. For a package whose keywords include supply-chain-security and vulnerability-management, a dependency quietly changing its license (e.g. MIT → AGPL‑3.0, or any permissive → copyleft transition) is a material legal/compliance risk — and the tool already has all the data needed to surface it, but doesn't.
This proposes adding license-change detection to the diff output. It's a natural, low-risk extension of the existing model that turns already-parsed-but-unused data into a real signal.
Evidence (current state)
The data is parsed on both code paths but never used downstream:
src/types.ts — Component.license?: string is defined (types.ts:19) but ChangeReport has no license field (types.ts:70).
src/diff.ts — diff() compares version for upgrades but never reads .license (diff.ts:23).
src/reporter.ts — no license column/section in any of text / json / markdown.
So a component that stays at the same version but changes its declared license produces zero output today.
Proposed change
When a component exists in both SBOMs (matched by the same key diff() already uses) and its license differs, record it as a license change.
1. Types (src/types.ts)
exportinterfaceLicenseChange{component: Component;from?: string;// license in the old SBOM (undefined if previously unset)to?: string;// license in the new SBOM}exportinterfaceChangeReport{// ...existing fields...licenseChanges: LicenseChange[];summary: {// ...existing counts...totalLicenseChanges: number;};}
2. Diff (src/diff.ts)
In the existing matched-component branch (the same place upgrades are detected), add:
This reuses the same component map / matching logic, so it composes with whatever key strategy diff() settles on (note PR #8 is reworking the purl key — this slots in cleanly either way).
3. Reporter (src/reporter.ts)
Add a "License Changes" section to text and markdown (mirroring the existing Upgraded section). json is automatic since it serializes the full report. Example markdown:
## ⚖️ License Changes
| Component | From | To |
|-----------|------|----|
| left-pad | MIT | AGPL-3.0 |
4. Tests (src/__tests__/diff.test.ts)
same version, license MIT → AGPL-3.0 ⇒ one licenseChanges entry
license unchanged ⇒ none
license newly added (undefined → MIT) ⇒ one entry (or document that this is intentionally reported)
Why this is high-leverage
Aligned with the stated purpose: the README targets "security engineers, DevSecOps teams, and supply-chain risk analysts … for compliance evidence." License drift is squarely in that remit.
Zero new parsing / dependencies: the field is already extracted on both formats; this only wires existing data into the diff + report.
Summary
The parser already extracts a
licensefor every component, stores it onComponent.license, and thendiff()silently discards it. For a package whosekeywordsincludesupply-chain-securityandvulnerability-management, a dependency quietly changing its license (e.g. MIT → AGPL‑3.0, or any permissive → copyleft transition) is a material legal/compliance risk — and the tool already has all the data needed to surface it, but doesn't.This proposes adding license-change detection to the diff output. It's a natural, low-risk extension of the existing model that turns already-parsed-but-unused data into a real signal.
Evidence (current state)
The data is parsed on both code paths but never used downstream:
src/parser.ts— CycloneDX:extractCycloneDXLicense(c)populatesComponent.license(parser.ts:33,parser.ts:106).src/parser.ts— SPDX:licenseConcludedpopulatesComponent.license(parser.ts:66).src/types.ts—Component.license?: stringis defined (types.ts:19) butChangeReporthas no license field (types.ts:70).src/diff.ts—diff()comparesversionfor upgrades but never reads.license(diff.ts:23).src/reporter.ts— no license column/section in any oftext/json/markdown.So a component that stays at the same version but changes its declared license produces zero output today.
Proposed change
When a component exists in both SBOMs (matched by the same key
diff()already uses) and itslicensediffers, record it as a license change.1. Types (
src/types.ts)2. Diff (
src/diff.ts)In the existing matched-component branch (the same place upgrades are detected), add:
This reuses the same component map / matching logic, so it composes with whatever key strategy
diff()settles on (note PR #8 is reworking the purl key — this slots in cleanly either way).3. Reporter (
src/reporter.ts)Add a "License Changes" section to
textandmarkdown(mirroring the existing Upgraded section).jsonis automatic since it serializes the full report. Example markdown:4. Tests (
src/__tests__/diff.test.ts)MIT → AGPL-3.0⇒ onelicenseChangesentryundefined → MIT) ⇒ one entry (or document that this is intentionally reported)Why this is high-leverage
--fail-on license-changebecomes trivial oncelicenseChangesexists in the report.ChangeReport; no change to existing fields or default CLI behavior.Happy to open a focused PR for this once the in-flight diff/CLI PRs (#5–#8) land, to avoid conflicts in
diff.ts/reporter.ts.