mirror of
https://github.com/anchore/syft
synced 2024-11-10 14:24:12 +00:00
Parse the Maven license from the pom.xml if not contained in the mani… (#2115)
* Parse the Maven license from the pom.xml if not contained in the manifest Signed-off-by: Colm O hEigeartaigh <coheigea@apache.org> * chore: restore 10.0.2 schema Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com> * chore: generate new 11.0.1 schema Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com> * refactor: remove schema change Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com> * test: update unit tests to align with new pattern Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com> * chore: pr feedback Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com> * chore: remove struct tags Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com> * keep license name and url semantics preserved on the pkg object Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> --------- Signed-off-by: Colm O hEigeartaigh <coheigea@apache.org> Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> Co-authored-by: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com> Co-authored-by: Christopher Phillips <christopher.phillips@anchore.com> Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
parent
185d0d1bfa
commit
0748945c83
6 changed files with 691 additions and 82 deletions
|
@ -171,19 +171,14 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
archiveCloser, err := os.Open(j.archivePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to open archive path (%s): %w", j.archivePath, err)
|
||||
}
|
||||
defer archiveCloser.Close()
|
||||
|
||||
// grab and assign digest for the entire archive
|
||||
digests, err := intFile.NewDigestsFromFile(archiveCloser, javaArchiveHashes)
|
||||
digests, err := getDigestsFromArchive(j.archivePath)
|
||||
if err != nil {
|
||||
log.Warnf("failed to create digest for file=%q: %+v", j.archivePath, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 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
|
||||
licenses := pkg.NewLicensesFromLocation(j.location, selectLicenses(manifest)...)
|
||||
/*
|
||||
We should name and version from, in this order:
|
||||
|
@ -192,13 +187,20 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) {
|
|||
3. manifest
|
||||
4. filename
|
||||
*/
|
||||
name, version := j.guessMainPackageNameAndVersionFromPomInfo()
|
||||
name, version, pomLicenses := j.guessMainPackageNameAndVersionFromPomInfo()
|
||||
if name == "" {
|
||||
name = selectName(manifest, j.fileInfo)
|
||||
}
|
||||
if version == "" {
|
||||
version = selectVersion(manifest, j.fileInfo)
|
||||
}
|
||||
if len(licenses) == 0 {
|
||||
// Today we don't have a way to distinguish between licenses from the manifest and licenses from the pom.xml
|
||||
// until the file.Location object can support sub-paths (i.e. paths within archives, recursively; issue https://github.com/anchore/syft/issues/2211).
|
||||
// Until then it's less confusing to use the licenses from the pom.xml only if the manifest did not list any.
|
||||
licenses = append(licenses, pomLicenses...)
|
||||
}
|
||||
|
||||
return &pkg.Package{
|
||||
// TODO: maybe select name should just have a pom properties in it?
|
||||
Name: name,
|
||||
|
@ -218,11 +220,16 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (j *archiveParser) guessMainPackageNameAndVersionFromPomInfo() (string, string) {
|
||||
type parsedPomProject struct {
|
||||
*pkg.PomProject
|
||||
Licenses []pkg.License
|
||||
}
|
||||
|
||||
func (j *archiveParser) guessMainPackageNameAndVersionFromPomInfo() (name, version string, licenses []pkg.License) {
|
||||
pomPropertyMatches := j.fileManifest.GlobMatch(pomPropertiesGlob)
|
||||
pomMatches := j.fileManifest.GlobMatch(pomXMLGlob)
|
||||
var pomPropertiesObject pkg.PomProperties
|
||||
var pomProjectObject pkg.PomProject
|
||||
var pomProjectObject parsedPomProject
|
||||
if len(pomPropertyMatches) == 1 || len(pomMatches) == 1 {
|
||||
// we have exactly 1 pom.properties or pom.xml in the archive; assume it represents the
|
||||
// package we're scanning if the names seem like a plausible match
|
||||
|
@ -238,15 +245,15 @@ func (j *archiveParser) guessMainPackageNameAndVersionFromPomInfo() (string, str
|
|||
}
|
||||
}
|
||||
}
|
||||
name := pomPropertiesObject.ArtifactID
|
||||
if name == "" {
|
||||
name = pomPropertiesObject.ArtifactID
|
||||
if name == "" && pomProjectObject.PomProject != nil {
|
||||
name = pomProjectObject.ArtifactID
|
||||
}
|
||||
version := pomPropertiesObject.Version
|
||||
if version == "" {
|
||||
version = pomPropertiesObject.Version
|
||||
if version == "" && pomProjectObject.PomProject != nil {
|
||||
version = pomProjectObject.Version
|
||||
}
|
||||
return name, version
|
||||
return name, version, pomProjectObject.Licenses
|
||||
}
|
||||
|
||||
// discoverPkgsFromAllMavenFiles parses Maven POM properties/xml for a given
|
||||
|
@ -273,7 +280,7 @@ func (j *archiveParser) discoverPkgsFromAllMavenFiles(parentPkg *pkg.Package) ([
|
|||
}
|
||||
|
||||
for parentPath, propertiesObj := range properties {
|
||||
var pomProject *pkg.PomProject
|
||||
var pomProject *parsedPomProject
|
||||
if proj, exists := projects[parentPath]; exists {
|
||||
pomProject = &proj
|
||||
}
|
||||
|
@ -287,6 +294,22 @@ func (j *archiveParser) discoverPkgsFromAllMavenFiles(parentPkg *pkg.Package) ([
|
|||
return pkgs, nil
|
||||
}
|
||||
|
||||
func getDigestsFromArchive(archivePath string) ([]file.Digest, error) {
|
||||
archiveCloser, err := os.Open(archivePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to open archive path (%s): %w", archivePath, err)
|
||||
}
|
||||
defer archiveCloser.Close()
|
||||
|
||||
// grab and assign digest for the entire archive
|
||||
digests, err := intFile.NewDigestsFromFile(archiveCloser, javaArchiveHashes)
|
||||
if err != nil {
|
||||
log.Warnf("failed to create digest for file=%q: %+v", archivePath, err)
|
||||
}
|
||||
|
||||
return digests, nil
|
||||
}
|
||||
|
||||
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
|
||||
return discoverPkgsFromZip(j.location, j.archivePath, j.contentPath, j.fileManifest, parentPkg)
|
||||
|
@ -388,15 +411,16 @@ func pomPropertiesByParentPath(archivePath string, location file.Location, extra
|
|||
return propertiesByParentPath, nil
|
||||
}
|
||||
|
||||
func pomProjectByParentPath(archivePath string, location file.Location, extractPaths []string) (map[string]pkg.PomProject, error) {
|
||||
func pomProjectByParentPath(archivePath string, location file.Location, extractPaths []string) (map[string]parsedPomProject, error) {
|
||||
contentsOfMavenProjectFiles, err := intFile.ContentsFromZip(archivePath, extractPaths...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to extract maven files: %w", err)
|
||||
}
|
||||
|
||||
projectByParentPath := make(map[string]pkg.PomProject)
|
||||
projectByParentPath := make(map[string]parsedPomProject)
|
||||
for filePath, fileContents := range contentsOfMavenProjectFiles {
|
||||
pomProject, err := parsePomXMLProject(filePath, strings.NewReader(fileContents))
|
||||
// TODO: when we support locations of paths within archives we should start passing the specific pom.xml location object instead of the top jar
|
||||
pomProject, err := parsePomXMLProject(filePath, strings.NewReader(fileContents), location)
|
||||
if err != nil {
|
||||
log.WithFields("contents-path", filePath, "location", location.AccessPath()).Warnf("failed to parse pom.xml: %+v", err)
|
||||
continue
|
||||
|
@ -418,7 +442,7 @@ func pomProjectByParentPath(archivePath string, location file.Location, extractP
|
|||
|
||||
// newPackageFromMavenData processes a single Maven POM properties for a given parent package, returning all listed Java packages found and
|
||||
// associating each discovered package to the given parent package. Note the pom.xml is optional, the pom.properties is not.
|
||||
func newPackageFromMavenData(pomProperties pkg.PomProperties, pomProject *pkg.PomProject, parentPkg *pkg.Package, location file.Location) *pkg.Package {
|
||||
func newPackageFromMavenData(pomProperties pkg.PomProperties, parsedPomProject *parsedPomProject, parentPkg *pkg.Package, location file.Location) *pkg.Package {
|
||||
// keep the artifact name within the virtual path if this package does not match the parent package
|
||||
vPathSuffix := ""
|
||||
groupID := ""
|
||||
|
@ -440,20 +464,27 @@ func newPackageFromMavenData(pomProperties pkg.PomProperties, pomProject *pkg.Po
|
|||
}
|
||||
virtualPath := location.AccessPath() + vPathSuffix
|
||||
|
||||
// discovered props = new package
|
||||
var pkgPomProject *pkg.PomProject
|
||||
licenses := make([]pkg.License, 0)
|
||||
if parsedPomProject != nil {
|
||||
pkgPomProject = parsedPomProject.PomProject
|
||||
licenses = append(licenses, parsedPomProject.Licenses...)
|
||||
}
|
||||
|
||||
p := pkg.Package{
|
||||
Name: pomProperties.ArtifactID,
|
||||
Version: pomProperties.Version,
|
||||
Locations: file.NewLocationSet(
|
||||
location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
Licenses: pkg.NewLicenseSet(licenses...),
|
||||
Language: pkg.Java,
|
||||
Type: pomProperties.PkgTypeIndicated(),
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
Metadata: pkg.JavaMetadata{
|
||||
VirtualPath: virtualPath,
|
||||
PomProperties: &pomProperties,
|
||||
PomProject: pomProject,
|
||||
PomProject: pkgPomProject,
|
||||
Parent: parentPkg,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
@ -17,6 +16,7 @@ import (
|
|||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/license"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
)
|
||||
|
@ -83,11 +83,13 @@ func generateJavaBuildFixture(t *testing.T, fixturePath string) {
|
|||
|
||||
func TestParseJar(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
expected map[string]pkg.Package
|
||||
ignoreExtras []string
|
||||
}{
|
||||
{
|
||||
name: "example-jenkins-plugin",
|
||||
fixture: "test-fixtures/java-builds/packages/example-jenkins-plugin.hpi",
|
||||
ignoreExtras: []string{
|
||||
"Plugin-Version", // has dynamic date
|
||||
|
@ -146,6 +148,7 @@ func TestParseJar(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "example-java-app-gradle",
|
||||
fixture: "test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar",
|
||||
expected: map[string]pkg.Package{
|
||||
"example-java-app-gradle": {
|
||||
|
@ -172,6 +175,16 @@ func TestParseJar(t *testing.T) {
|
|||
Language: pkg.Java,
|
||||
Type: pkg.JavaPkg,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromFields(
|
||||
"Apache 2",
|
||||
"http://www.apache.org/licenses/LICENSE-2.0.txt",
|
||||
func() *file.Location {
|
||||
l := file.NewLocation("test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar")
|
||||
return &l
|
||||
}(),
|
||||
),
|
||||
),
|
||||
Metadata: pkg.JavaMetadata{
|
||||
// ensure that nested packages with different names than that of the parent are appended as
|
||||
// a suffix on the virtual path with a colon separator between group name and artifact name
|
||||
|
@ -196,6 +209,7 @@ func TestParseJar(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "example-java-app-maven",
|
||||
fixture: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar",
|
||||
ignoreExtras: []string{
|
||||
"Build-Jdk", // can't guarantee the JDK used at build time
|
||||
|
@ -231,9 +245,19 @@ func TestParseJar(t *testing.T) {
|
|||
},
|
||||
},
|
||||
"joda-time": {
|
||||
Name: "joda-time",
|
||||
Version: "2.9.2",
|
||||
PURL: "pkg:maven/joda-time/joda-time@2.9.2",
|
||||
Name: "joda-time",
|
||||
Version: "2.9.2",
|
||||
PURL: "pkg:maven/joda-time/joda-time@2.9.2",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromFields(
|
||||
"Apache 2",
|
||||
"http://www.apache.org/licenses/LICENSE-2.0.txt",
|
||||
func() *file.Location {
|
||||
l := file.NewLocation("test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar")
|
||||
return &l
|
||||
}(),
|
||||
),
|
||||
),
|
||||
Language: pkg.Java,
|
||||
Type: pkg.JavaPkg,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
|
@ -263,7 +287,7 @@ func TestParseJar(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(path.Base(test.fixture), func(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
|
||||
generateJavaBuildFixture(t, test.fixture)
|
||||
|
||||
|
@ -618,7 +642,7 @@ func Test_newPackageFromMavenData(t *testing.T) {
|
|||
tests := []struct {
|
||||
name string
|
||||
props pkg.PomProperties
|
||||
project *pkg.PomProject
|
||||
project *parsedPomProject
|
||||
parent *pkg.Package
|
||||
expectedParent pkg.Package
|
||||
expectedPackage *pkg.Package
|
||||
|
@ -687,18 +711,29 @@ func Test_newPackageFromMavenData(t *testing.T) {
|
|||
ArtifactID: "some-artifact-id",
|
||||
Version: "1.0",
|
||||
},
|
||||
project: &pkg.PomProject{
|
||||
Parent: &pkg.PomParent{
|
||||
GroupID: "some-parent-group-id",
|
||||
ArtifactID: "some-parent-artifact-id",
|
||||
Version: "1.0-parent",
|
||||
project: &parsedPomProject{
|
||||
PomProject: &pkg.PomProject{
|
||||
Parent: &pkg.PomParent{
|
||||
GroupID: "some-parent-group-id",
|
||||
ArtifactID: "some-parent-artifact-id",
|
||||
Version: "1.0-parent",
|
||||
},
|
||||
Name: "some-name",
|
||||
GroupID: "some-group-id",
|
||||
ArtifactID: "some-artifact-id",
|
||||
Version: "1.0",
|
||||
Description: "desc",
|
||||
URL: "aweso.me",
|
||||
},
|
||||
Licenses: []pkg.License{
|
||||
{
|
||||
Value: "MIT",
|
||||
SPDXExpression: "MIT",
|
||||
Type: license.Declared,
|
||||
URLs: internal.NewStringSet("https://opensource.org/licenses/MIT"),
|
||||
Locations: file.NewLocationSet(file.NewLocation("some-license-path")),
|
||||
},
|
||||
},
|
||||
Name: "some-name",
|
||||
GroupID: "some-group-id",
|
||||
ArtifactID: "some-artifact-id",
|
||||
Version: "1.0",
|
||||
Description: "desc",
|
||||
URL: "aweso.me",
|
||||
},
|
||||
parent: &pkg.Package{
|
||||
Name: "some-parent-name",
|
||||
|
@ -727,6 +762,15 @@ func Test_newPackageFromMavenData(t *testing.T) {
|
|||
Language: pkg.Java,
|
||||
Type: pkg.JavaPkg,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.License{
|
||||
Value: "MIT",
|
||||
SPDXExpression: "MIT",
|
||||
Type: license.Declared,
|
||||
URLs: internal.NewStringSet("https://opensource.org/licenses/MIT"),
|
||||
Locations: file.NewLocationSet(file.NewLocation("some-license-path")),
|
||||
},
|
||||
),
|
||||
Metadata: pkg.JavaMetadata{
|
||||
VirtualPath: virtualPath + ":" + "some-group-id" + ":" + "some-artifact-id",
|
||||
PomProperties: &pkg.PomProperties{
|
||||
|
|
|
@ -49,28 +49,51 @@ func parserPomXML(_ file.Resolver, _ *generic.Environment, reader file.LocationR
|
|||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
func parsePomXMLProject(path string, reader io.Reader) (*pkg.PomProject, error) {
|
||||
func parsePomXMLProject(path string, reader io.Reader, location file.Location) (*parsedPomProject, error) {
|
||||
project, err := decodePomXML(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newPomProject(path, project), nil
|
||||
return newPomProject(path, project, location), nil
|
||||
}
|
||||
|
||||
func newPomProject(path string, p gopom.Project) *pkg.PomProject {
|
||||
func newPomProject(path string, p gopom.Project, location file.Location) *parsedPomProject {
|
||||
artifactID := safeString(p.ArtifactID)
|
||||
name := safeString(p.Name)
|
||||
projectURL := safeString(p.URL)
|
||||
|
||||
var licenses []pkg.License
|
||||
if p.Licenses != nil {
|
||||
for _, license := range *p.Licenses {
|
||||
var licenseName, licenseURL string
|
||||
if license.Name != nil {
|
||||
licenseName = *license.Name
|
||||
}
|
||||
if license.URL != nil {
|
||||
licenseURL = *license.URL
|
||||
}
|
||||
|
||||
if licenseName == "" && licenseURL == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
licenses = append(licenses, pkg.NewLicenseFromFields(licenseName, licenseURL, &location))
|
||||
}
|
||||
}
|
||||
|
||||
log.WithFields("path", path, "artifactID", artifactID, "name", name, "projectURL", projectURL).Trace("parsing pom.xml")
|
||||
return &pkg.PomProject{
|
||||
Path: path,
|
||||
Parent: pomParent(p, p.Parent),
|
||||
GroupID: resolveProperty(p, p.GroupID, "groupId"),
|
||||
ArtifactID: artifactID,
|
||||
Version: resolveProperty(p, p.Version, "version"),
|
||||
Name: name,
|
||||
Description: cleanDescription(p.Description),
|
||||
URL: projectURL,
|
||||
return &parsedPomProject{
|
||||
PomProject: &pkg.PomProject{
|
||||
Path: path,
|
||||
Parent: pomParent(p, p.Parent),
|
||||
GroupID: resolveProperty(p, p.GroupID, "groupId"),
|
||||
ArtifactID: artifactID,
|
||||
Version: resolveProperty(p, p.Version, "version"),
|
||||
Name: name,
|
||||
Description: cleanDescription(p.Description),
|
||||
URL: projectURL,
|
||||
},
|
||||
Licenses: licenses,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,9 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
"github.com/vifraa/gopom"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/license"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
)
|
||||
|
@ -293,33 +295,79 @@ func Test_parseCommonsTextPomXMLProject(t *testing.T) {
|
|||
}
|
||||
|
||||
func Test_parsePomXMLProject(t *testing.T) {
|
||||
// TODO: ideally we would have the path to the contained pom.xml, not the jar
|
||||
jarLocation := file.NewLocation("path/to/archive.jar")
|
||||
tests := []struct {
|
||||
expected pkg.PomProject
|
||||
name string
|
||||
expected parsedPomProject
|
||||
}{
|
||||
{
|
||||
expected: pkg.PomProject{
|
||||
Path: "test-fixtures/pom/commons-codec.pom.xml",
|
||||
Parent: &pkg.PomParent{
|
||||
GroupID: "org.apache.commons",
|
||||
ArtifactID: "commons-parent",
|
||||
Version: "42",
|
||||
name: "go case",
|
||||
expected: parsedPomProject{
|
||||
PomProject: &pkg.PomProject{
|
||||
Path: "test-fixtures/pom/commons-codec.pom.xml",
|
||||
Parent: &pkg.PomParent{
|
||||
GroupID: "org.apache.commons",
|
||||
ArtifactID: "commons-parent",
|
||||
Version: "42",
|
||||
},
|
||||
GroupID: "commons-codec",
|
||||
ArtifactID: "commons-codec",
|
||||
Version: "1.11",
|
||||
Name: "Apache Commons Codec",
|
||||
Description: "The Apache Commons Codec package contains simple encoder and decoders for various formats such as Base64 and Hexadecimal. In addition to these widely used encoders and decoders, the codec package also maintains a collection of phonetic encoding utilities.",
|
||||
URL: "http://commons.apache.org/proper/commons-codec/",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with license data",
|
||||
expected: parsedPomProject{
|
||||
PomProject: &pkg.PomProject{
|
||||
Path: "test-fixtures/pom/neo4j-license-maven-plugin.pom.xml",
|
||||
Parent: &pkg.PomParent{
|
||||
GroupID: "org.sonatype.oss",
|
||||
ArtifactID: "oss-parent",
|
||||
Version: "7",
|
||||
},
|
||||
GroupID: "org.neo4j.build.plugins",
|
||||
ArtifactID: "license-maven-plugin",
|
||||
Version: "4-SNAPSHOT",
|
||||
Name: "${project.artifactId}", // TODO: this is not an ideal answer
|
||||
Description: "Maven 2 plugin to check and update license headers in source files",
|
||||
URL: "http://components.neo4j.org/${project.artifactId}/${project.version}", // TODO: this is not an ideal answer
|
||||
},
|
||||
Licenses: []pkg.License{
|
||||
{
|
||||
Value: "The Apache Software License, Version 2.0",
|
||||
SPDXExpression: "", // TODO: ideally we would parse this title to get Apache-2.0 (created issue #2210 https://github.com/anchore/syft/issues/2210)
|
||||
Type: license.Declared,
|
||||
URLs: internal.NewStringSet("http://www.apache.org/licenses/LICENSE-2.0.txt"),
|
||||
Locations: file.NewLocationSet(jarLocation),
|
||||
},
|
||||
{
|
||||
Value: "MIT",
|
||||
SPDXExpression: "MIT",
|
||||
Type: license.Declared,
|
||||
URLs: internal.NewStringSet(),
|
||||
Locations: file.NewLocationSet(jarLocation),
|
||||
},
|
||||
{
|
||||
Type: license.Declared,
|
||||
URLs: internal.NewStringSet("https://opensource.org/license/unlicense/"),
|
||||
Locations: file.NewLocationSet(jarLocation),
|
||||
},
|
||||
},
|
||||
GroupID: "commons-codec",
|
||||
ArtifactID: "commons-codec",
|
||||
Version: "1.11",
|
||||
Name: "Apache Commons Codec",
|
||||
Description: "The Apache Commons Codec package contains simple encoder and decoders for various formats such as Base64 and Hexadecimal. In addition to these widely used encoders and decoders, the codec package also maintains a collection of phonetic encoding utilities.",
|
||||
URL: "http://commons.apache.org/proper/commons-codec/",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.expected.Path, func(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
fixture, err := os.Open(test.expected.Path)
|
||||
assert.NoError(t, err)
|
||||
|
||||
actual, err := parsePomXMLProject(fixture.Name(), fixture)
|
||||
actual, err := parsePomXMLProject(fixture.Name(), fixture, jarLocation)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, &test.expected, actual)
|
||||
|
|
|
@ -0,0 +1,459 @@
|
|||
<!-- sourced from https://github.com/neo4j/license-maven-plugin/blob/3/pom.xml -->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.sonatype.oss</groupId>
|
||||
<artifactId>oss-parent</artifactId>
|
||||
<version>7</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
|
||||
<groupId>org.neo4j.build.plugins</groupId>
|
||||
<artifactId>license-maven-plugin</artifactId>å
|
||||
<version>4-SNAPSHOT</version>
|
||||
<packaging>maven-plugin</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>Maven 2 plugin to check and update license headers in source files</description>
|
||||
<url>http://components.neo4j.org/${project.artifactId}/${project.version}</url>
|
||||
<inceptionYear>2008</inceptionYear>
|
||||
|
||||
<!--
|
||||
Properties
|
||||
-->
|
||||
|
||||
<properties>
|
||||
<jdk>1.6</jdk>
|
||||
<jdk.version>1.6</jdk.version>
|
||||
</properties>
|
||||
|
||||
<!--
|
||||
Versioning system
|
||||
-->
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/neo4j/license-maven-plugin.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:neo4j/license-maven-plugin.git</developerConnection>
|
||||
<url>https://github.com/neo4j/license-maven-plugin</url>
|
||||
</scm>
|
||||
|
||||
<!--
|
||||
Project settings
|
||||
-->
|
||||
|
||||
<organization>
|
||||
<name>Mathieu Carbou</name>
|
||||
<url>http://mathieu.carbou.free.fr/</url>
|
||||
</organization>
|
||||
|
||||
<licenses>
|
||||
<!-- this has been modified for the purposes of a syft test -->
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
<license>
|
||||
<name>MIT</name>
|
||||
</license>
|
||||
<license>
|
||||
<url>https://opensource.org/license/unlicense/</url>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>mimilowns</id>
|
||||
<name>Cédric</name>
|
||||
<email>mimilowns@gmail.com</email>
|
||||
<timezone>+1</timezone>
|
||||
<roles>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>mathieu.carbou</id>
|
||||
<name>Mathieu Carbou</name>
|
||||
<email>mathieu.carbou@gmail.com</email>
|
||||
<organization>Mycila</organization>
|
||||
<organizationUrl>http://mathieu.carbou.free.fr/</organizationUrl>
|
||||
<timezone>-5</timezone>
|
||||
<roles>
|
||||
<role>project owner</role>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<issueManagement>
|
||||
<system>Google Code</system>
|
||||
<url>http://code.google.com/p/${project.artifactId}/issues/list</url>
|
||||
</issueManagement>
|
||||
|
||||
<ciManagement />
|
||||
|
||||
<mailingLists>
|
||||
<mailingList>
|
||||
<name>maven-license-plugin-announce</name>
|
||||
<subscribe>maven-license-plugin-announce-subscribe@googlegroups.com</subscribe>
|
||||
<unsubscribe>maven-license-plugin-announce-unsubscribe@googlegroups.com</unsubscribe>
|
||||
<archive>http://groups.google.com/group/maven-license-plugin-announce</archive>
|
||||
</mailingList>
|
||||
<mailingList>
|
||||
<name>maven-license-plugin-codesite</name>
|
||||
<subscribe>maven-license-plugin-codesite-subscribe@googlegroups.com</subscribe>
|
||||
<unsubscribe>maven-license-plugin-codesite-unsubscribe@googlegroups.com</unsubscribe>
|
||||
<archive>http://groups.google.com/group/maven-license-plugin-codesite</archive>
|
||||
</mailingList>
|
||||
</mailingLists>
|
||||
|
||||
<!--
|
||||
Distributions
|
||||
-->
|
||||
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>sonatype-nexus-staging</id>
|
||||
<name>Nexus Release Repository</name>
|
||||
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>snapshots@m2.neo4j.org</id>
|
||||
<name>snapshots@m2.neo4j.org</name>
|
||||
<url>http://m2.neo4j.org/content/repositories/snapshots</url>
|
||||
</snapshotRepository>
|
||||
<site>
|
||||
<id>neo4j-site</id>
|
||||
<url>scpexe://components.neo4j.org/home/neo/components/${project.artifactId}/${project.version}</url>
|
||||
</site>
|
||||
</distributionManagement>
|
||||
|
||||
<!--
|
||||
BUILD
|
||||
-->
|
||||
|
||||
<build>
|
||||
<extensions>
|
||||
<extension>
|
||||
<groupId>org.apache.maven.wagon</groupId>
|
||||
<artifactId>wagon-webdav</artifactId>
|
||||
<version>1.0-beta-2</version>
|
||||
</extension>
|
||||
</extensions>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<version>2.1.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-ipojo-plugin</artifactId>
|
||||
<version>1.6.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.mycila.maven-license-plugin</groupId>
|
||||
<artifactId>maven-license-plugin</artifactId>
|
||||
<version>1.8.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<version>1.4</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.1.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.3.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.7</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>2.5</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
<version>2.4.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-invoker-plugin</artifactId>
|
||||
<version>1.5</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>1.3.3</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>2.2-beta-5</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>2.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.3.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-release-plugin</artifactId>
|
||||
<version>2.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.4</version>
|
||||
<configuration>
|
||||
<keyname>Neo Technology Build Server</keyname>
|
||||
<useAgent>true</useAgent>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<!-- for maven plugins -->
|
||||
<plugin>
|
||||
<artifactId>maven-plugin-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
<configuration>
|
||||
<filesets>
|
||||
<fileset>
|
||||
<directory>.clover</directory>
|
||||
</fileset>
|
||||
<fileset>
|
||||
<directory>test-output</directory>
|
||||
</fileset>
|
||||
</filesets>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- compilation -->
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${jdk}</source>
|
||||
<target>${jdk}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- testing -->
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
</plugin>
|
||||
<!-- packaging -->
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
|
||||
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-remote-resources-plugin</artifactId>
|
||||
<version>1.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>process</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<resourceBundles>
|
||||
<resourceBundle>org.apache:apache-jar-resource-bundle:1.3</resourceBundle>
|
||||
</resourceBundles>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>project</descriptorRef>
|
||||
</descriptorRefs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- releasing -->
|
||||
<plugin>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<updateReleaseInfo>true</updateReleaseInfo>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-release-plugin</artifactId>
|
||||
</plugin>
|
||||
<!-- documentation -->
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.mycila.maven-license-plugin</groupId>
|
||||
<artifactId>maven-license-plugin</artifactId>
|
||||
<version>1.9.0</version>
|
||||
<configuration>
|
||||
<header>${basedir}/src/etc/header.txt</header>
|
||||
<failIfMissing>true</failIfMissing>
|
||||
<excludes>
|
||||
<exclude>.gitignore</exclude>
|
||||
<exclude>LICENSE.txt</exclude>
|
||||
<exclude>NOTICE.txt</exclude>
|
||||
<exclude>src/test/data/**</exclude>
|
||||
<exclude>src/test/integration/**</exclude>
|
||||
<exclude>src/test/resources/**</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- automatically handle it tests -->
|
||||
<plugin>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
<configuration>
|
||||
<filesets>
|
||||
<fileset>
|
||||
<directory>it</directory>
|
||||
<includes>
|
||||
<include>target/**</include>
|
||||
<include>*/target/**</include>
|
||||
</includes>
|
||||
</fileset>
|
||||
<fileset>
|
||||
<directory>target</directory>
|
||||
</fileset>
|
||||
</filesets>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<inherited>true</inherited>
|
||||
<artifactId>maven-invoker-plugin</artifactId>
|
||||
<configuration>
|
||||
<projectsDirectory>it</projectsDirectory>
|
||||
<showErrors>true</showErrors>
|
||||
<streamLogs>true</streamLogs>
|
||||
<skipInstallation>${ittest.skip}</skipInstallation>
|
||||
<skipInvocation>${ittest.skip}</skipInvocation>
|
||||
<properties>
|
||||
<target.version>${target.version}</target.version>
|
||||
</properties>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
<pomIncludes>
|
||||
<pomInclude>**/pom.xml</pomInclude>
|
||||
</pomIncludes>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>integration-test</id>
|
||||
<goals>
|
||||
<goal>install</goal>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<!--
|
||||
LIBS
|
||||
-->
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-plugin-api</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mycila.xmltool</groupId>
|
||||
<artifactId>xmltool</artifactId>
|
||||
<version>3.3</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-project</artifactId>
|
||||
<version>3.0-alpha-2</version>
|
||||
<scope>compile</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>junit</artifactId>
|
||||
<groupId>junit</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.plexus</groupId>
|
||||
<artifactId>plexus-utils</artifactId>
|
||||
<version>2.0.5</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- testing -->
|
||||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
<artifactId>testng</artifactId>
|
||||
<version>5.7</version>
|
||||
<classifier>jdk15</classifier>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>junit</artifactId>
|
||||
<groupId>junit</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-embedder</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.plugin-testing</groupId>
|
||||
<artifactId>maven-plugin-testing-harness</artifactId>
|
||||
<version>2.0-alpha-1</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>junit</artifactId>
|
||||
<groupId>junit</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -60,24 +60,17 @@ func (l Licenses) Swap(i, j int) {
|
|||
}
|
||||
|
||||
func NewLicense(value string) License {
|
||||
spdxExpression, err := license.ParseExpression(value)
|
||||
if err != nil {
|
||||
log.Trace("unable to parse license expression for %q: %w", value, err)
|
||||
}
|
||||
|
||||
return License{
|
||||
Value: value,
|
||||
SPDXExpression: spdxExpression,
|
||||
Type: license.Declared,
|
||||
URLs: internal.NewStringSet(),
|
||||
Locations: file.NewLocationSet(),
|
||||
}
|
||||
return NewLicenseFromType(value, license.Declared)
|
||||
}
|
||||
|
||||
func NewLicenseFromType(value string, t license.Type) License {
|
||||
spdxExpression, err := license.ParseExpression(value)
|
||||
if err != nil {
|
||||
log.Trace("unable to parse license expression: %w", err)
|
||||
var spdxExpression string
|
||||
if value != "" {
|
||||
var err error
|
||||
spdxExpression, err = license.ParseExpression(value)
|
||||
if err != nil {
|
||||
log.Trace("unable to parse license expression: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return License{
|
||||
|
@ -124,6 +117,17 @@ func NewLicenseFromURLs(value string, urls ...string) License {
|
|||
return l
|
||||
}
|
||||
|
||||
func NewLicenseFromFields(value, url string, location *file.Location) License {
|
||||
l := NewLicense(value)
|
||||
if location != nil {
|
||||
l.Locations.Add(*location)
|
||||
}
|
||||
if url != "" {
|
||||
l.URLs.Add(url)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// this is a bit of a hack to not infinitely recurse when hashing a license
|
||||
func (s License) Merge(l License) (*License, error) {
|
||||
sHash, err := artifact.IDByHash(s)
|
||||
|
|
Loading…
Reference in a new issue