Parse Python licenses from LicenseFile entry in the Wheel Metadata (#2331)

Signed-off-by: Colm O hEigeartaigh <coheigea@apache.org>
This commit is contained in:
Colm O hEigeartaigh 2023-12-13 22:46:56 +00:00 committed by GitHub
parent 8bca0ac39e
commit d39ef44e40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 35 additions and 3 deletions

View File

@ -4,6 +4,8 @@ import (
"fmt" "fmt"
"github.com/anchore/packageurl-go" "github.com/anchore/packageurl-go"
"github.com/anchore/syft/internal/licenses"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
) )
@ -55,13 +57,37 @@ func newPackageForRequirementsWithMetadata(name, version string, metadata pkg.Py
return p return p
} }
func newPackageForPackage(m parsedData, sources ...file.Location) pkg.Package { func newPackageForPackage(resolver file.Resolver, m parsedData, sources ...file.Location) pkg.Package {
var licenseSet pkg.LicenseSet
if m.Licenses != "" {
licenseSet = pkg.NewLicenseSet(pkg.NewLicensesFromLocation(m.LicenseLocation, m.Licenses)...)
} else if m.LicenseLocation.Path() != "" {
// If we have a license file then resolve and parse it
found, err := resolver.FilesByPath(m.LicenseLocation.Path())
if err != nil {
log.WithFields("error", err).Tracef("unable to resolve python license path %s", m.LicenseLocation.Path())
}
if len(found) > 0 {
metadataContents, err := resolver.FileContentsByLocation(found[0])
if err == nil {
parsed, err := licenses.Parse(metadataContents, m.LicenseLocation)
if err != nil {
log.WithFields("error", err).Tracef("unable to parse a license from the file in %s", m.LicenseLocation.Path())
}
if len(parsed) > 0 {
licenseSet = pkg.NewLicenseSet(parsed...)
}
} else {
log.WithFields("error", err).Tracef("unable to read file contents at %s", m.LicenseLocation.Path())
}
}
}
p := pkg.Package{ p := pkg.Package{
Name: m.Name, Name: m.Name,
Version: m.Version, Version: m.Version,
PURL: packageURL(m.Name, m.Version, &m.PythonPackage), PURL: packageURL(m.Name, m.Version, &m.PythonPackage),
Locations: file.NewLocationSet(sources...), Locations: file.NewLocationSet(sources...),
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(m.LicenseLocation, m.Licenses)...), Licenses: licenseSet,
Language: pkg.Python, Language: pkg.Python,
Type: pkg.PythonPkg, Type: pkg.PythonPkg,
Metadata: m.PythonPackage, Metadata: m.PythonPackage,

View File

@ -32,7 +32,7 @@ func parseWheelOrEgg(resolver file.Resolver, _ *generic.Environment, reader file
return nil, nil, nil return nil, nil, nil
} }
pkgs := []pkg.Package{newPackageForPackage(*pd, sources...)} pkgs := []pkg.Package{newPackageForPackage(resolver, *pd, sources...)}
return pkgs, nil, nil return pkgs, nil, nil
} }

View File

@ -17,6 +17,7 @@ import (
type parsedData struct { type parsedData struct {
Licenses string `mapstructure:"License"` Licenses string `mapstructure:"License"`
LicenseFile string `mapstructure:"LicenseFile"`
LicenseLocation file.Location LicenseLocation file.Location
pkg.PythonPackage `mapstructure:",squash"` pkg.PythonPackage `mapstructure:",squash"`
} }
@ -82,6 +83,8 @@ func parseWheelOrEggMetadata(path string, reader io.Reader) (parsedData, error)
pd.SitePackagesRootPath = determineSitePackagesRootPath(path) pd.SitePackagesRootPath = determineSitePackagesRootPath(path)
if pd.Licenses != "" { if pd.Licenses != "" {
pd.LicenseLocation = file.NewLocation(path) pd.LicenseLocation = file.NewLocation(path)
} else if pd.LicenseFile != "" {
pd.LicenseLocation = file.NewLocation(filepath.Join(filepath.Dir(path), pd.LicenseFile))
} }
return pd, nil return pd, nil

View File

@ -19,6 +19,7 @@ func TestParseWheelEggMetadata(t *testing.T) {
Fixture: "test-fixtures/egg-info/PKG-INFO", Fixture: "test-fixtures/egg-info/PKG-INFO",
ExpectedMetadata: parsedData{ ExpectedMetadata: parsedData{
"Apache 2.0", "Apache 2.0",
"",
file.NewLocation("test-fixtures/egg-info/PKG-INFO"), file.NewLocation("test-fixtures/egg-info/PKG-INFO"),
pkg.PythonPackage{ pkg.PythonPackage{
Name: "requests", Name: "requests",
@ -34,6 +35,7 @@ func TestParseWheelEggMetadata(t *testing.T) {
Fixture: "test-fixtures/dist-info/METADATA", Fixture: "test-fixtures/dist-info/METADATA",
ExpectedMetadata: parsedData{ ExpectedMetadata: parsedData{
"BSD License", "BSD License",
"",
file.NewLocation("test-fixtures/dist-info/METADATA"), file.NewLocation("test-fixtures/dist-info/METADATA"),
pkg.PythonPackage{ pkg.PythonPackage{
Name: "Pygments", Name: "Pygments",
@ -134,6 +136,7 @@ func TestParseWheelEggMetadataInvalid(t *testing.T) {
{ {
Fixture: "test-fixtures/egg-info/PKG-INFO-INVALID", Fixture: "test-fixtures/egg-info/PKG-INFO-INVALID",
ExpectedMetadata: parsedData{ ExpectedMetadata: parsedData{
"",
"", "",
file.Location{}, file.Location{},
pkg.PythonPackage{ pkg.PythonPackage{