diff --git a/syft/pkg/cataloger/python/package.go b/syft/pkg/cataloger/python/package.go index 59fd9f6b5..76c092a05 100644 --- a/syft/pkg/cataloger/python/package.go +++ b/syft/pkg/cataloger/python/package.go @@ -4,6 +4,8 @@ import ( "fmt" "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/pkg" ) @@ -55,13 +57,37 @@ func newPackageForRequirementsWithMetadata(name, version string, metadata pkg.Py 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{ Name: m.Name, Version: m.Version, PURL: packageURL(m.Name, m.Version, &m.PythonPackage), Locations: file.NewLocationSet(sources...), - Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(m.LicenseLocation, m.Licenses)...), + Licenses: licenseSet, Language: pkg.Python, Type: pkg.PythonPkg, Metadata: m.PythonPackage, diff --git a/syft/pkg/cataloger/python/parse_wheel_egg.go b/syft/pkg/cataloger/python/parse_wheel_egg.go index e98fe9de8..a50f9d558 100644 --- a/syft/pkg/cataloger/python/parse_wheel_egg.go +++ b/syft/pkg/cataloger/python/parse_wheel_egg.go @@ -32,7 +32,7 @@ func parseWheelOrEgg(resolver file.Resolver, _ *generic.Environment, reader file return nil, nil, nil } - pkgs := []pkg.Package{newPackageForPackage(*pd, sources...)} + pkgs := []pkg.Package{newPackageForPackage(resolver, *pd, sources...)} return pkgs, nil, nil } diff --git a/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go b/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go index a52119995..81910483d 100644 --- a/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go +++ b/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go @@ -17,6 +17,7 @@ import ( type parsedData struct { Licenses string `mapstructure:"License"` + LicenseFile string `mapstructure:"LicenseFile"` LicenseLocation file.Location pkg.PythonPackage `mapstructure:",squash"` } @@ -82,6 +83,8 @@ func parseWheelOrEggMetadata(path string, reader io.Reader) (parsedData, error) pd.SitePackagesRootPath = determineSitePackagesRootPath(path) if pd.Licenses != "" { pd.LicenseLocation = file.NewLocation(path) + } else if pd.LicenseFile != "" { + pd.LicenseLocation = file.NewLocation(filepath.Join(filepath.Dir(path), pd.LicenseFile)) } return pd, nil diff --git a/syft/pkg/cataloger/python/parse_wheel_egg_metadata_test.go b/syft/pkg/cataloger/python/parse_wheel_egg_metadata_test.go index d3a927512..e661dc579 100644 --- a/syft/pkg/cataloger/python/parse_wheel_egg_metadata_test.go +++ b/syft/pkg/cataloger/python/parse_wheel_egg_metadata_test.go @@ -19,6 +19,7 @@ func TestParseWheelEggMetadata(t *testing.T) { Fixture: "test-fixtures/egg-info/PKG-INFO", ExpectedMetadata: parsedData{ "Apache 2.0", + "", file.NewLocation("test-fixtures/egg-info/PKG-INFO"), pkg.PythonPackage{ Name: "requests", @@ -34,6 +35,7 @@ func TestParseWheelEggMetadata(t *testing.T) { Fixture: "test-fixtures/dist-info/METADATA", ExpectedMetadata: parsedData{ "BSD License", + "", file.NewLocation("test-fixtures/dist-info/METADATA"), pkg.PythonPackage{ Name: "Pygments", @@ -134,6 +136,7 @@ func TestParseWheelEggMetadataInvalid(t *testing.T) { { Fixture: "test-fixtures/egg-info/PKG-INFO-INVALID", ExpectedMetadata: parsedData{ + "", "", file.Location{}, pkg.PythonPackage{