diff --git a/syft/pkg/cataloger/dotnet/parse_dotnet_portable_executable.go b/syft/pkg/cataloger/dotnet/parse_dotnet_portable_executable.go index 2228d11d1..3ec83f229 100644 --- a/syft/pkg/cataloger/dotnet/parse_dotnet_portable_executable.go +++ b/syft/pkg/cataloger/dotnet/parse_dotnet_portable_executable.go @@ -9,6 +9,7 @@ import ( "github.com/saferwall/pe" + version "github.com/anchore/go-version" "github.com/anchore/packageurl-go" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" @@ -127,12 +128,42 @@ func extractVersion(version string) string { return out } +func keepGreaterSemanticVersion(productVersion string, fileVersion string) string { + semanticProductVersion, err := version.NewVersion(productVersion) + + if err != nil || semanticProductVersion == nil { + log.Tracef("Unable to create semantic version from portable executable product version %s", productVersion) + return "" + } + + semanticFileVersion, err := version.NewVersion(fileVersion) + + if err != nil || semanticFileVersion == nil { + log.Tracef("Unable to create semantic version from portable executable file version %s", fileVersion) + return productVersion + } + + // Make no choice when they are semantically equal so that it falls + // through to the other comparison cases + if semanticProductVersion.Equal(semanticFileVersion) { + return "" + } + + if semanticFileVersion.GreaterThan(semanticProductVersion) { + return fileVersion + } + + return productVersion +} + func findVersion(versionResources map[string]string) string { productVersion := extractVersion(versionResources["ProductVersion"]) fileVersion := extractVersion(versionResources["FileVersion"]) - if productVersion == "" { - return fileVersion + semanticVersionCompareResult := keepGreaterSemanticVersion(productVersion, fileVersion) + + if semanticVersionCompareResult != "" { + return semanticVersionCompareResult } productVersionDetail := punctuationCount(productVersion) diff --git a/syft/pkg/cataloger/dotnet/parse_dotnet_portable_executable_test.go b/syft/pkg/cataloger/dotnet/parse_dotnet_portable_executable_test.go index d03470529..4d915a50d 100644 --- a/syft/pkg/cataloger/dotnet/parse_dotnet_portable_executable_test.go +++ b/syft/pkg/cataloger/dotnet/parse_dotnet_portable_executable_test.go @@ -193,6 +193,78 @@ func TestParseDotnetPortableExecutable(t *testing.T) { Version: "80.1.7.92", }, }, + { + name: "Higher semantic version Product Version", + versionResources: map[string]string{ + "FileDescription": "Higher semantic version Product Version", + "FileVersion": "3.0.0.0", + "ProductVersion": "3.0.1+b86b61bf676163639795b163d8d753b20aad6207", + }, + expectedPackage: pkg.Package{ + Name: "Higher semantic version Product Version", + Version: "3.0.1+b86b61bf676163639795b163d8d753b20aad6207", + }, + }, + { + name: "Higher semantic version File Version", + versionResources: map[string]string{ + "FileDescription": "Higher semantic version File Version", + "FileVersion": "3.0.1+b86b61bf676163639795b163d8d753b20aad6207", + "ProductVersion": "3.0.0", + }, + expectedPackage: pkg.Package{ + Name: "Higher semantic version File Version", + Version: "3.0.1+b86b61bf676163639795b163d8d753b20aad6207", + }, + }, + { + name: "Invalid semantic version File Version", + versionResources: map[string]string{ + "FileDescription": "Invalid semantic version File Version", + "FileVersion": "A", + "ProductVersion": "3.0.1+b86b61bf676163639795b163d8d753b20aad6207", + }, + expectedPackage: pkg.Package{ + Name: "Invalid semantic version File Version", + Version: "3.0.1+b86b61bf676163639795b163d8d753b20aad6207", + }, + }, + { + name: "Invalid semantic version File Version", + versionResources: map[string]string{ + "FileDescription": "Invalid semantic version File Version", + "FileVersion": "A", + "ProductVersion": "3.0.1+b86b61bf676163639795b163d8d753b20aad6207", + }, + expectedPackage: pkg.Package{ + Name: "Invalid semantic version File Version", + Version: "3.0.1+b86b61bf676163639795b163d8d753b20aad6207", + }, + }, + { + name: "Invalid semantic version Product Version", + versionResources: map[string]string{ + "FileDescription": "Invalid semantic version Product Version", + "FileVersion": "3.0.1+b86b61bf676163639795b163d8d753b20aad6207", + "ProductVersion": "A", + }, + expectedPackage: pkg.Package{ + Name: "Invalid semantic version Product Version", + Version: "3.0.1+b86b61bf676163639795b163d8d753b20aad6207", + }, + }, + { + name: "Semantically equal falls through, chooses File Version with more components", + versionResources: map[string]string{ + "FileDescription": "Semantically equal falls through, chooses File Version with more components", + "FileVersion": "3.0.0.0", + "ProductVersion": "3.0.0", + }, + expectedPackage: pkg.Package{ + Name: "Semantically equal falls through, chooses File Version with more components", + Version: "3.0.0.0", + }, + }, } for _, tc := range tests {