Detect a license file in the root directory or META-INF of a jar (#2213)

Signed-off-by: Colm O hEigeartaigh <coheigea@apache.org>
This commit is contained in:
Colm O hEigeartaigh 2023-10-12 16:09:53 +01:00 committed by GitHub
parent fe7a417fb2
commit 2687100e6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 87 additions and 17 deletions

View File

@ -8,6 +8,7 @@ import (
"strings" "strings"
intFile "github.com/anchore/syft/internal/file" intFile "github.com/anchore/syft/internal/file"
"github.com/anchore/syft/internal/licenses"
"github.com/anchore/syft/internal/log" "github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/file"
@ -177,6 +178,31 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) {
return nil, err return nil, err
} }
licenses, name, version, err := j.parseLicenses(manifest)
if err != nil {
return nil, err
}
return &pkg.Package{
// TODO: maybe select name should just have a pom properties in it?
Name: name,
Version: version,
Language: pkg.Java,
Licenses: pkg.NewLicenseSet(licenses...),
Locations: file.NewLocationSet(
j.location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
),
Type: j.fileInfo.pkgType(),
MetadataType: pkg.JavaMetadataType,
Metadata: pkg.JavaMetadata{
VirtualPath: j.location.AccessPath(),
Manifest: manifest,
ArchiveDigests: digests,
},
}, nil
}
func (j *archiveParser) parseLicenses(manifest *pkg.JavaManifest) ([]pkg.License, string, string, error) {
// we use j.location because we want to associate the license declaration with where we discovered the contents in the manifest // we use j.location because we want to associate the license declaration with where we discovered the contents in the manifest
// TODO: when we support locations of paths within archives we should start passing the specific manifest location object instead of the top jar // TODO: when we support locations of paths within archives we should start passing the specific manifest location object instead of the top jar
licenses := pkg.NewLicensesFromLocation(j.location, selectLicenses(manifest)...) licenses := pkg.NewLicensesFromLocation(j.location, selectLicenses(manifest)...)
@ -201,23 +227,17 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) {
licenses = append(licenses, pomLicenses...) licenses = append(licenses, pomLicenses...)
} }
return &pkg.Package{ if len(licenses) == 0 {
// TODO: maybe select name should just have a pom properties in it? fileLicenses, err := j.getLicenseFromFileInArchive()
Name: name, if err != nil {
Version: version, return nil, "", "", err
Language: pkg.Java, }
Licenses: pkg.NewLicenseSet(licenses...), if fileLicenses != nil {
Locations: file.NewLocationSet( licenses = append(licenses, fileLicenses...)
j.location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), }
), }
Type: j.fileInfo.pkgType(),
MetadataType: pkg.JavaMetadataType, return licenses, name, version, nil
Metadata: pkg.JavaMetadata{
VirtualPath: j.location.AccessPath(),
Manifest: manifest,
ArchiveDigests: digests,
},
}, nil
} }
type parsedPomProject struct { type parsedPomProject struct {
@ -310,6 +330,38 @@ func getDigestsFromArchive(archivePath string) ([]file.Digest, error) {
return digests, nil return digests, nil
} }
func (j *archiveParser) getLicenseFromFileInArchive() ([]pkg.License, error) {
var fileLicenses []pkg.License
for _, filename := range licenses.FileNames {
licenseMatches := j.fileManifest.GlobMatch("/META-INF/" + filename)
if len(licenseMatches) == 0 {
// Try the root directory if it's not in META-INF
licenseMatches = j.fileManifest.GlobMatch("/" + filename)
}
if len(licenseMatches) > 0 {
contents, err := intFile.ContentsFromZip(j.archivePath, licenseMatches...)
if err != nil {
return nil, fmt.Errorf("unable to extract java license (%s): %w", j.location, err)
}
for _, licenseMatch := range licenseMatches {
licenseContents := contents[licenseMatch]
parsed, err := licenses.Parse(strings.NewReader(licenseContents), j.location)
if err != nil {
return nil, err
}
if len(parsed) > 0 {
fileLicenses = append(fileLicenses, parsed...)
}
}
}
}
return fileLicenses, nil
}
func (j *archiveParser) discoverPkgsFromNestedArchives(parentPkg *pkg.Package) ([]pkg.Package, []artifact.Relationship, error) { func (j *archiveParser) discoverPkgsFromNestedArchives(parentPkg *pkg.Package) ([]pkg.Package, []artifact.Relationship, error) {
// we know that all java archives are zip formatted files, so we can use the shared zip helper // we know that all java archives are zip formatted files, so we can use the shared zip helper
return discoverPkgsFromZip(j.location, j.archivePath, j.contentPath, j.fileManifest, parentPkg) return discoverPkgsFromZip(j.location, j.archivePath, j.contentPath, j.fileManifest, parentPkg)

View File

@ -158,6 +158,15 @@ func TestParseJar(t *testing.T) {
Language: pkg.Java, Language: pkg.Java,
Type: pkg.JavaPkg, Type: pkg.JavaPkg,
MetadataType: pkg.JavaMetadataType, MetadataType: pkg.JavaMetadataType,
Licenses: pkg.NewLicenseSet(
pkg.License{
Value: "Apache-2.0",
SPDXExpression: "Apache-2.0",
Type: license.Concluded,
URLs: internal.NewStringSet(),
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar")),
},
),
Metadata: pkg.JavaMetadata{ Metadata: pkg.JavaMetadata{
VirtualPath: "test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar", VirtualPath: "test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar",
Manifest: &pkg.JavaManifest{ Manifest: &pkg.JavaManifest{
@ -223,6 +232,15 @@ func TestParseJar(t *testing.T) {
Language: pkg.Java, Language: pkg.Java,
Type: pkg.JavaPkg, Type: pkg.JavaPkg,
MetadataType: pkg.JavaMetadataType, MetadataType: pkg.JavaMetadataType,
Licenses: pkg.NewLicenseSet(
pkg.License{
Value: "Apache-2.0",
SPDXExpression: "Apache-2.0",
Type: license.Concluded,
URLs: internal.NewStringSet(),
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar")),
},
),
Metadata: pkg.JavaMetadata{ Metadata: pkg.JavaMetadata{
VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar", VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar",
Manifest: &pkg.JavaManifest{ Manifest: &pkg.JavaManifest{