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"
"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,

View File

@ -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
}

View File

@ -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

View File

@ -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{