diff --git a/go.mod b/go.mod
index 82cea45b0ea..87692a0f38c 100644
--- a/go.mod
+++ b/go.mod
@@ -90,6 +90,7 @@ require (
github.com/BurntSushi/toml v1.4.0
github.com/adrg/xdg v0.5.0
github.com/magiconair/properties v1.8.7
+ golang.org/x/exp v0.0.0-20231108232855-2478ac86f678
)
require (
@@ -230,7 +231,6 @@ require (
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
- golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/term v0.22.0 // indirect
diff --git a/syft/pkg/cataloger/java/archive_parser.go b/syft/pkg/cataloger/java/archive_parser.go
index 134e8f18af3..2262d79fb7c 100644
--- a/syft/pkg/cataloger/java/archive_parser.go
+++ b/syft/pkg/cataloger/java/archive_parser.go
@@ -6,8 +6,11 @@ import (
"fmt"
"os"
"path"
+ "slices"
"strings"
+ "golang.org/x/exp/maps"
+
intFile "github.com/anchore/syft/internal/file"
"github.com/anchore/syft/internal/licenses"
"github.com/anchore/syft/internal/log"
@@ -298,7 +301,10 @@ func (j *archiveParser) guessMainPackageNameAndVersionFromPomInfo(ctx context.Co
properties, _ := pomPropertiesByParentPath(j.archivePath, j.location, pomPropertyMatches)
projects, _ := pomProjectByParentPath(j.archivePath, j.location, pomMatches)
- for parentPath, propertiesObj := range properties {
+ parentPaths := maps.Keys(properties)
+ slices.Sort(parentPaths)
+ for _, parentPath := range parentPaths {
+ propertiesObj := properties[parentPath]
if artifactIDMatchesFilename(propertiesObj.ArtifactID, j.fileInfo.name) {
pomPropertiesObject = propertiesObj
if proj, exists := projects[parentPath]; exists {
diff --git a/syft/pkg/cataloger/java/archive_parser_test.go b/syft/pkg/cataloger/java/archive_parser_test.go
index 7d7164196c3..968be857862 100644
--- a/syft/pkg/cataloger/java/archive_parser_test.go
+++ b/syft/pkg/cataloger/java/archive_parser_test.go
@@ -1386,6 +1386,44 @@ func Test_parseJavaArchive_regressions(t *testing.T) {
}
}
+func Test_deterministicMatchingPomProperties(t *testing.T) {
+ tests := []struct {
+ fixture string
+ expectedName string
+ expectedVersion string
+ }{
+ {
+ fixture: "multiple-matching-2.11.5",
+ expectedName: "multiple-matching-1",
+ expectedVersion: "2.11.5",
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.fixture, func(t *testing.T) {
+ fixturePath := generateJavaMetadataJarFixture(t, test.fixture)
+
+ for i := 0; i < 5; i++ {
+ func() {
+ fixture, err := os.Open(fixturePath)
+ require.NoError(t, err)
+
+ parser, cleanupFn, err := newJavaArchiveParser(file.LocationReadCloser{
+ Location: file.NewLocation(fixture.Name()),
+ ReadCloser: fixture,
+ }, false, ArchiveCatalogerConfig{UseNetwork: false})
+ defer cleanupFn()
+ require.NoError(t, err)
+
+ name, version, _ := parser.guessMainPackageNameAndVersionFromPomInfo(context.TODO())
+ require.Equal(t, test.expectedName, name)
+ require.Equal(t, test.expectedVersion, version)
+ }()
+ }
+ })
+ }
+}
+
func assignParent(parent *pkg.Package, childPackages ...pkg.Package) {
for i, jp := range childPackages {
if v, ok := jp.Metadata.(pkg.JavaArchive); ok {
diff --git a/syft/pkg/cataloger/java/test-fixtures/jar-metadata/Makefile b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/Makefile
index b6533f1471c..c5d3c52a3af 100644
--- a/syft/pkg/cataloger/java/test-fixtures/jar-metadata/Makefile
+++ b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/Makefile
@@ -6,6 +6,7 @@ SBT_JACKSON_CORE = com.fasterxml.jackson.core.jackson-core-2.15.2
OPENSAML_CORE = opensaml-core-3.4.6
API_ALL_SOURCES = api-all-2.0.0-sources
SPRING_INSTRUMENTATION = spring-instrumentation-4.3.0-1.0
+MULTIPLE_MATCHING = multiple-matching-2.11.5
$(CACHE_DIR):
mkdir -p $(CACHE_DIR)
@@ -24,3 +25,6 @@ $(CACHE_DIR)/$(API_ALL_SOURCES).jar: $(CACHE_DIR)
$(CACHE_DIR)/$(SPRING_INSTRUMENTATION).jar: $(CACHE_DIR)
cd $(SPRING_INSTRUMENTATION) && zip -r $(CACHE_PATH)/$(SPRING_INSTRUMENTATION).jar .
+
+$(CACHE_DIR)/$(MULTIPLE_MATCHING).jar: $(CACHE_DIR)
+ cd $(MULTIPLE_MATCHING) && zip -r $(CACHE_PATH)/$(MULTIPLE_MATCHING).jar .
diff --git a/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/MANIFEST.MF b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/MANIFEST.MF
new file mode 100644
index 00000000000..525109c7337
--- /dev/null
+++ b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/MANIFEST.MF
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+Created-By: Multi
diff --git a/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/maven/org.multiple/multiple-matching-1/pom.properties b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/maven/org.multiple/multiple-matching-1/pom.properties
new file mode 100644
index 00000000000..43e0d811b73
--- /dev/null
+++ b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/maven/org.multiple/multiple-matching-1/pom.properties
@@ -0,0 +1,3 @@
+version=2.11.5
+groupId=org.multiple
+artifactId=multiple-matching-1
diff --git a/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/maven/org.multiple/multiple-matching-1/pom.xml b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/maven/org.multiple/multiple-matching-1/pom.xml
new file mode 100644
index 00000000000..1c359dd9aa2
--- /dev/null
+++ b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/maven/org.multiple/multiple-matching-1/pom.xml
@@ -0,0 +1,8 @@
+
+
+ 4.0.0
+
+ org.multiple
+ multiple-matching-1
+ 2.11.5
+
diff --git a/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/maven/org.multiple/multiple-matching-2/pom.properties b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/maven/org.multiple/multiple-matching-2/pom.properties
new file mode 100644
index 00000000000..6be2acfbd13
--- /dev/null
+++ b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/maven/org.multiple/multiple-matching-2/pom.properties
@@ -0,0 +1,3 @@
+version=2.11.5
+groupId=org.multiple
+artifactId=multiple-matching-2
\ No newline at end of file
diff --git a/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/maven/org.multiple/multiple-matching-2/pom.xml b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/maven/org.multiple/multiple-matching-2/pom.xml
new file mode 100644
index 00000000000..343c49d303d
--- /dev/null
+++ b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/maven/org.multiple/multiple-matching-2/pom.xml
@@ -0,0 +1,8 @@
+
+
+ 4.0.0
+
+ org.multiple
+ multiple-matching-2
+ 2.11.5
+
diff --git a/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/maven/org.multiple/multiple-matching-3/pom.properties b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/maven/org.multiple/multiple-matching-3/pom.properties
new file mode 100644
index 00000000000..187215b293d
--- /dev/null
+++ b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/maven/org.multiple/multiple-matching-3/pom.properties
@@ -0,0 +1,3 @@
+version=2.11.5
+groupId=org.multiple
+artifactId=multiple-matching-3
diff --git a/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/maven/org.multiple/multiple-matching-3/pom.xml b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/maven/org.multiple/multiple-matching-3/pom.xml
new file mode 100644
index 00000000000..b428bd69bbd
--- /dev/null
+++ b/syft/pkg/cataloger/java/test-fixtures/jar-metadata/multiple-matching-2.11.5/META-INF/maven/org.multiple/multiple-matching-3/pom.xml
@@ -0,0 +1,8 @@
+
+
+ 4.0.0
+
+ org.multiple
+ multiple-matching-3
+ 2.11.5
+