From 0748945c8341a06415ac80e624b84e8d3f548d39 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Tue, 10 Oct 2023 18:09:44 +0100 Subject: [PATCH] =?UTF-8?q?Parse=20the=20Maven=20license=20from=20the=20po?= =?UTF-8?q?m.xml=20if=20not=20contained=20in=20the=20mani=E2=80=A6=20(#211?= =?UTF-8?q?5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Parse the Maven license from the pom.xml if not contained in the manifest Signed-off-by: Colm O hEigeartaigh * chore: restore 10.0.2 schema Signed-off-by: Christopher Phillips * chore: generate new 11.0.1 schema Signed-off-by: Christopher Phillips * refactor: remove schema change Signed-off-by: Christopher Phillips * test: update unit tests to align with new pattern Signed-off-by: Christopher Phillips * chore: pr feedback Signed-off-by: Christopher Phillips * chore: remove struct tags Signed-off-by: Christopher Phillips * keep license name and url semantics preserved on the pkg object Signed-off-by: Alex Goodman --------- Signed-off-by: Colm O hEigeartaigh Signed-off-by: Christopher Phillips Signed-off-by: Alex Goodman Co-authored-by: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com> Co-authored-by: Christopher Phillips Co-authored-by: Alex Goodman --- syft/pkg/cataloger/java/archive_parser.go | 77 ++- .../pkg/cataloger/java/archive_parser_test.go | 78 ++- syft/pkg/cataloger/java/parse_pom_xml.go | 47 +- syft/pkg/cataloger/java/parse_pom_xml_test.go | 78 ++- .../pom/neo4j-license-maven-plugin.pom.xml | 459 ++++++++++++++++++ syft/pkg/license.go | 34 +- 6 files changed, 691 insertions(+), 82 deletions(-) create mode 100644 syft/pkg/cataloger/java/test-fixtures/pom/neo4j-license-maven-plugin.pom.xml diff --git a/syft/pkg/cataloger/java/archive_parser.go b/syft/pkg/cataloger/java/archive_parser.go index f6570cb79..76de15348 100644 --- a/syft/pkg/cataloger/java/archive_parser.go +++ b/syft/pkg/cataloger/java/archive_parser.go @@ -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, }, } diff --git a/syft/pkg/cataloger/java/archive_parser_test.go b/syft/pkg/cataloger/java/archive_parser_test.go index 4635a9c98..78cbb2574 100644 --- a/syft/pkg/cataloger/java/archive_parser_test.go +++ b/syft/pkg/cataloger/java/archive_parser_test.go @@ -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{ diff --git a/syft/pkg/cataloger/java/parse_pom_xml.go b/syft/pkg/cataloger/java/parse_pom_xml.go index a6f5340cf..eb0f9d9b2 100644 --- a/syft/pkg/cataloger/java/parse_pom_xml.go +++ b/syft/pkg/cataloger/java/parse_pom_xml.go @@ -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, } } diff --git a/syft/pkg/cataloger/java/parse_pom_xml_test.go b/syft/pkg/cataloger/java/parse_pom_xml_test.go index 9e79fe96e..acbadf883 100644 --- a/syft/pkg/cataloger/java/parse_pom_xml_test.go +++ b/syft/pkg/cataloger/java/parse_pom_xml_test.go @@ -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) diff --git a/syft/pkg/cataloger/java/test-fixtures/pom/neo4j-license-maven-plugin.pom.xml b/syft/pkg/cataloger/java/test-fixtures/pom/neo4j-license-maven-plugin.pom.xml new file mode 100644 index 000000000..dc8a072ad --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/pom/neo4j-license-maven-plugin.pom.xml @@ -0,0 +1,459 @@ + + + + 4.0.0 + + org.sonatype.oss + oss-parent + 7 + + + + org.neo4j.build.plugins + license-maven-pluginå + 4-SNAPSHOT + maven-plugin + + ${project.artifactId} + Maven 2 plugin to check and update license headers in source files + http://components.neo4j.org/${project.artifactId}/${project.version} + 2008 + + + + + 1.6 + 1.6 + + + + + + scm:git:git://github.com/neo4j/license-maven-plugin.git + scm:git:git@github.com:neo4j/license-maven-plugin.git + https://github.com/neo4j/license-maven-plugin + + + + + + Mathieu Carbou + http://mathieu.carbou.free.fr/ + + + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + MIT + + + https://opensource.org/license/unlicense/ + + + + + + mimilowns + Cédric + mimilowns@gmail.com + +1 + + developer + + + + mathieu.carbou + Mathieu Carbou + mathieu.carbou@gmail.com + Mycila + http://mathieu.carbou.free.fr/ + -5 + + project owner + developer + + + + + + Google Code + http://code.google.com/p/${project.artifactId}/issues/list + + + + + + + maven-license-plugin-announce + maven-license-plugin-announce-subscribe@googlegroups.com + maven-license-plugin-announce-unsubscribe@googlegroups.com + http://groups.google.com/group/maven-license-plugin-announce + + + maven-license-plugin-codesite + maven-license-plugin-codesite-subscribe@googlegroups.com + maven-license-plugin-codesite-unsubscribe@googlegroups.com + http://groups.google.com/group/maven-license-plugin-codesite + + + + + + + + sonatype-nexus-staging + Nexus Release Repository + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + snapshots@m2.neo4j.org + snapshots@m2.neo4j.org + http://m2.neo4j.org/content/repositories/snapshots + + + neo4j-site + scpexe://components.neo4j.org/home/neo/components/${project.artifactId}/${project.version} + + + + + + + + + org.apache.maven.wagon + wagon-webdav + 1.0-beta-2 + + + + + + org.apache.felix + maven-bundle-plugin + 2.1.0 + + + org.apache.felix + maven-ipojo-plugin + 1.6.0 + + + com.mycila.maven-license-plugin + maven-license-plugin + 1.8.0 + + + maven-antrun-plugin + 1.4 + + + maven-source-plugin + 2.1.2 + + + maven-jar-plugin + 2.3.1 + + + maven-javadoc-plugin + 2.7 + + + maven-deploy-plugin + 2.5 + + + maven-clean-plugin + 2.4.1 + + + maven-invoker-plugin + 1.5 + + + maven-shade-plugin + 1.3.3 + + + maven-assembly-plugin + 2.2-beta-5 + + + maven-dependency-plugin + 2.1 + + + maven-compiler-plugin + 2.3.1 + + + maven-release-plugin + 2.0 + + + maven-surefire-plugin + 2.6 + + + org.apache.maven.plugins + maven-gpg-plugin + 1.4 + + Neo Technology Build Server + true + + + + + + + + maven-plugin-plugin + 2.6 + + + maven-clean-plugin + + + + .clover + + + test-output + + + + + + + maven-compiler-plugin + + ${jdk} + ${jdk} + + + + + maven-surefire-plugin + + + + maven-jar-plugin + + + + true + true + + + + + + maven-source-plugin + + + attach-sources + + jar + + + + + + maven-remote-resources-plugin + 1.1 + + + + process + + + + org.apache:apache-jar-resource-bundle:1.3 + + + + + + + maven-assembly-plugin + + + project + + + + + + maven-deploy-plugin + + true + + + + maven-release-plugin + + + + maven-dependency-plugin + + + com.mycila.maven-license-plugin + maven-license-plugin + 1.9.0 + +
${basedir}/src/etc/header.txt
+ true + + .gitignore + LICENSE.txt + NOTICE.txt + src/test/data/** + src/test/integration/** + src/test/resources/** + +
+ + + + check + + + +
+ + + maven-clean-plugin + + + + it + + target/** + */target/** + + + + target + + + + + + true + maven-invoker-plugin + + it + true + true + ${ittest.skip} + ${ittest.skip} + + ${target.version} + + + test + + + **/pom.xml + + + + + integration-test + + install + run + + + + +
+
+ + + + + + org.apache.maven + maven-plugin-api + 3.0.1 + compile + + + com.mycila.xmltool + xmltool + 3.3 + compile + + + org.apache.maven + maven-project + 3.0-alpha-2 + compile + + + junit + junit + + + + + org.codehaus.plexus + plexus-utils + 2.0.5 + compile + + + + org.testng + testng + 5.7 + jdk15 + test + + + junit + junit + + + + + org.apache.maven + maven-embedder + 3.0.1 + test + + + org.apache.maven.plugin-testing + maven-plugin-testing-harness + 2.0-alpha-1 + test + + + junit + junit + + + + + +
diff --git a/syft/pkg/license.go b/syft/pkg/license.go index 6e681da63..a6270ec9e 100644 --- a/syft/pkg/license.go +++ b/syft/pkg/license.go @@ -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)