SBOM: publish an unrecognised licence as a name, not as a fake SPDX id - #196
Merged
villelaitila merged 1 commit intoAug 30, 2026
Merged
Conversation
CycloneDX $refs the SPDX identifier list from license.id, so only a checked identifier may go there. resolve_license_spdx_id put the literal string 'UNKNOWN LICENSE' there for every licence except MIT -- the mapping table it consulted first has always been empty -- and bom_licenses paired it with the literal url 'UNKNOWN'. That does not degrade one field. It makes the whole document fail validation, at every spec version this converter can declare, so a consumer that validates on ingest rejects the entire BOM: one ordinarily-licensed package costs a repository its whole SBOM. The failure then surfaces as a rejected upload rather than as a licence problem, which is the hardest kind to trace back. Measured on modelfile_for_sbom_tests.xml, whose single licence attribute is "OTHER": 1 schema error at 1.4, 1.5 and 1.6 before, 0 after. It also discarded what the model knew. 'OTHER', 'Apache-2.0' and 'AGPL;GPL' all became the same placeholder, which tells a reader nothing and cannot be recovered. name is the slot the specification designates for a licence that cannot be expressed as an identifier, and it is free text, so the real value survives. Every value not in SPDX_LICENSE_URLS goes there -- including ones that look like valid SPDX identifiers, because looking valid is what the previous code assumed and is exactly the assumption that has to stop. Widening that table is evidence work, one identifier at a time, and until an entry exists the name path reports the licence accurately anyway. A url is emitted only alongside an identifier we hold one for. 'UNKNOWN', 'TODO' and '' are not IRIs, and an invented url is worse than an absent one because a consumer cannot tell it from a real one -- the same reason _add_vcs_reference omits a missing repo_url rather than emitting a blank. An attribute present but empty now reads as absent instead of producing a licence entry invented out of an empty string. Dormant today: no analyzer stamps a licence on an External element unless the .licenses cache directory exists, and it does not on any instance checked. But creating that empty directory is all it takes to activate the clearlydefined.io lookups that do, at which point every non-MIT package would emit an invalid document.
Softagram Impact Report for pull/196 (head commit: 02d454e)TL;DR Arch. Impact: 📈 +2 | Changed code files: 2 | Directly impacted code files: 4⭐ Change Overview
⭐ Details of Dependency Changes (diagram)
🤖 AGENTS - machine-readable impact data (2 files changed, 4 impacted, +8/-1 deps)Change overviewHead Added dependencies (8)
Removed dependencies (1)
Impacted files (4)Unchanged files that directly depend on files changed in this PR - check them for behavioral impact. Grouped by changed file; dependent paths starting with ./ are relative to the changed file's directory:
Complete data
[] 📄 Full report
Impact Report explained. Give feedback on this report to support@softagram.com |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


license.idis constrained to the SPDX identifier enum — CycloneDX$refs the SPDX schemathere.
resolve_license_spdx_idput the literal string'UNKNOWN LICENSE'in it for everylicence except exactly
MIT(the mapping table it consulted first has always been{}), andbom_licensespaired that with the literal url'UNKNOWN'.That does not degrade one field. It makes the whole document fail validation, at every spec
version this converter can declare, so a consumer that validates on ingest rejects the entire
BOM — one ordinarily-licensed package costs a repository its whole SBOM. The failure then
surfaces as a rejected upload rather than as a licence problem, which is the hardest kind to
trace back.
It also discarded what the model knew:
OTHER,Apache-2.0andAGPL;GPLall collapsed to thesame placeholder, unrecoverably.
Measured
tests/converters/modelfile_for_sbom_tests.xmlcarrieslicense="OTHER", so the committedfixture already produces an invalid document today:
The fix
nameis the slot the specification designates for a licence that cannot be expressed as anidentifier, and it is free text, so the real value survives. Everything not in
SPDX_LICENSE_URLSgoes there — including values that look like valid SPDX identifiers,because "looks valid" is precisely the assumption that caused this. Widening that table is
evidence work, one identifier at a time, and until an entry exists the
namepath reports thelicence accurately anyway.
A url is emitted only alongside an identifier we hold one for.
'UNKNOWN','TODO'and''are not IRIs, and an invented url is worse than an absent one because a consumer cannot tell it
from a real one — the same reasoning
_add_vcs_referencealready applies to a missingrepo_url.An attribute present but empty now reads as absent, instead of producing a licence entry
invented out of an empty string.
Blast radius: none today, and one
mkdirawayNo analyzer stamps a licence on an
/External/element unless the.licensescache directoryexists —
dependencyanalysiswrapper.pygates the clearlydefined.io lookups onos.path.exists(licenses_cache), and its comment invites creating it: "can be created to getthis functionality activated, it needs to be just an empty directory." Not present on any
instance checked.
Verified that internal elements cannot reach this code at all:
bom_licensesis called only fromelem_as_bom_data(the External-component path);_internal_element_componentemits nolicenseskey. So the ~1,850 licence attributes that real models carry on internal elements arenot affected either way.
Corpus impact: none. 20 real models, 892 documents, generated on
mainand on this branch:identical, 0 differing. The fixture is the only place the defect is reachable today — which is
why it has gone unnoticed, not evidence that it is harmless.
Tests
556 → 566 passed (rebased on 1.17.0). Five new tests; with the implementation reverted in a
scratch copy, 4 of the 5 fail and the
MITcase passes throughout as the control — so thesuite distinguishes the fix rather than merely accompanying it.
Nothing else in the suite asserted the
licensesfield except oneMITcase, which isunchanged and still passes.