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
+4 -4
View File
@@ -21,17 +21,17 @@ func newGemfileLockPackage(name, version string, locations ...source.Location) p
return p
}
func newGemspecPackage(m pkg.GemMetadata, locations ...source.Location) pkg.Package {
func newGemspecPackage(m gemData, gemSpecLocation source.Location) pkg.Package {
p := pkg.Package{
Name: m.Name,
Version: m.Version,
Locations: source.NewLocationSet(locations...),
Locations: source.NewLocationSet(gemSpecLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(gemSpecLocation, m.Licenses...)...),
PURL: packageURL(m.Name, m.Version),
Licenses: m.Licenses,
Language: pkg.Ruby,
Type: pkg.GemPkg,
MetadataType: pkg.GemMetadataType,
Metadata: m,
Metadata: m.GemMetadata,
}
p.SetID()
+10 -2
View File
@@ -20,6 +20,11 @@ var _ generic.Parser = parseGemFileLockEntries
type postProcessor func(string) []string
type gemData struct {
Licenses []string `mapstructure:"licenses" json:"licenses,omitempty"`
pkg.GemMetadata `mapstructure:",squash" json:",inline"`
}
// match example: Al\u003Ex ---> 003E
var unicodePattern = regexp.MustCompile(`\\u(?P<unicode>[0-9A-F]{4})`)
@@ -90,14 +95,17 @@ func parseGemSpecEntries(_ source.FileResolver, _ *generic.Environment, reader s
}
if fields["name"] != "" && fields["version"] != "" {
var metadata pkg.GemMetadata
var metadata gemData
if err := mapstructure.Decode(fields, &metadata); err != nil {
return nil, nil, fmt.Errorf("unable to decode gem metadata: %w", err)
}
pkgs = append(
pkgs,
newGemspecPackage(metadata, reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
newGemspecPackage(
metadata,
reader.Location,
),
)
}
@@ -14,12 +14,14 @@ func TestParseGemspec(t *testing.T) {
locations := source.NewLocationSet(source.NewLocation(fixture))
var expectedPkg = pkg.Package{
Name: "bundler",
Version: "2.1.4",
PURL: "pkg:gem/bundler@2.1.4",
Locations: locations,
Type: pkg.GemPkg,
Licenses: []string{"MIT"},
Name: "bundler",
Version: "2.1.4",
PURL: "pkg:gem/bundler@2.1.4",
Locations: locations,
Type: pkg.GemPkg,
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromLocations("MIT", source.NewLocation(fixture)),
),
Language: pkg.Ruby,
MetadataType: pkg.GemMetadataType,
Metadata: pkg.GemMetadata{
@@ -27,7 +29,6 @@ func TestParseGemspec(t *testing.T) {
Version: "2.1.4",
Files: []string{"exe/bundle", "exe/bundler"},
Authors: []string{"André Arko", "Samuel Giddins", "Colby Swandale", "Hiroshi Shibata", "David Rodríguez", "Grey Baker", "Stephanie Morillo", "Chris Morris", "James Wen", "Tim Moore", "André Medeiros", "Jessica Lynn Suttles", "Terence Lee", "Carl Lerche", "Yehuda Katz"},
Licenses: []string{"MIT"},
Homepage: "https://bundler.io",
},
}