fix: use "contents" field and remove "fullText" license field (#3857)

---------
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
This commit is contained in:
Christopher Angelo Phillips 2025-05-05 17:40:09 -04:00 committed by GitHub
parent 6db60c5975
commit 1ba1186410
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 64 additions and 40 deletions

View File

@ -80,7 +80,7 @@ func ParseLicenses(raw []pkg.License) (concluded, declared []SPDXLicense) {
func createSPDXLicense(l pkg.License) SPDXLicense { func createSPDXLicense(l pkg.License) SPDXLicense {
candidate := SPDXLicense{ candidate := SPDXLicense{
ID: generateLicenseID(l), ID: generateLicenseID(l),
FullText: l.FullText, FullText: l.Contents,
} }
if l.SPDXExpression == "" { if l.SPDXExpression == "" {
@ -96,7 +96,7 @@ func generateLicenseID(l pkg.License) string {
if l.Value != "" { if l.Value != "" {
return spdxlicense.LicenseRefPrefix + SanitizeElementID(l.Value) return spdxlicense.LicenseRefPrefix + SanitizeElementID(l.Value)
} }
return licenseSum(l.FullText) return licenseSum(l.Contents)
} }
func licenseSum(s string) string { func licenseSum(s string) string {

View File

@ -116,7 +116,7 @@ func TestGenerateLicenseID(t *testing.T) {
license: pkg.License{ license: pkg.License{
SPDXExpression: "Apache-2.0", SPDXExpression: "Apache-2.0",
Value: "SomeValue", Value: "SomeValue",
FullText: "Some text", Contents: "Some text",
}, },
expected: "Apache-2.0", expected: "Apache-2.0",
}, },
@ -136,9 +136,9 @@ func TestGenerateLicenseID(t *testing.T) {
"LGPLv2--and-LGPLv2--with-exceptions-and-GPLv2--and-GPLv2--with-exceptions-and-BSD-and-Inner-Net-and-ISC-and-Public-Domain-and-GFDL", "LGPLv2--and-LGPLv2--with-exceptions-and-GPLv2--and-GPLv2--with-exceptions-and-BSD-and-Inner-Net-and-ISC-and-Public-Domain-and-GFDL",
}, },
{ {
name: "Uses hash of fullText when nothing else is provided", name: "Uses hash of contents when nothing else is provided",
license: pkg.License{ license: pkg.License{
FullText: "This is a very long custom license text that should be hashed because it's more than 64 characters long.", Contents: "This is a very long custom license text that should be hashed because it's more than 64 characters long.",
}, },
expected: "", // We'll verify it starts with the correct prefix expected: "", // We'll verify it starts with the correct prefix
}, },

View File

@ -15,7 +15,7 @@
"packages": [ "packages": [
{ {
"name": "package-1", "name": "package-1",
"SPDXID": "SPDXRef-Package-python-package-1-cf21bacaa74c8c08", "SPDXID": "SPDXRef-Package-python-package-1-4dd25c6ee16b729a",
"versionInfo": "1.0.1", "versionInfo": "1.0.1",
"supplier": "NOASSERTION", "supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION", "downloadLocation": "NOASSERTION",
@ -76,7 +76,7 @@
"relationships": [ "relationships": [
{ {
"spdxElementId": "SPDXRef-DocumentRoot-Directory-some-path", "spdxElementId": "SPDXRef-DocumentRoot-Directory-some-path",
"relatedSpdxElement": "SPDXRef-Package-python-package-1-cf21bacaa74c8c08", "relatedSpdxElement": "SPDXRef-Package-python-package-1-4dd25c6ee16b729a",
"relationshipType": "CONTAINS" "relationshipType": "CONTAINS"
}, },
{ {

View File

@ -15,7 +15,7 @@
"packages": [ "packages": [
{ {
"name": "package-1", "name": "package-1",
"SPDXID": "SPDXRef-Package-python-package-1-2d8996d6f81313df", "SPDXID": "SPDXRef-Package-python-package-1-72567175418f73f8",
"versionInfo": "1.0.1", "versionInfo": "1.0.1",
"supplier": "NOASSERTION", "supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION", "downloadLocation": "NOASSERTION",
@ -90,7 +90,7 @@
"relationships": [ "relationships": [
{ {
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input", "spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
"relatedSpdxElement": "SPDXRef-Package-python-package-1-2d8996d6f81313df", "relatedSpdxElement": "SPDXRef-Package-python-package-1-72567175418f73f8",
"relationshipType": "CONTAINS" "relationshipType": "CONTAINS"
}, },
{ {

View File

@ -15,7 +15,7 @@
"packages": [ "packages": [
{ {
"name": "package-1", "name": "package-1",
"SPDXID": "SPDXRef-Package-python-package-1-2d8996d6f81313df", "SPDXID": "SPDXRef-Package-python-package-1-72567175418f73f8",
"versionInfo": "1.0.1", "versionInfo": "1.0.1",
"supplier": "NOASSERTION", "supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION", "downloadLocation": "NOASSERTION",
@ -199,38 +199,38 @@
], ],
"relationships": [ "relationships": [
{ {
"spdxElementId": "SPDXRef-Package-python-package-1-2d8996d6f81313df", "spdxElementId": "SPDXRef-Package-python-package-1-72567175418f73f8",
"relatedSpdxElement": "SPDXRef-File-f1-5265a4dde3edbf7c", "relatedSpdxElement": "SPDXRef-File-f1-5265a4dde3edbf7c",
"relationshipType": "CONTAINS" "relationshipType": "CONTAINS"
}, },
{ {
"spdxElementId": "SPDXRef-Package-python-package-1-2d8996d6f81313df", "spdxElementId": "SPDXRef-Package-python-package-1-72567175418f73f8",
"relatedSpdxElement": "SPDXRef-File-z1-f5-839d99ee67d9d174", "relatedSpdxElement": "SPDXRef-File-z1-f5-839d99ee67d9d174",
"relationshipType": "CONTAINS" "relationshipType": "CONTAINS"
}, },
{ {
"spdxElementId": "SPDXRef-Package-python-package-1-2d8996d6f81313df", "spdxElementId": "SPDXRef-Package-python-package-1-72567175418f73f8",
"relatedSpdxElement": "SPDXRef-File-a1-f6-9c2f7510199b17f6", "relatedSpdxElement": "SPDXRef-File-a1-f6-9c2f7510199b17f6",
"relationshipType": "CONTAINS" "relationshipType": "CONTAINS"
}, },
{ {
"spdxElementId": "SPDXRef-Package-python-package-1-2d8996d6f81313df", "spdxElementId": "SPDXRef-Package-python-package-1-72567175418f73f8",
"relatedSpdxElement": "SPDXRef-File-d2-f4-c641caa71518099f", "relatedSpdxElement": "SPDXRef-File-d2-f4-c641caa71518099f",
"relationshipType": "CONTAINS" "relationshipType": "CONTAINS"
}, },
{ {
"spdxElementId": "SPDXRef-Package-python-package-1-2d8996d6f81313df", "spdxElementId": "SPDXRef-Package-python-package-1-72567175418f73f8",
"relatedSpdxElement": "SPDXRef-File-d1-f3-c6f5b29dca12661f", "relatedSpdxElement": "SPDXRef-File-d1-f3-c6f5b29dca12661f",
"relationshipType": "CONTAINS" "relationshipType": "CONTAINS"
}, },
{ {
"spdxElementId": "SPDXRef-Package-python-package-1-2d8996d6f81313df", "spdxElementId": "SPDXRef-Package-python-package-1-72567175418f73f8",
"relatedSpdxElement": "SPDXRef-File-f2-f9e49132a4b96ccd", "relatedSpdxElement": "SPDXRef-File-f2-f9e49132a4b96ccd",
"relationshipType": "CONTAINS" "relationshipType": "CONTAINS"
}, },
{ {
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input", "spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
"relatedSpdxElement": "SPDXRef-Package-python-package-1-2d8996d6f81313df", "relatedSpdxElement": "SPDXRef-Package-python-package-1-72567175418f73f8",
"relationshipType": "CONTAINS" "relationshipType": "CONTAINS"
}, },
{ {

View File

@ -91,7 +91,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1
##### Package: package-1 ##### Package: package-1
PackageName: package-1 PackageName: package-1
SPDXID: SPDXRef-Package-python-package-1-2d8996d6f81313df SPDXID: SPDXRef-Package-python-package-1-72567175418f73f8
PackageVersion: 1.0.1 PackageVersion: 1.0.1
PackageSupplier: NOASSERTION PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION PackageDownloadLocation: NOASSERTION
@ -105,13 +105,13 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-1
##### Relationships ##### Relationships
Relationship: SPDXRef-Package-python-package-1-2d8996d6f81313df CONTAINS SPDXRef-File-f1-5265a4dde3edbf7c Relationship: SPDXRef-Package-python-package-1-72567175418f73f8 CONTAINS SPDXRef-File-f1-5265a4dde3edbf7c
Relationship: SPDXRef-Package-python-package-1-2d8996d6f81313df CONTAINS SPDXRef-File-z1-f5-839d99ee67d9d174 Relationship: SPDXRef-Package-python-package-1-72567175418f73f8 CONTAINS SPDXRef-File-z1-f5-839d99ee67d9d174
Relationship: SPDXRef-Package-python-package-1-2d8996d6f81313df CONTAINS SPDXRef-File-a1-f6-9c2f7510199b17f6 Relationship: SPDXRef-Package-python-package-1-72567175418f73f8 CONTAINS SPDXRef-File-a1-f6-9c2f7510199b17f6
Relationship: SPDXRef-Package-python-package-1-2d8996d6f81313df CONTAINS SPDXRef-File-d2-f4-c641caa71518099f Relationship: SPDXRef-Package-python-package-1-72567175418f73f8 CONTAINS SPDXRef-File-d2-f4-c641caa71518099f
Relationship: SPDXRef-Package-python-package-1-2d8996d6f81313df CONTAINS SPDXRef-File-d1-f3-c6f5b29dca12661f Relationship: SPDXRef-Package-python-package-1-72567175418f73f8 CONTAINS SPDXRef-File-d1-f3-c6f5b29dca12661f
Relationship: SPDXRef-Package-python-package-1-2d8996d6f81313df CONTAINS SPDXRef-File-f2-f9e49132a4b96ccd Relationship: SPDXRef-Package-python-package-1-72567175418f73f8 CONTAINS SPDXRef-File-f2-f9e49132a4b96ccd
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-2d8996d6f81313df Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-72567175418f73f8
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-4b756c6f6fb127a3 Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-4b756c6f6fb127a3
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input

View File

@ -38,7 +38,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1
##### Package: package-1 ##### Package: package-1
PackageName: package-1 PackageName: package-1
SPDXID: SPDXRef-Package-python-package-1-cf21bacaa74c8c08 SPDXID: SPDXRef-Package-python-package-1-4dd25c6ee16b729a
PackageVersion: 1.0.1 PackageVersion: 1.0.1
PackageSupplier: NOASSERTION PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION PackageDownloadLocation: NOASSERTION
@ -52,7 +52,7 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-2
##### Relationships ##### Relationships
Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-python-package-1-cf21bacaa74c8c08 Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-python-package-1-4dd25c6ee16b729a
Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-deb-package-2-39392bb5e270f669 Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-deb-package-2-39392bb5e270f669
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Directory-some-path Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Directory-some-path

View File

@ -41,7 +41,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1
##### Package: package-1 ##### Package: package-1
PackageName: package-1 PackageName: package-1
SPDXID: SPDXRef-Package-python-package-1-2d8996d6f81313df SPDXID: SPDXRef-Package-python-package-1-72567175418f73f8
PackageVersion: 1.0.1 PackageVersion: 1.0.1
PackageSupplier: NOASSERTION PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION PackageDownloadLocation: NOASSERTION
@ -55,7 +55,7 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-1
##### Relationships ##### Relationships
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-2d8996d6f81313df Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-72567175418f73f8
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-4b756c6f6fb127a3 Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-4b756c6f6fb127a3
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input

View File

@ -1,7 +1,7 @@
{ {
"artifacts": [ "artifacts": [
{ {
"id": "cf21bacaa74c8c08", "id": "4dd25c6ee16b729a",
"name": "package-1", "name": "package-1",
"version": "1.0.1", "version": "1.0.1",
"type": "python", "type": "python",

View File

@ -1,7 +1,7 @@
{ {
"artifacts": [ "artifacts": [
{ {
"id": "783177db0211edb6", "id": "fba4ca04d4906f25",
"name": "package-1", "name": "package-1",
"version": "1.0.1", "version": "1.0.1",
"type": "python", "type": "python",

View File

@ -1,7 +1,7 @@
{ {
"artifacts": [ "artifacts": [
{ {
"id": "2d8996d6f81313df", "id": "72567175418f73f8",
"name": "package-1", "name": "package-1",
"version": "1.0.1", "version": "1.0.1",
"type": "python", "type": "python",

View File

@ -230,12 +230,11 @@ func toLicenseModel(pkgLicenses []pkg.License) (modelLicenses []model.License) {
modelLicenses = append(modelLicenses, model.License{ modelLicenses = append(modelLicenses, model.License{
Value: l.Value, Value: l.Value,
FullText: l.FullText,
SPDXExpression: l.SPDXExpression, SPDXExpression: l.SPDXExpression,
Contents: l.Contents,
Type: l.Type, Type: l.Type,
URLs: urls, URLs: urls,
Locations: locations, Locations: locations,
Contents: l.Contents,
}) })
} }
return return

View File

@ -27,17 +27,16 @@ var _ sort.Interface = (*Licenses)(nil)
// of where a license was declared/concluded for a given package // of where a license was declared/concluded for a given package
// If a license is given as it's full text in the metadata rather than it's value or SPDX expression // If a license is given as it's full text in the metadata rather than it's value or SPDX expression
// The FullText field is used to represent this data // The Contents field is used to represent this data
// A Concluded License type is the license the SBOM creator believes governs the package (human crafted or altered SBOM) // A Concluded License type is the license the SBOM creator believes governs the package (human crafted or altered SBOM)
// The Declared License is what the authors of a project believe govern the package. This is the default type syft declares. // The Declared License is what the authors of a project believe govern the package. This is the default type syft declares.
type License struct { type License struct {
SPDXExpression string SPDXExpression string
Value string Value string
FullText string
Type license.Type Type license.Type
Contents string
URLs []string `hash:"ignore"` URLs []string `hash:"ignore"`
Locations file.LocationSet `hash:"ignore"` Locations file.LocationSet `hash:"ignore"`
Contents string `hash:"ignore"` // The optional binary contents of the license file
} }
type Licenses []License type Licenses []License
@ -93,7 +92,7 @@ func NewLicenseFromType(value string, t license.Type) License {
if fullText != "" { if fullText != "" {
return License{ return License{
FullText: fullText, Contents: fullText,
Type: t, Type: t,
Locations: file.NewLocationSet(), Locations: file.NewLocationSet(),
} }
@ -180,7 +179,7 @@ func NewLicenseFromFields(value, url string, location *file.Location) License {
} }
func (s License) Empty() bool { func (s License) Empty() bool {
return s.Value == "" && s.SPDXExpression == "" && s.FullText == "" return s.Value == "" && s.SPDXExpression == "" && s.Contents == ""
} }
// Merge two licenses into a new license object. If the merge is not possible due to unmergeable fields // Merge two licenses into a new license object. If the merge is not possible due to unmergeable fields

View File

@ -1,6 +1,7 @@
package pkg package pkg
import ( import (
"os"
"testing" "testing"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
@ -125,6 +126,23 @@ func TestLicenseSet_Add(t *testing.T) {
}, },
}, },
}, },
{
name: "licenses that are unknown with different contents can exist in the same set",
licenses: []License{
NewLicense(readFileAsString("../../internal/licenses/test-fixtures/nvidia-software-and-cuda-supplement")),
NewLicense(readFileAsString("../../internal/licenses/test-fixtures/apache-license-2.0")),
},
want: []License{
{
Contents: readFileAsString("../../internal/licenses/test-fixtures/apache-license-2.0"),
Type: license.Declared,
},
{
Contents: readFileAsString("../../internal/licenses/test-fixtures/nvidia-software-and-cuda-supplement"),
Type: license.Declared,
},
},
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
@ -160,3 +178,11 @@ func defaultLicenseComparer(x, y License) bool {
}, },
)) ))
} }
func readFileAsString(filepath string) string {
data, err := os.ReadFile(filepath)
if err != nil {
panic(err)
}
return string(data)
}

View File

@ -240,7 +240,7 @@ func TestFullText(t *testing.T) {
want: License{ want: License{
Value: "", Value: "",
Type: license.Declared, Type: license.Declared,
FullText: fullText, Contents: fullText,
}, },
}, },
} }