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
+1 -1
View File
@@ -6,5 +6,5 @@ const (
// JSONSchemaVersion is the current schema version output by the JSON encoder
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
JSONSchemaVersion = "7.1.6"
JSONSchemaVersion = "8.0.0"
)
+16 -10
View File
@@ -4,7 +4,10 @@ import (
"io"
"github.com/google/licensecheck"
"golang.org/x/exp/slices"
"github.com/anchore/syft/syft/license"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
)
const (
@@ -13,21 +16,24 @@ const (
)
// Parse scans the contents of a license file to attempt to determine the type of license it is
func Parse(reader io.Reader) (licenses []string, err error) {
func Parse(reader io.Reader, l source.Location) (licenses []pkg.License, err error) {
licenses = make([]pkg.License, 0)
contents, err := io.ReadAll(reader)
if err != nil {
return nil, err
}
cov := licensecheck.Scan(contents)
if cov.Percent < coverageThreshold {
// unknown or no licenses here?
return licenses, nil
}
if cov.Percent < float64(coverageThreshold) {
licenses = append(licenses, unknownLicenseType)
}
for _, m := range cov.Match {
if slices.Contains(licenses, m.ID) {
continue
}
licenses = append(licenses, m.ID)
lic := pkg.NewLicenseFromLocations(m.ID, l)
lic.Type = license.Concluded
licenses = append(licenses, lic)
}
return
return licenses, nil
}
+20 -2
View File
@@ -15,8 +15,10 @@ func NewStringSet(start ...string) StringSet {
}
// Add a string to the set.
func (s StringSet) Add(i string) {
s[i] = struct{}{}
func (s StringSet) Add(i ...string) {
for _, str := range i {
s[str] = struct{}{}
}
}
// Remove a string from the set.
@@ -41,3 +43,19 @@ func (s StringSet) ToSlice() []string {
sort.Strings(ret)
return ret
}
func (s StringSet) Equals(o StringSet) bool {
if len(s) != len(o) {
return false
}
for k := range s {
if !o.Contains(k) {
return false
}
}
return true
}
func (s StringSet) Empty() bool {
return len(s) < 1
}