From 1c4394fed014fcd5e9daf43552f369e8888c7d01 Mon Sep 17 00:00:00 2001 From: Doug Clarke Date: Thu, 21 May 2026 15:24:38 -0400 Subject: [PATCH] fix: enhancement to java cataloger to consider .zap files as jar files (#4932) * Enhancements to java cataloger to consider .zap files as jar files - Issue #4654 Signed-off-by: Doug Clarke --- syft/pkg/cataloger/java/archive_filename.go | 2 +- syft/pkg/cataloger/java/archive_parser.go | 1 + .../pkg/cataloger/java/archive_parser_test.go | 79 +++++++++++++++++++ syft/pkg/cataloger/java/cataloger_test.go | 1 + .../glob-paths/java-archives/example.zap | 1 + .../java/testdata/java-builds/Makefile | 5 +- 6 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 syft/pkg/cataloger/java/testdata/glob-paths/java-archives/example.zap diff --git a/syft/pkg/cataloger/java/archive_filename.go b/syft/pkg/cataloger/java/archive_filename.go index 1e3efb50d..2104a9c9d 100644 --- a/syft/pkg/cataloger/java/archive_filename.go +++ b/syft/pkg/cataloger/java/archive_filename.go @@ -108,7 +108,7 @@ func (a archiveFilename) extension() string { func (a archiveFilename) pkgType() pkg.Type { switch strings.ToLower(a.extension()) { - case "jar", "war", "ear", "lpkg", "par", "sar", "nar", "kar", "rar": + case "jar", "war", "ear", "lpkg", "par", "sar", "nar", "kar", "rar", "zap": return pkg.JavaPkg case "jpi", "hpi": return pkg.JenkinsPluginPkg diff --git a/syft/pkg/cataloger/java/archive_parser.go b/syft/pkg/cataloger/java/archive_parser.go index 0195247a9..e54b73696 100644 --- a/syft/pkg/cataloger/java/archive_parser.go +++ b/syft/pkg/cataloger/java/archive_parser.go @@ -48,6 +48,7 @@ var archiveFormatGlobs = []string{ // out of date, and they charge for their IDE. If you find an example // project that we can build in CI feel free to include it "**/*.rar", // Java Resource Adapter Archive + "**/*.zap", // ZAP add-ons https://github.com/zaproxy/zaproxy/wiki/ZapAddOns } // javaArchiveHashes are all the current hash algorithms used to calculate archive digests diff --git a/syft/pkg/cataloger/java/archive_parser_test.go b/syft/pkg/cataloger/java/archive_parser_test.go index 5ff522eaa..84167c5ef 100644 --- a/syft/pkg/cataloger/java/archive_parser_test.go +++ b/syft/pkg/cataloger/java/archive_parser_test.go @@ -351,6 +351,85 @@ func TestParseJar(t *testing.T) { }, }, }, + { + // Dupicate the example-java-app-gradle test and the Makefile is adjusted to copy its jar to example-zap-addon-0.1.0.zap + name: "example-zap-addon", + fixture: "testdata/java-builds/packages/example-zap-addon-0.1.0.zap", + wantErr: require.NoError, // no nested jars + expected: map[string]pkg.Package{ + "example-zap-addon": { + Name: "example-zap-addon", + Version: "0.1.0", + PURL: "pkg:maven/example-zap-addon/example-zap-addon@0.1.0", + Language: pkg.Java, + Type: pkg.JavaPkg, + Licenses: pkg.NewLicenseSet( + pkg.License{ + Value: "Apache-2.0", + SPDXExpression: "Apache-2.0", + Type: license.Concluded, + Locations: file.NewLocationSet(file.NewLocation("testdata/java-builds/packages/example-zap-addon-0.1.0.zap")), + }, + ), + Metadata: pkg.JavaArchive{ + VirtualPath: "testdata/java-builds/packages/example-zap-addon-0.1.0.zap", + Manifest: &pkg.JavaManifest{ + Main: []pkg.KeyValue{ + { + Key: "Manifest-Version", + Value: "1.0", + }, + { + Key: "Main-Class", + Value: "hello.HelloWorld", + }, + }, + }, + // PomProject: &pkg.JavaPomProject{ + // Path: "META-INF/maven/io.jenkins.plugins/example-jenkins-plugin/pom.xml", + // Parent: &pkg.JavaPomParent{GroupID: "org.jenkins-ci.plugins", ArtifactID: "plugin", Version: "4.46"}, + // GroupID: "io.jenkins.plugins", + // ArtifactID: "example-jenkins-plugin", + // Version: "1.0-SNAPSHOT", + // Name: "Example Jenkins Plugin", + // }, + }, + }, + "joda-time": { + Name: "joda-time", + Version: "2.2", + PURL: "pkg:maven/joda-time/joda-time@2.2", + Language: pkg.Java, + Type: pkg.JavaPkg, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromFieldsWithContext(ctx, "Apache 2", "http://www.apache.org/licenses/LICENSE-2.0.txt", func() *file.Location { + l := file.NewLocation("testdata/java-builds/packages/example-zap-addon-0.1.0.zap") + return &l + }()), + ), + Metadata: pkg.JavaArchive{ + // 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 + VirtualPath: "testdata/java-builds/packages/example-zap-addon-0.1.0.zap:joda-time:joda-time", + PomProperties: &pkg.JavaPomProperties{ + Path: "META-INF/maven/joda-time/joda-time/pom.properties", + GroupID: "joda-time", + ArtifactID: "joda-time", + Version: "2.2", + }, + PomProject: &pkg.JavaPomProject{ + Path: "META-INF/maven/joda-time/joda-time/pom.xml", + GroupID: "joda-time", + ArtifactID: "joda-time", + Version: "2.2", + Name: "Joda time", + Description: "Date and time library to replace JDK date handling", + URL: "http://joda-time.sourceforge.net", + }, + }, + }, + }, + }, } for _, test := range tests { diff --git a/syft/pkg/cataloger/java/cataloger_test.go b/syft/pkg/cataloger/java/cataloger_test.go index f0a91c118..f047adc76 100644 --- a/syft/pkg/cataloger/java/cataloger_test.go +++ b/syft/pkg/cataloger/java/cataloger_test.go @@ -32,6 +32,7 @@ func Test_ArchiveCataloger_Globs(t *testing.T) { "java-archives/example.far", "java-archives/example.lpkg", "java-archives/example.rar", + "java-archives/example.zap", "archives/example.zip", "archives/example.tar", "archives/example.tar.gz", diff --git a/syft/pkg/cataloger/java/testdata/glob-paths/java-archives/example.zap b/syft/pkg/cataloger/java/testdata/glob-paths/java-archives/example.zap new file mode 100644 index 000000000..8944cbcc0 --- /dev/null +++ b/syft/pkg/cataloger/java/testdata/glob-paths/java-archives/example.zap @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/testdata/java-builds/Makefile b/syft/pkg/cataloger/java/testdata/java-builds/Makefile index 1ef3fab78..5ed42951e 100644 --- a/syft/pkg/cataloger/java/testdata/java-builds/Makefile +++ b/syft/pkg/cataloger/java/testdata/java-builds/Makefile @@ -14,7 +14,7 @@ fixtures: jars archives native-image # requirement 2: 'fingerprint' goal to determine if the fixture input that indicates any existing cache should be busted fingerprint: $(FINGERPRINT_FILE) -jars: $(PKGSDIR)/example-java-app-maven-0.1.0.jar $(PKGSDIR)/example-java-app-gradle-0.1.0.jar $(PKGSDIR)/example-jenkins-plugin.hpi $(PKGSDIR)/spring-boot-0.0.1-SNAPSHOT.jar +jars: $(PKGSDIR)/example-java-app-maven-0.1.0.jar $(PKGSDIR)/example-java-app-gradle-0.1.0.jar $(PKGSDIR)/example-jenkins-plugin.hpi $(PKGSDIR)/spring-boot-0.0.1-SNAPSHOT.jar $(PKGSDIR)/example-zap-addon-0.1.0.zap archives: $(PKGSDIR)/example-java-app-maven-0.1.0.zip $(PKGSDIR)/example-java-app-maven-0.1.0.tar $(PKGSDIR)/example-java-app-maven-0.1.0.tar.gz $(PKGSDIR)/example-java-app-maven-0.1.0.tgz @@ -55,6 +55,9 @@ clean-maven: $(PKGSDIR)/example-java-app-gradle-0.1.0.jar: ./build-example-java-app-gradle.sh $(PKGSDIR) +$(PKGSDIR)/example-zap-addon-0.1.0.zap: $(PKGSDIR)/example-java-app-gradle-0.1.0.jar + cp $(PKGSDIR)/example-java-app-gradle-0.1.0.jar $(PKGSDIR)/example-zap-addon-0.1.0.zap + clean-gradle: rm -rf example-java-app/.gradle \ example-java-app/build