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
+99
View File
@@ -0,0 +1,99 @@
package pkg
import (
"sort"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/source"
)
func Test_Hash(t *testing.T) {
loc1 := source.NewLocation("place!")
loc1.FileSystemID = "fs1"
loc2 := source.NewLocation("place!")
loc2.FileSystemID = "fs2" // important! there is a different file system ID
lic1 := NewLicenseFromLocations("MIT", loc1)
lic2 := NewLicenseFromLocations("MIT", loc2)
lic1.URL.Add("foo")
lic2.URL.Add("bar") // we also want to check the URL are ignored
hash1, err := artifact.IDByHash(lic1)
require.NoError(t, err)
hash2, err := artifact.IDByHash(lic2)
require.NoError(t, err)
assert.Equal(t, hash1, hash2)
}
func Test_Sort(t *testing.T) {
tests := []struct {
name string
licenses Licenses
expected Licenses
}{
{
name: "empty",
licenses: []License{},
expected: []License{},
},
{
name: "single",
licenses: []License{
NewLicenseFromLocations("MIT", source.NewLocation("place!")),
},
expected: []License{
NewLicenseFromLocations("MIT", source.NewLocation("place!")),
},
},
{
name: "multiple",
licenses: []License{
NewLicenseFromLocations("MIT", source.NewLocation("place!")),
NewLicenseFromURLs("MIT", "https://github.com/anchore/syft/blob/main/LICENSE"),
NewLicenseFromLocations("Apache", source.NewLocation("area!")),
NewLicenseFromLocations("gpl2+", source.NewLocation("area!")),
},
expected: Licenses{
NewLicenseFromLocations("Apache", source.NewLocation("area!")),
NewLicenseFromURLs("MIT", "https://github.com/anchore/syft/blob/main/LICENSE"),
NewLicenseFromLocations("MIT", source.NewLocation("place!")),
NewLicenseFromLocations("gpl2+", source.NewLocation("area!")),
},
},
{
name: "multiple with location variants",
licenses: []License{
NewLicenseFromLocations("MIT", source.NewLocation("place!")),
NewLicenseFromLocations("MIT", source.NewLocation("park!")),
NewLicense("MIT"),
NewLicense("AAL"),
NewLicense("Adobe-2006"),
NewLicenseFromLocations("Apache", source.NewLocation("area!")),
},
expected: Licenses{
NewLicense("AAL"),
NewLicense("Adobe-2006"),
NewLicenseFromLocations("Apache", source.NewLocation("area!")),
NewLicense("MIT"),
NewLicenseFromLocations("MIT", source.NewLocation("park!")),
NewLicenseFromLocations("MIT", source.NewLocation("place!")),
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
sort.Sort(test.licenses)
assert.Equal(t, test.expected, test.licenses)
})
}
}