From 26cdbfc2991fdd21999e094df9c2457cba92ba15 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Wed, 1 Nov 2023 17:10:17 +0000 Subject: [PATCH] fix: syft does not handle the case of parsing a jar with multiple poms (#2231) --------- Signed-off-by: Colm O hEigeartaigh Signed-off-by: Christopher Phillips Co-authored-by: Christopher Phillips --- syft/pkg/cataloger/java/archive_parser.go | 22 +-- .../pkg/cataloger/java/archive_parser_test.go | 77 ++++++++++ .../java/test-fixtures/jar-metadata/Makefile | 4 + .../java/test-fixtures/jar-metadata/README.md | 8 ++ .../META-INF/MANIFEST.MF | 4 + .../api-all/pom.properties | 4 + .../org.apache.directory.api/api-all/pom.xml | 135 ++++++++++++++++++ .../api-asn1-api/pom.properties | 4 + .../api-asn1-api/pom.xml | 99 +++++++++++++ 9 files changed, 346 insertions(+), 11 deletions(-) create mode 100644 syft/pkg/cataloger/java/test-fixtures/jar-metadata/api-all-2.0.0-sources/META-INF/MANIFEST.MF create mode 100644 syft/pkg/cataloger/java/test-fixtures/jar-metadata/api-all-2.0.0-sources/META-INF/maven/org.apache.directory.api/api-all/pom.properties create mode 100644 syft/pkg/cataloger/java/test-fixtures/jar-metadata/api-all-2.0.0-sources/META-INF/maven/org.apache.directory.api/api-all/pom.xml create mode 100644 syft/pkg/cataloger/java/test-fixtures/jar-metadata/api-all-2.0.0-sources/META-INF/maven/org.apache.directory.api/api-asn1-api/pom.properties create mode 100644 syft/pkg/cataloger/java/test-fixtures/jar-metadata/api-all-2.0.0-sources/META-INF/maven/org.apache.directory.api/api-asn1-api/pom.xml diff --git a/syft/pkg/cataloger/java/archive_parser.go b/syft/pkg/cataloger/java/archive_parser.go index 026b7af4c..6653738db 100644 --- a/syft/pkg/cataloger/java/archive_parser.go +++ b/syft/pkg/cataloger/java/archive_parser.go @@ -263,21 +263,21 @@ func (j *archiveParser) guessMainPackageNameAndVersionFromPomInfo() (name, versi pomMatches := j.fileManifest.GlobMatch(false, pomXMLGlob) var pomPropertiesObject pkg.JavaPomProperties 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 - properties, _ := pomPropertiesByParentPath(j.archivePath, j.location, pomPropertyMatches) - projects, _ := pomProjectByParentPath(j.archivePath, j.location, pomMatches) - for parentPath, propertiesObj := range properties { - if artifactIDMatchesFilename(propertiesObj.ArtifactID, j.fileInfo.name) { - pomPropertiesObject = propertiesObj - if proj, exists := projects[parentPath]; exists { - pomProjectObject = proj - } + // Find the pom.properties/pom.xml if the names seem like a plausible match + properties, _ := pomPropertiesByParentPath(j.archivePath, j.location, pomPropertyMatches) + projects, _ := pomProjectByParentPath(j.archivePath, j.location, pomMatches) + + for parentPath, propertiesObj := range properties { + if artifactIDMatchesFilename(propertiesObj.ArtifactID, j.fileInfo.name) { + pomPropertiesObject = propertiesObj + if proj, exists := projects[parentPath]; exists { + pomProjectObject = proj + break } } } + name = pomPropertiesObject.ArtifactID if name == "" && pomProjectObject != nil { name = pomProjectObject.ArtifactID diff --git a/syft/pkg/cataloger/java/archive_parser_test.go b/syft/pkg/cataloger/java/archive_parser_test.go index 40927d213..3d05fb868 100644 --- a/syft/pkg/cataloger/java/archive_parser_test.go +++ b/syft/pkg/cataloger/java/archive_parser_test.go @@ -1036,6 +1036,7 @@ func Test_parseJavaArchive_regressions(t *testing.T) { fixtureName string expectedPkgs []pkg.Package expectedRelationships []artifact.Relationship + assignParent bool want bool }{ { @@ -1146,10 +1147,74 @@ func Test_parseJavaArchive_regressions(t *testing.T) { }, }, }, + { + name: "multiple pom for parent selection regression (pr 2231)", + fixtureName: "api-all-2.0.0-sources", + assignParent: true, + expectedPkgs: []pkg.Package{ + { + Name: "api-all", + Version: "2.0.0", + Type: pkg.JavaPkg, + Language: pkg.Java, + PURL: "pkg:maven/org.apache.directory.api/api-all@2.0.0", + Locations: file.NewLocationSet(file.NewLocation("test-fixtures/jar-metadata/cache/api-all-2.0.0-sources.jar")), + Metadata: pkg.JavaArchive{ + VirtualPath: "test-fixtures/jar-metadata/cache/api-all-2.0.0-sources.jar", + Manifest: &pkg.JavaManifest{ + Main: map[string]string{ + "Build-Jdk": "1.8.0_191", + "Built-By": "elecharny", + "Created-By": "Apache Maven 3.6.0", + "Manifest-Version": "1.0", + }, + }, + PomProperties: &pkg.JavaPomProperties{ + Path: "META-INF/maven/org.apache.directory.api/api-all/pom.properties", + GroupID: "org.apache.directory.api", + ArtifactID: "api-all", + Version: "2.0.0", + }, + }, + }, + { + Name: "api-asn1-api", + Version: "2.0.0", + PURL: "pkg:maven/org.apache.directory.api/api-asn1-api@2.0.0", + Locations: file.NewLocationSet(file.NewLocation("test-fixtures/jar-metadata/cache/api-all-2.0.0-sources.jar")), + Type: pkg.JavaPkg, + Language: pkg.Java, + Metadata: pkg.JavaArchive{ + VirtualPath: "test-fixtures/jar-metadata/cache/api-all-2.0.0-sources.jar:org.apache.directory.api:api-asn1-api", + PomProperties: &pkg.JavaPomProperties{ + Path: "META-INF/maven/org.apache.directory.api/api-asn1-api/pom.properties", + GroupID: "org.apache.directory.api", + ArtifactID: "api-asn1-api", + Version: "2.0.0", + }, + PomProject: &pkg.JavaPomProject{ + Path: "META-INF/maven/org.apache.directory.api/api-asn1-api/pom.xml", + ArtifactID: "api-asn1-api", + Name: "Apache Directory API ASN.1 API", + Description: "ASN.1 API", + Parent: &pkg.JavaPomParent{ + GroupID: "org.apache.directory.api", + ArtifactID: "api-asn1-parent", + Version: "2.0.0", + }, + }, + Parent: nil, + }, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gap := newGenericArchiveParserAdapter(Config{}) + if tt.assignParent { + assignParent(&tt.expectedPkgs[0], tt.expectedPkgs[1:]...) + } pkgtest.NewCatalogTester(). FromFile(t, generateJavaMetadataJarFixture(t, tt.fixtureName)). Expects(tt.expectedPkgs, tt.expectedRelationships). @@ -1159,6 +1224,18 @@ func Test_parseJavaArchive_regressions(t *testing.T) { } } +func assignParent(parent *pkg.Package, childPackages ...pkg.Package) { + for i, jp := range childPackages { + if v, ok := jp.Metadata.(pkg.JavaArchive); ok { + parent := *parent + // PURL are not calculated after the fact for parent + parent.PURL = "" + v.Parent = &parent + childPackages[i].Metadata = v + } + } +} + func generateJavaMetadataJarFixture(t *testing.T, fixtureName string) string { fixturePath := filepath.Join("test-fixtures/jar-metadata/cache/", fixtureName+".jar") if _, err := os.Stat(fixturePath); !os.IsNotExist(err) { diff --git a/syft/pkg/cataloger/java/test-fixtures/jar-metadata/Makefile b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/Makefile index 6f2398820..1fd78b446 100644 --- a/syft/pkg/cataloger/java/test-fixtures/jar-metadata/Makefile +++ b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/Makefile @@ -3,6 +3,7 @@ CACHE_PATH = $(shell pwd)/cache JACKSON_CORE = jackson-core-2.15.2 SBT_JACKSON_CORE = com.fasterxml.jackson.core.jackson-core-2.15.2 +API_ALL_SOURCES = api-all-2.0.0-sources $(CACHE_DIR): mkdir -p $(CACHE_DIR) @@ -12,3 +13,6 @@ $(CACHE_DIR)/$(JACKSON_CORE).jar: $(CACHE_DIR) $(CACHE_DIR)/$(SBT_JACKSON_CORE).jar: $(CACHE_DIR) cd $(SBT_JACKSON_CORE) && zip -r $(CACHE_PATH)/$(SBT_JACKSON_CORE).jar . + +$(CACHE_DIR)/$(API_ALL_SOURCES).jar: $(CACHE_DIR) + cd $(API_ALL_SOURCES) && zip -r $(CACHE_PATH)/$(API_ALL_SOURCES).jar . \ No newline at end of file diff --git a/syft/pkg/cataloger/java/test-fixtures/jar-metadata/README.md b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/README.md index dfb2a3489..c3e3fcc52 100644 --- a/syft/pkg/cataloger/java/test-fixtures/jar-metadata/README.md +++ b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/README.md @@ -3,3 +3,11 @@ Each directory is the name of a jar to be created (simply a zip) based on the contents of the directory. This prevents us from having to create real jars by hand or keep binaries in the repo. This also means we dont need the entire jar, only the necessary metadata for testing. + +### api-all-2.0.0-sources +This fixture is built to simulate the case where we have a jar with multiple pom files discovered when trying to determine the parent. +This is a valid case, but not one that we covered before [PR 2231](https://github.com/anchore/syft/pull/2231) + +### jackson-core-2.15.2 +These two fixtures are built to simulate the case where we would have a duplicate jar +regression as seen in [issue #2130](https://github.com/anchore/syft/issues/2130) \ No newline at end of file diff --git a/syft/pkg/cataloger/java/test-fixtures/jar-metadata/api-all-2.0.0-sources/META-INF/MANIFEST.MF b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/api-all-2.0.0-sources/META-INF/MANIFEST.MF new file mode 100644 index 000000000..c303943c4 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/api-all-2.0.0-sources/META-INF/MANIFEST.MF @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +Built-By: elecharny +Created-By: Apache Maven 3.6.0 +Build-Jdk: 1.8.0_191 \ No newline at end of file diff --git a/syft/pkg/cataloger/java/test-fixtures/jar-metadata/api-all-2.0.0-sources/META-INF/maven/org.apache.directory.api/api-all/pom.properties b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/api-all-2.0.0-sources/META-INF/maven/org.apache.directory.api/api-all/pom.properties new file mode 100644 index 000000000..a409e2fef --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/api-all-2.0.0-sources/META-INF/maven/org.apache.directory.api/api-all/pom.properties @@ -0,0 +1,4 @@ +#Created by Apache Maven 3.6.0 +version=2.0.0 +groupId=org.apache.directory.api +artifactId=api-all \ No newline at end of file diff --git a/syft/pkg/cataloger/java/test-fixtures/jar-metadata/api-all-2.0.0-sources/META-INF/maven/org.apache.directory.api/api-all/pom.xml b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/api-all-2.0.0-sources/META-INF/maven/org.apache.directory.api/api-all/pom.xml new file mode 100644 index 000000000..3726550be --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/api-all-2.0.0-sources/META-INF/maven/org.apache.directory.api/api-all/pom.xml @@ -0,0 +1,135 @@ + + + + 4.0.0 + + org.apache.directory.api + api-parent + 2.0.0 + + + api-all + Apache Directory API All + + + + ${project.groupId} + api-asn1-api + + + ${project.groupId} + api-asn1-ber + + + ${project.groupId} + api-dsml-engine + + + ${project.groupId} + api-dsml-parser + + + ${project.groupId} + api-i18n + + + ${project.groupId} + api-ldap-client-api + + + ${project.groupId} + api-ldap-codec-core + + + ${project.groupId} + api-ldap-codec-standalone + + + ${project.groupId} + api-ldap-extras-aci + + + ${project.groupId} + api-ldap-extras-codec + + + ${project.groupId} + api-ldap-extras-codec-api + + + ${project.groupId} + api-ldap-extras-sp + + + ${project.groupId} + api-ldap-extras-trigger + + + ${project.groupId} + api-ldap-extras-util + + + ${project.groupId} + api-ldap-model + + + ${project.groupId} + api-ldap-net-mina + + + ${project.groupId} + api-ldap-schema-converter + + + ${project.groupId} + api-ldap-schema-data + + + ${project.groupId} + api-util + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + + + ${project.groupId} + + + true + true + + + + + + + \ No newline at end of file diff --git a/syft/pkg/cataloger/java/test-fixtures/jar-metadata/api-all-2.0.0-sources/META-INF/maven/org.apache.directory.api/api-asn1-api/pom.properties b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/api-all-2.0.0-sources/META-INF/maven/org.apache.directory.api/api-asn1-api/pom.properties new file mode 100644 index 000000000..ec2a06fa1 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/api-all-2.0.0-sources/META-INF/maven/org.apache.directory.api/api-asn1-api/pom.properties @@ -0,0 +1,4 @@ +#Created by Apache Maven 3.6.0 +version=2.0.0 +groupId=org.apache.directory.api +artifactId=api-asn1-api diff --git a/syft/pkg/cataloger/java/test-fixtures/jar-metadata/api-all-2.0.0-sources/META-INF/maven/org.apache.directory.api/api-asn1-api/pom.xml b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/api-all-2.0.0-sources/META-INF/maven/org.apache.directory.api/api-asn1-api/pom.xml new file mode 100644 index 000000000..a57cda8e7 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/api-all-2.0.0-sources/META-INF/maven/org.apache.directory.api/api-asn1-api/pom.xml @@ -0,0 +1,99 @@ + + + + 4.0.0 + + org.apache.directory.api + api-asn1-parent + 2.0.0 + + + api-asn1-api + Apache Directory API ASN.1 API + bundle + + ASN.1 API + + + + org.junit.jupiter + junit-jupiter-engine + test + + + + log4j + log4j + test + + + + org.slf4j + slf4j-log4j12 + test + + + + org.slf4j + slf4j-api + test + + + + ${project.groupId} + api-i18n + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + META-INF/MANIFEST.MF + false + + + + + + org.apache.felix + maven-bundle-plugin + true + true + + META-INF + + ${project.groupId}.asn1.api + + org.apache.directory.api.asn1;version=${project.version};-noimport:=true, + org.apache.directory.api.asn1.util;version=${project.version};-noimport:=true + + + org.apache.directory.api.i18n;version=${project.version} + + + + + + +