From 8f02bd85f669c5d180e797ec382dac4669087490 Mon Sep 17 00:00:00 2001 From: Adam McClenaghan Date: Fri, 16 May 2025 14:29:53 +0100 Subject: [PATCH] fix: Distinguish openjdk vs jdk when using file source (#3895) * fix: Distinguish openjdk vs jdk when using file source Signed-off-by: adammcclenaghan * fix: Fix goimport order Signed-off-by: adammcclenaghan * add comment Signed-off-by: Alex Goodman --------- Signed-off-by: adammcclenaghan Signed-off-by: Alex Goodman Co-authored-by: Alex Goodman --- .../internal/pkgtest/test_generic_parser.go | 18 +++ syft/pkg/cataloger/java/cataloger_test.go | 108 ++++++++++++++++++ syft/pkg/cataloger/java/parse_jvm_release.go | 5 +- 3 files changed, 130 insertions(+), 1 deletion(-) diff --git a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go index eec0c6789..95f6e5d03 100644 --- a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go +++ b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go @@ -27,6 +27,7 @@ import ( "github.com/anchore/syft/syft/pkg/cataloger/generic" "github.com/anchore/syft/syft/source" "github.com/anchore/syft/syft/source/directorysource" + "github.com/anchore/syft/syft/source/filesource" "github.com/anchore/syft/syft/source/stereoscopesource" ) @@ -110,6 +111,18 @@ func (p *CatalogTester) FromDirectory(t *testing.T, path string) *CatalogTester return p } +func (p *CatalogTester) FromFileSource(t *testing.T, path string) *CatalogTester { + t.Helper() + + s, err := filesource.NewFromPath(path) + require.NoError(t, err) + resolver, err := s.FileResolver(source.AllLayersScope) + require.NoError(t, err) + + p.resolver = resolver + return p +} + func (p *CatalogTester) FromFile(t *testing.T, path string) *CatalogTester { t.Helper() @@ -340,6 +353,11 @@ func TestCataloger(t *testing.T, fixtureDir string, cataloger pkg.Cataloger, exp NewCatalogTester().FromDirectory(t, fixtureDir).Expects(expectedPkgs, expectedRelationships).TestCataloger(t, cataloger) } +func TestCatalogerFromFileSource(t *testing.T, fixturePath string, cataloger pkg.Cataloger, expectedPkgs []pkg.Package, expectedRelationships []artifact.Relationship) { + t.Helper() + NewCatalogTester().FromFileSource(t, fixturePath).Expects(expectedPkgs, expectedRelationships).TestCataloger(t, cataloger) +} + func TestFileParserWithEnv(t *testing.T, fixturePath string, parser generic.Parser, env *generic.Environment, expectedPkgs []pkg.Package, expectedRelationships []artifact.Relationship) { t.Helper() diff --git a/syft/pkg/cataloger/java/cataloger_test.go b/syft/pkg/cataloger/java/cataloger_test.go index f80b3963d..7bed8cfcb 100644 --- a/syft/pkg/cataloger/java/cataloger_test.go +++ b/syft/pkg/cataloger/java/cataloger_test.go @@ -215,3 +215,111 @@ func TestJvmDistributionCataloger(t *testing.T) { } } + +func TestJvmDistributionCatalogerFromFile(t *testing.T) { + + cases := []struct { + name string + fixture string + expected pkg.Package + }{ + { + name: "valid 1.8.0", + fixture: "test-fixtures/jvm-installs/oracle-jdk-se-8/usr/lib/jvm/jdk-1.8-oracle-x64/release", + expected: pkg.Package{ + Name: "jdk", + Version: "1.8.0_411-b25", + FoundBy: "java-jvm-cataloger", + Locations: file.NewLocationSet(file.NewLocation("/release")), + Licenses: pkg.NewLicenseSet(), + Type: pkg.BinaryPkg, + CPEs: []cpe.CPE{ + cpe.Must("cpe:2.3:a:oracle:java_se:1.8.0:update411:*:*:*:*:*:*", cpe.DeclaredSource), + cpe.Must("cpe:2.3:a:oracle:jre:1.8.0:update411:*:*:*:*:*:*", cpe.DeclaredSource), + cpe.Must("cpe:2.3:a:oracle:jdk:1.8.0:update411:*:*:*:*:*:*", cpe.DeclaredSource), + }, + PURL: "pkg:generic/oracle/jdk@1.8.0_411-b25", + Metadata: pkg.JavaVMInstallation{ + Release: pkg.JavaVMRelease{ + JavaRuntimeVersion: "1.8.0_411-b25", + JavaVersion: "1.8.0_411", + OsArch: "amd64", + OsName: "Linux", + OsVersion: "2.6", + Source: ".:git:71ec2089cf8c+", + BuildType: "commercial", + }, + Files: []string{ + "/release", + }, + }, + }, + }, + { + name: "valid post-jep223", + fixture: "test-fixtures/jvm-installs/valid-post-jep223/jvm/openjdk/release", + expected: pkg.Package{ + Name: "openjdk", + Version: "21.0.4+7-LTS", + FoundBy: "java-jvm-cataloger", + Locations: file.NewLocationSet(file.NewLocation("/release")), + Licenses: pkg.NewLicenseSet(), + Type: pkg.BinaryPkg, + CPEs: []cpe.CPE{cpe.Must("cpe:2.3:a:oracle:openjdk:21.0.4:*:*:*:*:*:*:*", cpe.DeclaredSource)}, + PURL: "pkg:generic/oracle/openjdk@21.0.4%2B7-LTS?repository_url=https%3A%2F%2Fgithub.com%2Fadoptium%2Fjdk21u.git", + Metadata: pkg.JavaVMInstallation{ + Release: pkg.JavaVMRelease{ + Implementor: "Eclipse Adoptium", + ImplementorVersion: "Temurin-21.0.4+7", + JavaRuntimeVersion: "21.0.4+7-LTS", + JavaVersion: "21.0.4", + JavaVersionDate: "2024-07-16", + Libc: "gnu", + Modules: []string{ + "java.base", "java.compiler", "java.datatransfer", "java.xml", "java.prefs", + "java.desktop", "java.instrument", "java.logging", "java.management", + "java.security.sasl", "java.naming", "java.rmi", "java.management.rmi", + "java.net.http", "java.scripting", "java.security.jgss", + "java.transaction.xa", "java.sql", "java.sql.rowset", "java.xml.crypto", "java.se", + "java.smartcardio", "jdk.accessibility", "jdk.internal.jvmstat", "jdk.attach", + "jdk.charsets", "jdk.internal.opt", "jdk.zipfs", "jdk.compiler", "jdk.crypto.ec", + "jdk.crypto.cryptoki", "jdk.dynalink", "jdk.internal.ed", "jdk.editpad", "jdk.hotspot.agent", + "jdk.httpserver", "jdk.incubator.vector", "jdk.internal.le", "jdk.internal.vm.ci", + "jdk.internal.vm.compiler", "jdk.internal.vm.compiler.management", "jdk.jartool", + "jdk.javadoc", "jdk.jcmd", "jdk.management", "jdk.management.agent", "jdk.jconsole", + "jdk.jdeps", "jdk.jdwp.agent", "jdk.jdi", "jdk.jfr", "jdk.jlink", "jdk.jpackage", "jdk.jshell", + "jdk.jsobject", "jdk.jstatd", "jdk.localedata", "jdk.management.jfr", "jdk.naming.dns", + "jdk.naming.rmi", "jdk.net", "jdk.nio.mapmode", "jdk.random", "jdk.sctp", "jdk.security.auth", + "jdk.security.jgss", "jdk.unsupported", "jdk.unsupported.desktop", "jdk.xml.dom", + }, + OsArch: "aarch64", + OsName: "Linux", + Source: ".:git:13710926b798", + BuildSource: "git:1271f10a26c47e1489a814dd2731f936a588d621", + BuildSourceRepo: "https://github.com/adoptium/temurin-build.git", + SourceRepo: "https://github.com/adoptium/jdk21u.git", + FullVersion: "21.0.4+7-LTS", + SemanticVersion: "21.0.4+7", + BuildInfo: "OS: Linux Version: 5.4.0-150-generic", + JvmVariant: "Hotspot", + JvmVersion: "21.0.4+7-LTS", + ImageType: "JDK", + }, + Files: []string{ + "/release", + }, + }, + }, + }, + } + + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + p := tt.expected + p.SetID() + + pkgtest.TestCatalogerFromFileSource(t, tt.fixture, NewJvmDistributionCataloger(), []pkg.Package{p}, nil) + }) + } + +} diff --git a/syft/pkg/cataloger/java/parse_jvm_release.go b/syft/pkg/cataloger/java/parse_jvm_release.go index 4bcd640fb..df1a5cf5e 100644 --- a/syft/pkg/cataloger/java/parse_jvm_release.go +++ b/syft/pkg/cataloger/java/parse_jvm_release.go @@ -88,7 +88,10 @@ func parseJVMRelease(_ context.Context, resolver file.Resolver, _ *generic.Envir installDir := path.Dir(reader.Path()) files, hasJdk := findJvmFiles(resolver, installDir) - vendor, product := jvmPrimaryVendorProduct(ri.Implementor, reader.Path(), ri.ImageType, hasJdk) + // the reason we use the reference to get the real path is in cases where the file cataloger is involved + // (thus the real path is not available except for in the original file reference). This is important + // since the path is critical for distinguishing between different JVM vendors. + vendor, product := jvmPrimaryVendorProduct(ri.Implementor, string(reader.Reference().RealPath), ri.ImageType, hasJdk) p := pkg.Package{ Name: product,