feat: update syft license concept to complex struct (#1743)

this PR makes the following changes to update the underlying license model to have more expressive capabilities
it also provides some guarantee's surrounding the license values themselves

- Licenses are updated from string -> pkg.LicenseSet which contain pkg.License with the following fields:
- original `Value` read by syft
- If it's possible to construct licenses will always have a valid SPDX expression for downstream consumption
- the above is run against a generated list of SPDX license ID to try and find the correct ID
- SPDX concluded vs declared is added to the new struct
- URL source for license is added to the new struct
- Location source is added to the new struct to show where the expression was pulled from
This commit is contained in:
Christopher Angelo Phillips
2023-05-15 16:23:39 -04:00
committed by GitHub
parent 8046f09562
commit 42fa9e4965
126 changed files with 5165 additions and 1503 deletions
+139
View File
@@ -0,0 +1,139 @@
package pkg
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/license"
"github.com/anchore/syft/syft/source"
)
func TestLicenseSet_Add(t *testing.T) {
tests := []struct {
name string
licenses []License
want []License
}{
{
name: "add one simple license",
licenses: []License{
NewLicense("MIT"),
},
want: []License{
NewLicense("MIT"),
},
},
{
name: "add multiple simple licenses",
licenses: []License{
NewLicense("MIT"),
NewLicense("MIT"),
NewLicense("Apache-2.0"),
},
want: []License{
NewLicense("Apache-2.0"),
NewLicense("MIT"),
},
},
{
name: "attempt to add a license with no name",
licenses: []License{
NewLicense(""),
},
want: nil,
},
{
name: "keep multiple licenses sorted",
licenses: []License{
NewLicense("MIT"),
NewLicense("Apache-2.0"),
},
want: []License{
NewLicense("Apache-2.0"),
NewLicense("MIT"),
},
},
{
name: "deduplicate licenses with locations",
licenses: []License{
NewLicenseFromLocations("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "1"})),
NewLicenseFromLocations("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "1"})),
NewLicenseFromLocations("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "2"})),
},
want: []License{
NewLicenseFromLocations(
"MIT",
source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "1"}),
source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "2"}),
),
},
},
{
name: "same licenses with different locations",
licenses: []License{
NewLicense("MIT"),
NewLicenseFromLocations("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "2"})),
NewLicenseFromLocations("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "1"})),
},
want: []License{
NewLicenseFromLocations(
"MIT",
source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "1"}),
source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "2"}),
),
},
},
{
name: "same license from different sources",
licenses: []License{
NewLicense("MIT"),
NewLicenseFromLocations("MIT", source.NewLocation("/place")),
NewLicenseFromURLs("MIT", "https://example.com"),
},
want: []License{
{
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Declared,
URL: internal.NewStringSet("https://example.com"),
Location: source.NewLocationSet(source.NewLocation("/place")),
},
},
},
{
name: "different licenses from different sources with different types constitute two licenses",
licenses: []License{
NewLicenseFromType("MIT", license.Concluded),
NewLicenseFromType("MIT", license.Declared),
NewLicenseFromLocations("MIT", source.NewLocation("/place")),
NewLicenseFromURLs("MIT", "https://example.com"),
},
want: []License{
{
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Concluded,
URL: internal.NewStringSet(),
Location: source.NewLocationSet(),
},
{
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Declared,
URL: internal.NewStringSet("https://example.com"),
Location: source.NewLocationSet(source.NewLocation("/place")),
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := NewLicenseSet()
s.Add(tt.licenses...)
testMe := s.ToSlice()
assert.Equal(t, tt.want, testMe)
})
}
}