diff --git a/pkg/dependency/parser/java/jar/parse.go b/pkg/dependency/parser/java/jar/parse.go index 8cb81d1fc48e..be767e0cf248 100644 --- a/pkg/dependency/parser/java/jar/parse.go +++ b/pkg/dependency/parser/java/jar/parse.go @@ -6,15 +6,15 @@ import ( "crypto/sha1" // nolint:gosec "encoding/hex" "errors" - "fmt" "io" "os" "path" "path/filepath" "regexp" + "slices" "strings" - "github.com/samber/lo" + mavenversion "github.com/masahiro331/go-mvn-version" "golang.org/x/xerrors" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" @@ -439,7 +439,24 @@ func (m manifest) determineVersion() (string, error) { } func removePackageDuplicates(pkgs []ftypes.Package) []ftypes.Package { - return lo.UniqBy(pkgs, func(pkg ftypes.Package) string { - return fmt.Sprintf("%s::%s::%s", pkg.Name, pkg.Version, pkg.FilePath) - }) + // name::filePath => versions + var uniq = make(map[string][]mavenversion.Version) + var uniqPkgs []ftypes.Package + for _, pkg := range pkgs { + uniqID := pkg.Name + "::" + pkg.FilePath + // err is always nil + // cf. https://github.com/masahiro331/go-mvn-version/blob/d3157d602a08806ad94464c443e0cef1370694a1/version.go#L20-L25 + pkgVer, _ := mavenversion.NewVersion(pkg.Version) + savedVers, ok := uniq[uniqID] + if !ok || !slices.ContainsFunc(savedVers, func(v mavenversion.Version) bool { + // There are times when patch `0` is omitted. + // So we can't compare versions just as strings + // for example `2.17.0` and `2.17` must be equal + return v.Equal(pkgVer) + }) { + uniq[uniqID] = append(uniq[uniqID], pkgVer) + uniqPkgs = append(uniqPkgs, pkg) + } + } + return uniqPkgs } diff --git a/pkg/dependency/parser/java/jar/parse_test.go b/pkg/dependency/parser/java/jar/parse_test.go index 20f9c682be3b..958b2d0667a5 100644 --- a/pkg/dependency/parser/java/jar/parse_test.go +++ b/pkg/dependency/parser/java/jar/parse_test.go @@ -168,22 +168,54 @@ var ( }, } - // manually created + // Manually created. + // Files of `io.quarkus.gizmo.gizmo-1.1.jar` (gizmo:1.1.0 (from sha1)): + //├── bar + //│ ├── bar + //│ │ └── pom.properties (jackson-databind:2.13.4) + //│ └── foo + //│ └── pom.properties (jackson-databind:2.12.3) + //├── foo + //│ ├── bar + //│ │ └── pom.properties (jackson-databind:2.12.3) + //│ └── foo + //│ └── pom.properties (jackson-databind:2.13.4) + //├── jars + //│ ├── log4j-1.2.16.jar (log4j:1.2.16) + //│ └── log4j-1.2.17.jar (log4j:1.2.17) + //└── META-INF + // ├── INDEX.LIST + // ├── MANIFEST.MF + // └── maven + // └── io.quarkus.gizmo + // └── gizmo + // ├── pom.properties (gizmo:1.1) + // └── pom.xml wantDuplicatesJar = []ftypes.Package{ { Name: "io.quarkus.gizmo:gizmo", - Version: "1.1.1.Final", - FilePath: "testdata/io.quarkus.gizmo.gizmo-1.1.1.Final.jar", + Version: "1.1", + FilePath: "testdata/io.quarkus.gizmo.gizmo-1.1.jar", }, { Name: "log4j:log4j", Version: "1.2.16", - FilePath: "testdata/io.quarkus.gizmo.gizmo-1.1.1.Final.jar/jars/log4j-1.2.16.jar", + FilePath: "testdata/io.quarkus.gizmo.gizmo-1.1.jar/jars/log4j-1.2.16.jar", }, { Name: "log4j:log4j", Version: "1.2.17", - FilePath: "testdata/io.quarkus.gizmo.gizmo-1.1.1.Final.jar/jars/log4j-1.2.17.jar", + FilePath: "testdata/io.quarkus.gizmo.gizmo-1.1.jar/jars/log4j-1.2.17.jar", + }, + { + Name: "com.fasterxml.jackson.core:jackson-databind", + Version: "2.12.3", + FilePath: "testdata/io.quarkus.gizmo.gizmo-1.1.jar", + }, + { + Name: "com.fasterxml.jackson.core:jackson-databind", + Version: "2.13.4", + FilePath: "testdata/io.quarkus.gizmo.gizmo-1.1.jar", }, } ) @@ -251,7 +283,7 @@ func TestParse(t *testing.T) { }, { name: "duplicate libraries", - file: "testdata/io.quarkus.gizmo.gizmo-1.1.1.Final.jar", + file: "testdata/io.quarkus.gizmo.gizmo-1.1.jar", want: wantDuplicatesJar, }, } @@ -277,13 +309,13 @@ func TestParse(t *testing.T) { } case strings.Contains(r.URL.Query().Get("q"), "Gizmo"): res.Response.NumFound = 0 - case strings.Contains(r.URL.Query().Get("q"), "85d30c06026afd9f5be26da3194d4698c447a904"): + case strings.Contains(r.URL.Query().Get("q"), "1c78bbc4d8c58b9af8eee82b84f2c26ec48e9a2b"): res.Response.Docs = []doc{ { ID: "io.quarkus.gizmo.gizmo", GroupID: "io.quarkus.gizmo", ArtifactID: "gizmo", - Version: "1.1.1.Final", + Version: "1.1.0", }, } case strings.Contains(r.URL.Query().Get("q"), "heuristic"): diff --git a/pkg/dependency/parser/java/jar/testdata/io.quarkus.gizmo.gizmo-1.1.1.Final.jar b/pkg/dependency/parser/java/jar/testdata/io.quarkus.gizmo.gizmo-1.1.jar similarity index 99% rename from pkg/dependency/parser/java/jar/testdata/io.quarkus.gizmo.gizmo-1.1.1.Final.jar rename to pkg/dependency/parser/java/jar/testdata/io.quarkus.gizmo.gizmo-1.1.jar index 080b15ba5e40..3b82fae55ec9 100644 Binary files a/pkg/dependency/parser/java/jar/testdata/io.quarkus.gizmo.gizmo-1.1.1.Final.jar and b/pkg/dependency/parser/java/jar/testdata/io.quarkus.gizmo.gizmo-1.1.jar differ