From 40dd5d0bbd60b7333446675b590402fb251af300 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Fri, 28 Mar 2025 10:58:59 -0400 Subject: [PATCH] better .NET cpe generation (#3764) Signed-off-by: Alex Goodman --- syft/pkg/cataloger/dotnet/cataloger_test.go | 226 +----------------- syft/pkg/cataloger/dotnet/package_test.go | 50 ++++ .../cataloger/internal/cpegenerate/dotnet.go | 98 ++++++++ .../internal/cpegenerate/generate.go | 30 ++- .../internal/cpegenerate/generate_test.go | 90 ++++++- .../pkg/cataloger/internal/cpegenerate/rpm.go | 25 +- 6 files changed, 272 insertions(+), 247 deletions(-) create mode 100644 syft/pkg/cataloger/internal/cpegenerate/dotnet.go diff --git a/syft/pkg/cataloger/dotnet/cataloger_test.go b/syft/pkg/cataloger/dotnet/cataloger_test.go index c1e8fdf89..bda2519f3 100644 --- a/syft/pkg/cataloger/dotnet/cataloger_test.go +++ b/syft/pkg/cataloger/dotnet/cataloger_test.go @@ -305,181 +305,6 @@ func TestCataloger(t *testing.T) { "runtimepack.Microsoft.NETCore.App.Runtime.win-x64 @ 8.0.14 (/app/dotnetapp.deps.json) [dependency-of] dotnetapp @ 1.0.0 (/app/dotnetapp.deps.json)", ) - var net8AppExpectedBinarySelfContainedRelationships []string - net8AppExpectedBinarySelfContainedRelationships = append(net8AppExpectedBinarySelfContainedRelationships, net8AppBinaryOnlyPkgs...) - net8AppExpectedBinarySelfContainedRelationships = append(net8AppExpectedBinarySelfContainedRelationships, - // include the runtime libs... - "Microsoft.CSharp @ 8.0.1425.11118 (/app/Microsoft.CSharp.dll)", - "Microsoft.VisualBasic @ 8.0.1425.11118 (/app/Microsoft.VisualBasic.dll)", - "Microsoft.VisualBasic.Core @ 13.0.1425.11118 (/app/Microsoft.VisualBasic.Core.dll)", - "Microsoft.Win32.Primitives @ 8.0.1425.11118 (/app/Microsoft.Win32.Primitives.dll)", - "Microsoft.Win32.Registry @ 8.0.1425.11118 (/app/Microsoft.Win32.Registry.dll)", - "System @ 8.0.1425.11118 (/app/System.dll)", - "System.AppContext @ 8.0.1425.11118 (/app/System.AppContext.dll)", - "System.Buffers @ 8.0.1425.11118 (/app/System.Buffers.dll)", - "System.Collections @ 8.0.1425.11118 (/app/System.Collections.dll)", - "System.Collections.Concurrent @ 8.0.1425.11118 (/app/System.Collections.Concurrent.dll)", - "System.Collections.Immutable @ 8.0.1425.11118 (/app/System.Collections.Immutable.dll)", - "System.Collections.NonGeneric @ 8.0.1425.11118 (/app/System.Collections.NonGeneric.dll)", - "System.Collections.Specialized @ 8.0.1425.11118 (/app/System.Collections.Specialized.dll)", - "System.ComponentModel @ 8.0.1425.11118 (/app/System.ComponentModel.dll)", - "System.ComponentModel.Annotations @ 8.0.1425.11118 (/app/System.ComponentModel.Annotations.dll)", - "System.ComponentModel.DataAnnotations @ 8.0.1425.11118 (/app/System.ComponentModel.DataAnnotations.dll)", - "System.ComponentModel.EventBasedAsync @ 8.0.1425.11118 (/app/System.ComponentModel.EventBasedAsync.dll)", - "System.ComponentModel.Primitives @ 8.0.1425.11118 (/app/System.ComponentModel.Primitives.dll)", - "System.ComponentModel.TypeConverter @ 8.0.1425.11118 (/app/System.ComponentModel.TypeConverter.dll)", - "System.Configuration @ 8.0.1425.11118 (/app/System.Configuration.dll)", - "System.Console @ 8.0.1425.11118 (/app/System.Console.dll)", - "System.Core @ 8.0.1425.11118 (/app/System.Core.dll)", - "System.Data @ 8.0.1425.11118 (/app/System.Data.dll)", - "System.Data.Common @ 8.0.1425.11118 (/app/System.Data.Common.dll)", - "System.Data.DataSetExtensions @ 8.0.1425.11118 (/app/System.Data.DataSetExtensions.dll)", - "System.Diagnostics.Contracts @ 8.0.1425.11118 (/app/System.Diagnostics.Contracts.dll)", - "System.Diagnostics.Debug @ 8.0.1425.11118 (/app/System.Diagnostics.Debug.dll)", - "System.Diagnostics.DiagnosticSource @ 8.0.1425.11118 (/app/System.Diagnostics.DiagnosticSource.dll)", - "System.Diagnostics.FileVersionInfo @ 8.0.1425.11118 (/app/System.Diagnostics.FileVersionInfo.dll)", - "System.Diagnostics.Process @ 8.0.1425.11118 (/app/System.Diagnostics.Process.dll)", - "System.Diagnostics.StackTrace @ 8.0.1425.11118 (/app/System.Diagnostics.StackTrace.dll)", - "System.Diagnostics.TextWriterTraceListener @ 8.0.1425.11118 (/app/System.Diagnostics.TextWriterTraceListener.dll)", - "System.Diagnostics.Tools @ 8.0.1425.11118 (/app/System.Diagnostics.Tools.dll)", - "System.Diagnostics.TraceSource @ 8.0.1425.11118 (/app/System.Diagnostics.TraceSource.dll)", - "System.Diagnostics.Tracing @ 8.0.1425.11118 (/app/System.Diagnostics.Tracing.dll)", - "System.Drawing @ 8.0.1425.11118 (/app/System.Drawing.dll)", - "System.Drawing.Primitives @ 8.0.1425.11118 (/app/System.Drawing.Primitives.dll)", - "System.Dynamic.Runtime @ 8.0.1425.11118 (/app/System.Dynamic.Runtime.dll)", - "System.Formats.Asn1 @ 8.0.1425.11118 (/app/System.Formats.Asn1.dll)", - "System.Formats.Tar @ 8.0.1425.11118 (/app/System.Formats.Tar.dll)", - "System.Globalization @ 8.0.1425.11118 (/app/System.Globalization.dll)", - "System.Globalization.Calendars @ 8.0.1425.11118 (/app/System.Globalization.Calendars.dll)", - "System.Globalization.Extensions @ 8.0.1425.11118 (/app/System.Globalization.Extensions.dll)", - "System.IO @ 8.0.1425.11118 (/app/System.IO.dll)", - "System.IO.Compression @ 8.0.1425.11118 (/app/System.IO.Compression.dll)", - "System.IO.Compression.Brotli @ 8.0.1425.11118 (/app/System.IO.Compression.Brotli.dll)", - "System.IO.Compression.FileSystem @ 8.0.1425.11118 (/app/System.IO.Compression.FileSystem.dll)", - "System.IO.Compression.ZipFile @ 8.0.1425.11118 (/app/System.IO.Compression.ZipFile.dll)", - "System.IO.FileSystem @ 8.0.1425.11118 (/app/System.IO.FileSystem.dll)", - "System.IO.FileSystem.AccessControl @ 8.0.1425.11118 (/app/System.IO.FileSystem.AccessControl.dll)", - "System.IO.FileSystem.DriveInfo @ 8.0.1425.11118 (/app/System.IO.FileSystem.DriveInfo.dll)", - "System.IO.FileSystem.Primitives @ 8.0.1425.11118 (/app/System.IO.FileSystem.Primitives.dll)", - "System.IO.FileSystem.Watcher @ 8.0.1425.11118 (/app/System.IO.FileSystem.Watcher.dll)", - "System.IO.IsolatedStorage @ 8.0.1425.11118 (/app/System.IO.IsolatedStorage.dll)", - "System.IO.MemoryMappedFiles @ 8.0.1425.11118 (/app/System.IO.MemoryMappedFiles.dll)", - "System.IO.Pipes @ 8.0.1425.11118 (/app/System.IO.Pipes.dll)", - "System.IO.Pipes.AccessControl @ 8.0.1425.11118 (/app/System.IO.Pipes.AccessControl.dll)", - "System.IO.UnmanagedMemoryStream @ 8.0.1425.11118 (/app/System.IO.UnmanagedMemoryStream.dll)", - "System.Linq @ 8.0.1425.11118 (/app/System.Linq.dll)", - "System.Linq.Expressions @ 8.0.1425.11118 (/app/System.Linq.Expressions.dll)", - "System.Linq.Parallel @ 8.0.1425.11118 (/app/System.Linq.Parallel.dll)", - "System.Linq.Queryable @ 8.0.1425.11118 (/app/System.Linq.Queryable.dll)", - "System.Memory @ 8.0.1425.11118 (/app/System.Memory.dll)", - "System.Net @ 8.0.1425.11118 (/app/System.Net.dll)", - "System.Net.Http @ 8.0.1425.11118 (/app/System.Net.Http.dll)", - "System.Net.Http.Json @ 8.0.1425.11118 (/app/System.Net.Http.Json.dll)", - "System.Net.HttpListener @ 8.0.1425.11118 (/app/System.Net.HttpListener.dll)", - "System.Net.Mail @ 8.0.1425.11118 (/app/System.Net.Mail.dll)", - "System.Net.NameResolution @ 8.0.1425.11118 (/app/System.Net.NameResolution.dll)", - "System.Net.NetworkInformation @ 8.0.1425.11118 (/app/System.Net.NetworkInformation.dll)", - "System.Net.Ping @ 8.0.1425.11118 (/app/System.Net.Ping.dll)", - "System.Net.Primitives @ 8.0.1425.11118 (/app/System.Net.Primitives.dll)", - "System.Net.Quic @ 8.0.1425.11118 (/app/System.Net.Quic.dll)", - "System.Net.Requests @ 8.0.1425.11118 (/app/System.Net.Requests.dll)", - "System.Net.Security @ 8.0.1425.11118 (/app/System.Net.Security.dll)", - "System.Net.ServicePoint @ 8.0.1425.11118 (/app/System.Net.ServicePoint.dll)", - "System.Net.Sockets @ 8.0.1425.11118 (/app/System.Net.Sockets.dll)", - "System.Net.WebClient @ 8.0.1425.11118 (/app/System.Net.WebClient.dll)", - "System.Net.WebHeaderCollection @ 8.0.1425.11118 (/app/System.Net.WebHeaderCollection.dll)", - "System.Net.WebProxy @ 8.0.1425.11118 (/app/System.Net.WebProxy.dll)", - "System.Net.WebSockets @ 8.0.1425.11118 (/app/System.Net.WebSockets.dll)", - "System.Net.WebSockets.Client @ 8.0.1425.11118 (/app/System.Net.WebSockets.Client.dll)", - "System.Numerics @ 8.0.1425.11118 (/app/System.Numerics.dll)", - "System.Numerics.Vectors @ 8.0.1425.11118 (/app/System.Numerics.Vectors.dll)", - "System.ObjectModel @ 8.0.1425.11118 (/app/System.ObjectModel.dll)", - "System.Private.CoreLib @ 8.0.1425.11118 (/app/System.Private.CoreLib.dll)", - "System.Private.DataContractSerialization @ 8.0.1425.11118 (/app/System.Private.DataContractSerialization.dll)", - "System.Private.Uri @ 8.0.1425.11118 (/app/System.Private.Uri.dll)", - "System.Private.Xml @ 8.0.1425.11118 (/app/System.Private.Xml.dll)", - "System.Private.Xml.Linq @ 8.0.1425.11118 (/app/System.Private.Xml.Linq.dll)", - "System.Reflection @ 8.0.1425.11118 (/app/System.Reflection.dll)", - "System.Reflection.DispatchProxy @ 8.0.1425.11118 (/app/System.Reflection.DispatchProxy.dll)", - "System.Reflection.Emit @ 8.0.1425.11118 (/app/System.Reflection.Emit.dll)", - "System.Reflection.Emit.ILGeneration @ 8.0.1425.11118 (/app/System.Reflection.Emit.ILGeneration.dll)", - "System.Reflection.Emit.Lightweight @ 8.0.1425.11118 (/app/System.Reflection.Emit.Lightweight.dll)", - "System.Reflection.Extensions @ 8.0.1425.11118 (/app/System.Reflection.Extensions.dll)", - "System.Reflection.Metadata @ 8.0.1425.11118 (/app/System.Reflection.Metadata.dll)", - "System.Reflection.Primitives @ 8.0.1425.11118 (/app/System.Reflection.Primitives.dll)", - "System.Reflection.TypeExtensions @ 8.0.1425.11118 (/app/System.Reflection.TypeExtensions.dll)", - "System.Resources.Reader @ 8.0.1425.11118 (/app/System.Resources.Reader.dll)", - "System.Resources.ResourceManager @ 8.0.1425.11118 (/app/System.Resources.ResourceManager.dll)", - "System.Resources.Writer @ 8.0.1425.11118 (/app/System.Resources.Writer.dll)", - "System.Runtime @ 8.0.1425.11118 (/app/System.Runtime.dll)", - "System.Runtime.CompilerServices.Unsafe @ 8.0.1425.11118 (/app/System.Runtime.CompilerServices.Unsafe.dll)", - "System.Runtime.CompilerServices.VisualC @ 8.0.1425.11118 (/app/System.Runtime.CompilerServices.VisualC.dll)", - "System.Runtime.Extensions @ 8.0.1425.11118 (/app/System.Runtime.Extensions.dll)", - "System.Runtime.Handles @ 8.0.1425.11118 (/app/System.Runtime.Handles.dll)", - "System.Runtime.InteropServices @ 8.0.1425.11118 (/app/System.Runtime.InteropServices.dll)", - "System.Runtime.InteropServices.JavaScript @ 8.0.1425.11118 (/app/System.Runtime.InteropServices.JavaScript.dll)", - "System.Runtime.InteropServices.RuntimeInformation @ 8.0.1425.11118 (/app/System.Runtime.InteropServices.RuntimeInformation.dll)", - "System.Runtime.Intrinsics @ 8.0.1425.11118 (/app/System.Runtime.Intrinsics.dll)", - "System.Runtime.Loader @ 8.0.1425.11118 (/app/System.Runtime.Loader.dll)", - "System.Runtime.Numerics @ 8.0.1425.11118 (/app/System.Runtime.Numerics.dll)", - "System.Runtime.Serialization @ 8.0.1425.11118 (/app/System.Runtime.Serialization.dll)", - "System.Runtime.Serialization.Formatters @ 8.0.1425.11118 (/app/System.Runtime.Serialization.Formatters.dll)", - "System.Runtime.Serialization.Json @ 8.0.1425.11118 (/app/System.Runtime.Serialization.Json.dll)", - "System.Runtime.Serialization.Primitives @ 8.0.1425.11118 (/app/System.Runtime.Serialization.Primitives.dll)", - "System.Runtime.Serialization.Xml @ 8.0.1425.11118 (/app/System.Runtime.Serialization.Xml.dll)", - "System.Security @ 8.0.1425.11118 (/app/System.Security.dll)", - "System.Security.AccessControl @ 8.0.1425.11118 (/app/System.Security.AccessControl.dll)", - "System.Security.Claims @ 8.0.1425.11118 (/app/System.Security.Claims.dll)", - "System.Security.Cryptography @ 8.0.1425.11118 (/app/System.Security.Cryptography.dll)", - "System.Security.Cryptography.Algorithms @ 8.0.1425.11118 (/app/System.Security.Cryptography.Algorithms.dll)", - "System.Security.Cryptography.Cng @ 8.0.1425.11118 (/app/System.Security.Cryptography.Cng.dll)", - "System.Security.Cryptography.Csp @ 8.0.1425.11118 (/app/System.Security.Cryptography.Csp.dll)", - "System.Security.Cryptography.Encoding @ 8.0.1425.11118 (/app/System.Security.Cryptography.Encoding.dll)", - "System.Security.Cryptography.OpenSsl @ 8.0.1425.11118 (/app/System.Security.Cryptography.OpenSsl.dll)", - "System.Security.Cryptography.Primitives @ 8.0.1425.11118 (/app/System.Security.Cryptography.Primitives.dll)", - "System.Security.Cryptography.X509Certificates @ 8.0.1425.11118 (/app/System.Security.Cryptography.X509Certificates.dll)", - "System.Security.Principal @ 8.0.1425.11118 (/app/System.Security.Principal.dll)", - "System.Security.Principal.Windows @ 8.0.1425.11118 (/app/System.Security.Principal.Windows.dll)", - "System.Security.SecureString @ 8.0.1425.11118 (/app/System.Security.SecureString.dll)", - "System.ServiceModel.Web @ 8.0.1425.11118 (/app/System.ServiceModel.Web.dll)", - "System.ServiceProcess @ 8.0.1425.11118 (/app/System.ServiceProcess.dll)", - "System.Text.Encoding @ 8.0.1425.11118 (/app/System.Text.Encoding.dll)", - "System.Text.Encoding.CodePages @ 8.0.1425.11118 (/app/System.Text.Encoding.CodePages.dll)", - "System.Text.Encoding.Extensions @ 8.0.1425.11118 (/app/System.Text.Encoding.Extensions.dll)", - "System.Text.Encodings.Web @ 8.0.1425.11118 (/app/System.Text.Encodings.Web.dll)", - "System.Text.Json @ 8.0.1425.11118 (/app/System.Text.Json.dll)", - "System.Text.RegularExpressions @ 8.0.1425.11118 (/app/System.Text.RegularExpressions.dll)", - "System.Threading @ 8.0.1425.11118 (/app/System.Threading.dll)", - "System.Threading.Channels @ 8.0.1425.11118 (/app/System.Threading.Channels.dll)", - "System.Threading.Overlapped @ 8.0.1425.11118 (/app/System.Threading.Overlapped.dll)", - "System.Threading.Tasks @ 8.0.1425.11118 (/app/System.Threading.Tasks.dll)", - "System.Threading.Tasks.Dataflow @ 8.0.1425.11118 (/app/System.Threading.Tasks.Dataflow.dll)", - "System.Threading.Tasks.Extensions @ 8.0.1425.11118 (/app/System.Threading.Tasks.Extensions.dll)", - "System.Threading.Tasks.Parallel @ 8.0.1425.11118 (/app/System.Threading.Tasks.Parallel.dll)", - "System.Threading.Thread @ 8.0.1425.11118 (/app/System.Threading.Thread.dll)", - "System.Threading.ThreadPool @ 8.0.1425.11118 (/app/System.Threading.ThreadPool.dll)", - "System.Threading.Timer @ 8.0.1425.11118 (/app/System.Threading.Timer.dll)", - "System.Transactions @ 8.0.1425.11118 (/app/System.Transactions.dll)", - "System.Transactions.Local @ 8.0.1425.11118 (/app/System.Transactions.Local.dll)", - "System.ValueTuple @ 8.0.1425.11118 (/app/System.ValueTuple.dll)", - "System.Web @ 8.0.1425.11118 (/app/System.Web.dll)", - "System.Web.HttpUtility @ 8.0.1425.11118 (/app/System.Web.HttpUtility.dll)", - "System.Windows @ 8.0.1425.11118 (/app/System.Windows.dll)", - "System.Xml @ 8.0.1425.11118 (/app/System.Xml.dll)", - "System.Xml.Linq @ 8.0.1425.11118 (/app/System.Xml.Linq.dll)", - "System.Xml.ReaderWriter @ 8.0.1425.11118 (/app/System.Xml.ReaderWriter.dll)", - "System.Xml.Serialization @ 8.0.1425.11118 (/app/System.Xml.Serialization.dll)", - "System.Xml.XDocument @ 8.0.1425.11118 (/app/System.Xml.XDocument.dll)", - "System.Xml.XPath @ 8.0.1425.11118 (/app/System.Xml.XPath.dll)", - "System.Xml.XPath.XDocument @ 8.0.1425.11118 (/app/System.Xml.XPath.XDocument.dll)", - "System.Xml.XmlDocument @ 8.0.1425.11118 (/app/System.Xml.XmlDocument.dll)", - "System.Xml.XmlSerializer @ 8.0.1425.11118 (/app/System.Xml.XmlSerializer.dll)", - "WindowsBase @ 8.0.1425.11118 (/app/WindowsBase.dll)", - "dotnetapp @ 1.0.0.0 (/app/dotnetapp.dll)", - "mscorlib @ 8.0.1425.11118 (/app/mscorlib.dll)", - "netstandard @ 8.0.1425.11118 (/app/netstandard.dll)", - ) - var net8AppExpectedBinarySelfContainedPkgs []string net8AppExpectedBinarySelfContainedPkgs = append(net8AppExpectedBinarySelfContainedPkgs, net8AppBinaryOnlyPkgs...) net8AppExpectedBinarySelfContainedPkgs = append(net8AppExpectedBinarySelfContainedPkgs, @@ -1197,56 +1022,6 @@ func Test_corruptDotnetPE(t *testing.T) { TestCataloger(t, NewDotnetPortableExecutableCataloger()) } -func Test_extractVersion(t *testing.T) { - tests := []struct { - input string - expected string - }{ - { - input: "1, 0, 0, 0", - expected: "1, 0, 0, 0", - }, - { - input: "Release 73", - expected: "Release 73", - }, - { - input: "4.7.4076.0 built by: NET472REL1LAST_B", - expected: "4.7.4076.0", - }, - } - - for _, test := range tests { - t.Run(test.input, func(t *testing.T) { - got := extractVersionFromResourcesValue(test.input) - assert.Equal(t, test.expected, got) - }) - } -} - -func Test_spaceNormalize(t *testing.T) { - tests := []struct { - input string - expected string - }{ - { - expected: "some spaces apart", - input: " some spaces\n\t\t \n\rapart\n", - }, - { - expected: "söme ¡nvalid characters", - input: "\rsöme \u0001¡nvalid\t characters\n", - }, - } - - for _, test := range tests { - t.Run(test.expected, func(t *testing.T) { - got := spaceNormalize(test.input) - assert.Equal(t, test.expected, got) - }) - } -} - func Test_corruptDotnetDeps(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, "test-fixtures/glob-paths/src"). @@ -1604,6 +1379,7 @@ func TestParseDotnetDeps(t *testing.T) { } func extractMatchingPackage(t *testing.T, name string, pkgs []pkg.Package) pkg.Package { + t.Helper() for _, p := range pkgs { if p.Name == name { return p diff --git a/syft/pkg/cataloger/dotnet/package_test.go b/syft/pkg/cataloger/dotnet/package_test.go index 32bfe0e54..985b99be3 100644 --- a/syft/pkg/cataloger/dotnet/package_test.go +++ b/syft/pkg/cataloger/dotnet/package_test.go @@ -327,3 +327,53 @@ func Test_NewDotnetBinaryPackage(t *testing.T) { }) } } + +func Test_extractVersion(t *testing.T) { + tests := []struct { + input string + expected string + }{ + { + input: "1, 0, 0, 0", + expected: "1, 0, 0, 0", + }, + { + input: "Release 73", + expected: "Release 73", + }, + { + input: "4.7.4076.0 built by: NET472REL1LAST_B", + expected: "4.7.4076.0", + }, + } + + for _, test := range tests { + t.Run(test.input, func(t *testing.T) { + got := extractVersionFromResourcesValue(test.input) + assert.Equal(t, test.expected, got) + }) + } +} + +func Test_spaceNormalize(t *testing.T) { + tests := []struct { + input string + expected string + }{ + { + expected: "some spaces apart", + input: " some spaces\n\t\t \n\rapart\n", + }, + { + expected: "söme ¡nvalid characters", + input: "\rsöme \u0001¡nvalid\t characters\n", + }, + } + + for _, test := range tests { + t.Run(test.expected, func(t *testing.T) { + got := spaceNormalize(test.input) + assert.Equal(t, test.expected, got) + }) + } +} diff --git a/syft/pkg/cataloger/internal/cpegenerate/dotnet.go b/syft/pkg/cataloger/internal/cpegenerate/dotnet.go new file mode 100644 index 000000000..db6143234 --- /dev/null +++ b/syft/pkg/cataloger/internal/cpegenerate/dotnet.go @@ -0,0 +1,98 @@ +package cpegenerate + +import ( + "strings" + + "github.com/anchore/syft/syft/pkg" +) + +func candidateProductsForDotnet(p pkg.Package) fieldCandidateSet { + products := newFieldCandidateSet() + + switch m := p.Metadata.(type) { + case pkg.DotnetDepsEntry: + products.add(dotnetProductVariants(m.Name)...) + for _, pe := range m.Executables { + if pe.ProductName == "" { + continue + } + products.add(dotnetProductVariants(pe.ProductName)...) + } + case pkg.DotnetPortableExecutableEntry: + if m.ProductName != "" { + products.add(dotnetProductVariants(m.ProductName)...) + } + case pkg.DotnetPackagesLockEntry: + products.add(dotnetProductVariants(m.Name)...) + } + + return products +} + +func dotnetProductVariants(names ...string) []fieldCandidate { + var variants []fieldCandidate + for _, suff := range []string{"", "_.net"} { + for _, name := range names { + if name == "" { + continue + } + if suff != "" && strings.HasSuffix(name, suff) { + continue + } + variants = append(variants, fieldCandidate{ + value: normalizeDotnetReference(name) + suff, + disallowSubSelections: true, + disallowDelimiterVariations: true, + }) + } + } + return variants +} + +func normalizeDotnetReference(name string) string { + name = strings.TrimSpace(strings.ToLower(name)) + name = strings.TrimSuffix(name, ".dll") + name = strings.ReplaceAll(name, "-", "_") + name = strings.ReplaceAll(name, " ", "_") + name = strings.ReplaceAll(name, ".", "_") + return name +} + +func candidateVendorsForDotnet(p pkg.Package) fieldCandidateSet { + vendors := newFieldCandidateSet() + + switch m := p.Metadata.(type) { + case pkg.DotnetDepsEntry: + vendors.add(fieldCandidate{ + value: normalizeDotnetReference(m.Name), + disallowSubSelections: true, + disallowDelimiterVariations: true, + }) + for _, pe := range m.Executables { + if pe.CompanyName == "" { + continue + } + vendors.add(fieldCandidate{ + value: normalizeDotnetReference(pe.CompanyName), + disallowSubSelections: true, + disallowDelimiterVariations: true, + }) + } + case pkg.DotnetPortableExecutableEntry: + if m.CompanyName != "" { + vendors.add(fieldCandidate{ + value: normalizeDotnetReference(m.CompanyName), + disallowSubSelections: true, + disallowDelimiterVariations: true, + }) + } + case pkg.DotnetPackagesLockEntry: + vendors.add(fieldCandidate{ + value: normalizeDotnetReference(m.Name), + disallowSubSelections: true, + disallowDelimiterVariations: true, + }) + } + + return vendors +} diff --git a/syft/pkg/cataloger/internal/cpegenerate/generate.go b/syft/pkg/cataloger/internal/cpegenerate/generate.go index 2c9f5175c..3a8e7ac9d 100644 --- a/syft/pkg/cataloger/internal/cpegenerate/generate.go +++ b/syft/pkg/cataloger/internal/cpegenerate/generate.go @@ -186,7 +186,8 @@ func candidateVendors(p pkg.Package) []string { // are the elasticsearch gem, xstream jar, and rack gem... all of these cases you can find vulnerabilities // with CPEs where the vendor is the product name and doesn't appear to be derived from any available package // metadata. - vendors := newFieldCandidateSet(candidateProducts(p)...) + vendors := newFieldCandidateSet() + vendors.union(candidateProductSet(p)) switch p.Language { case pkg.JavaScript: @@ -207,7 +208,10 @@ func candidateVendors(p pkg.Package) []string { } switch p.Metadata.(type) { - case pkg.RpmDBEntry: + case pkg.DotnetDepsEntry, pkg.DotnetPackagesLockEntry, pkg.DotnetPortableExecutableEntry: + vendors.clear() + vendors.union(candidateVendorsForDotnet(p)) + case pkg.RpmDBEntry, pkg.RpmArchive: vendors.union(candidateVendorsForRPM(p)) case pkg.RubyGemspec: vendors.union(candidateVendorsForRuby(p)) @@ -256,18 +260,25 @@ func candidateVendors(p pkg.Package) []string { } func candidateProducts(p pkg.Package) []string { + return candidateProductSet(p).uniqueValues() +} + +func candidateProductSet(p pkg.Package) fieldCandidateSet { products := newFieldCandidateSet(p.Name) _, hasJavaMetadata := p.Metadata.(pkg.JavaArchive) switch { - case p.Language == pkg.Python: + case p.Language == pkg.Dotnet || p.Type == pkg.DotnetPkg: + products.clear() + products.union(candidateProductsForDotnet(p)) + case p.Language == pkg.Python || p.Type == pkg.PythonPkg: if !strings.HasPrefix(p.Name, "python") { products.addValue("python-" + p.Name) } - case p.Language == pkg.Java || hasJavaMetadata: + case p.Language == pkg.Java || hasJavaMetadata || p.Type == pkg.JavaPkg: products.addValue(candidateProductsForJava(p)...) - case p.Language == pkg.Go: + case p.Language == pkg.Go || p.Type == pkg.GoModulePkg: // replace all candidates with only the golang-specific helper products.clear() @@ -277,11 +288,10 @@ func candidateProducts(p pkg.Package) []string { } } - if _, hasAPKMetadata := p.Metadata.(pkg.ApkDBEntry); hasAPKMetadata { + switch p.Metadata.(type) { + case pkg.ApkDBEntry: products.union(candidateProductsForAPK(p)) - } - - if _, hasWordpressMetadata := p.Metadata.(pkg.WordpressPluginEntry); hasWordpressMetadata { + case pkg.WordpressPluginEntry: products.clear() products.union(candidateProductsForWordpressPlugin(p)) } @@ -299,7 +309,7 @@ func candidateProducts(p pkg.Package) []string { // remove known candidate removals products.removeByValue(findProductsToRemove(defaultCandidateRemovals, p.Type, p.Name)...) - return products.uniqueValues() + return products } func addAllSubSelections(fields fieldCandidateSet) { diff --git a/syft/pkg/cataloger/internal/cpegenerate/generate_test.go b/syft/pkg/cataloger/internal/cpegenerate/generate_test.go index bd4070852..d78baea1b 100644 --- a/syft/pkg/cataloger/internal/cpegenerate/generate_test.go +++ b/syft/pkg/cataloger/internal/cpegenerate/generate_test.go @@ -308,6 +308,23 @@ func TestGeneratePackageCPEs(t *testing.T) { "cpe:2.3:a:apache:cxf_rt_bindings_xml:3.3.10:*:*:*:*:*:*:*", }, }, + { + name: "rpm archive vendor selection", + p: pkg.Package{ + Name: "name", + Version: "3.2", + FoundBy: "some-analyzer", + Type: pkg.RpmPkg, + Metadata: pkg.RpmArchive{ + Vendor: "some-vendor", + }, + }, + expected: []string{ + "cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*", + "cpe:2.3:a:some-vendor:name:3.2:*:*:*:*:*:*:*", + "cpe:2.3:a:some_vendor:name:3.2:*:*:*:*:*:*:*", + }, + }, { name: "rpm vendor selection", p: pkg.Package{ @@ -760,6 +777,75 @@ func TestGeneratePackageCPEs(t *testing.T) { "cpe:2.3:a:wow_estore:wp_coder:2.5.1:*:*:*:*:wordpress:*:*", }, }, + { + name: "dotnet deps.json", + p: pkg.Package{ + Name: "Something", + Version: "2.5.1", + Type: pkg.DotnetPkg, + Metadata: pkg.DotnetDepsEntry{ + Name: "Something-Else", + + Executables: map[string]pkg.DotnetPortableExecutableEntry{ + "1": { + AssemblyVersion: "assembly-version!", + LegalCopyright: "copyright!", + Comments: "comments!", + InternalName: "internal!", + CompanyName: "company!", + ProductName: "product!", + ProductVersion: "version!", + }, + }, + }, + }, + expected: []string{ + "cpe:2.3:a:company\\!:product\\!:2.5.1:*:*:*:*:*:*:*", + "cpe:2.3:a:company\\!:product\\!_.net:2.5.1:*:*:*:*:*:*:*", + "cpe:2.3:a:company\\!:something_else:2.5.1:*:*:*:*:*:*:*", + "cpe:2.3:a:company\\!:something_else_.net:2.5.1:*:*:*:*:*:*:*", + "cpe:2.3:a:something_else:product\\!:2.5.1:*:*:*:*:*:*:*", + "cpe:2.3:a:something_else:product\\!_.net:2.5.1:*:*:*:*:*:*:*", + "cpe:2.3:a:something_else:something_else:2.5.1:*:*:*:*:*:*:*", + "cpe:2.3:a:something_else:something_else_.net:2.5.1:*:*:*:*:*:*:*", + }, + }, + { + name: "dotnet executable", + p: pkg.Package{ + Name: "Something", + Version: "2.5.1", + Type: pkg.DotnetPkg, + Metadata: pkg.DotnetPortableExecutableEntry{ + AssemblyVersion: "assembly-version!", + LegalCopyright: "copyright!", + Comments: "comments!", + InternalName: "internal!", + CompanyName: "company!", + ProductName: "product!", + ProductVersion: "version!", + }, + }, + expected: []string{ + "cpe:2.3:a:company\\!:product\\!:2.5.1:*:*:*:*:*:*:*", + "cpe:2.3:a:company\\!:product\\!_.net:2.5.1:*:*:*:*:*:*:*", + }, + }, + { + name: "dotnet package.lock", + p: pkg.Package{ + Name: "Something", + Version: "2.5.1", + Type: pkg.DotnetPkg, + Metadata: pkg.DotnetPackagesLockEntry{ + Name: "Something-Else", + }, + }, + expected: []string{ + "cpe:2.3:a:something_else:something_else:2.5.1:*:*:*:*:*:*:*", + "cpe:2.3:a:something_else:something_else_.net:2.5.1:*:*:*:*:*:*:*", + }, + }, } for _, test := range tests { @@ -937,12 +1023,12 @@ func TestCandidateVendor(t *testing.T) { Name: "Django", Type: pkg.PythonPkg, }, - expected: []string{"djangoproject" /* <-- known good names | default guess --> */, "Django"}, + expected: []string{"djangoproject", "python-Django", "python_Django" /* <-- known good names | default guess --> */, "python", "Django"}, }, } for _, test := range tests { - t.Run(fmt.Sprintf("%+v %+v", test.p, test.expected), func(t *testing.T) { + t.Run(test.name, func(t *testing.T) { assert.ElementsMatch(t, test.expected, candidateVendors(test.p)) }) } diff --git a/syft/pkg/cataloger/internal/cpegenerate/rpm.go b/syft/pkg/cataloger/internal/cpegenerate/rpm.go index caf664e42..ecaea15f5 100644 --- a/syft/pkg/cataloger/internal/cpegenerate/rpm.go +++ b/syft/pkg/cataloger/internal/cpegenerate/rpm.go @@ -3,18 +3,23 @@ package cpegenerate import "github.com/anchore/syft/syft/pkg" func candidateVendorsForRPM(p pkg.Package) fieldCandidateSet { - metadata, ok := p.Metadata.(pkg.RpmDBEntry) - if !ok { - return nil - } - vendors := newFieldCandidateSet() - if metadata.Vendor != "" { - vendors.add(fieldCandidate{ - value: normalizeName(metadata.Vendor), - disallowSubSelections: true, - }) + switch m := p.Metadata.(type) { + case pkg.RpmDBEntry: + if m.Vendor != "" { + vendors.add(fieldCandidate{ + value: normalizeName(m.Vendor), + disallowSubSelections: true, + }) + } + case pkg.RpmArchive: + if m.Vendor != "" { + vendors.add(fieldCandidate{ + value: normalizeName(m.Vendor), + disallowSubSelections: true, + }) + } } return vendors