diff --git a/syft/pkg/cataloger/dotnet/package.go b/syft/pkg/cataloger/dotnet/package.go index c8cb261a6fd..a7b3b209f89 100644 --- a/syft/pkg/cataloger/dotnet/package.go +++ b/syft/pkg/cataloger/dotnet/package.go @@ -1,6 +1,8 @@ package dotnet import ( + "fmt" + "regexp" "strings" "github.com/anchore/packageurl-go" @@ -9,13 +11,7 @@ import ( ) func newDotnetDepsPackage(nameVersion string, lib dotnetDepsLibrary, locations ...file.Location) *pkg.Package { - if lib.Type != "package" { - return nil - } - - fields := strings.Split(nameVersion, "/") - name := fields[0] - version := fields[1] + name, version := extractNameAndVersion(nameVersion) m := pkg.DotnetDepsMetadata{ Name: name, @@ -41,6 +37,27 @@ func newDotnetDepsPackage(nameVersion string, lib dotnetDepsLibrary, locations . return p } +func getDepsJSONFilePrefix(p string) string { + r := regexp.MustCompile(`([^\/]+)\.deps\.json$`) + match := r.FindStringSubmatch(p) + if len(match) > 1 { + return match[1] + } + return "" +} + +func extractNameAndVersion(nameVersion string) (name, version string) { + fields := strings.Split(nameVersion, "/") + name = fields[0] + version = fields[1] + return +} + +func createNameAndVersion(name, version string) (nameVersion string) { + nameVersion = fmt.Sprintf("%s/%s", name, version) + return +} + func packageURL(m pkg.DotnetDepsMetadata) string { var qualifiers packageurl.Qualifiers diff --git a/syft/pkg/cataloger/dotnet/parse_dotnet_deps.go b/syft/pkg/cataloger/dotnet/parse_dotnet_deps.go index 2c7e1cf0bf9..c471af5bc33 100644 --- a/syft/pkg/cataloger/dotnet/parse_dotnet_deps.go +++ b/syft/pkg/cataloger/dotnet/parse_dotnet_deps.go @@ -5,6 +5,7 @@ import ( "fmt" "sort" + "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" @@ -13,8 +14,18 @@ import ( var _ generic.Parser = parseDotnetDeps +type dotnetRuntimeTarget struct { + Name string `json:"name"` +} + +type dotnetDepsTarget struct { + Dependencies map[string]string `json:"dependencies"` + Runtime map[string]struct{} `json:"runtime"` +} type dotnetDeps struct { - Libraries map[string]dotnetDepsLibrary `json:"libraries"` + RuntimeTarget dotnetRuntimeTarget `json:"runtimeTarget"` + Targets map[string]map[string]dotnetDepsTarget `json:"targets"` + Libraries map[string]dotnetDepsLibrary `json:"libraries"` } type dotnetDepsLibrary struct { @@ -24,8 +35,11 @@ type dotnetDepsLibrary struct { HashPath string `json:"hashPath"` } +//nolint:funlen func parseDotnetDeps(_ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { var pkgs []pkg.Package + var pkgMap = make(map[string]pkg.Package) + var relationships []artifact.Relationship dec := json.NewDecoder(reader) @@ -35,11 +49,9 @@ func parseDotnetDeps(_ file.Resolver, _ *generic.Environment, reader file.Locati } var names []string - for nameVersion := range p.Libraries { names = append(names, nameVersion) } - // sort the names so that the order of the packages is deterministic sort.Strings(names) @@ -53,8 +65,53 @@ func parseDotnetDeps(_ file.Resolver, _ *generic.Environment, reader file.Locati if dotnetPkg != nil { pkgs = append(pkgs, *dotnetPkg) + pkgMap[nameVersion] = *dotnetPkg + } + } + + rootName := getDepsJSONFilePrefix(reader.AccessPath()) + if rootName == "" { + return nil, nil, fmt.Errorf("unable to determine root package name from deps.json file: %s", reader.AccessPath()) + } + var rootpkg *pkg.Package + for nameVersion, lib := range p.Libraries { + name, _ := extractNameAndVersion(nameVersion) + if lib.Type == "project" && name == rootName { + rootpkg = newDotnetDepsPackage( + nameVersion, + lib, + reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), + ) + } + } + + if rootpkg == nil { + return nil, nil, fmt.Errorf("unable to determine root package from deps.json file: %s", reader.AccessPath()) + } + + for pkgNameVersion, target := range p.Targets[p.RuntimeTarget.Name] { + for depName, depVersion := range target.Dependencies { + depNameVersion := createNameAndVersion(depName, depVersion) + depPkg, ok := pkgMap[depNameVersion] + if !ok { + log.Debug("unable to find package in map", depNameVersion) + continue + } + pkg, ok := pkgMap[pkgNameVersion] + if !ok { + log.Debug("unable to find package in map", pkgNameVersion) + continue + } + rel := artifact.Relationship{ + From: depPkg, + To: pkg, + Type: artifact.DependencyOfRelationship, + } + relationships = append(relationships, rel) } } - return pkgs, nil, nil + pkg.Sort(pkgs) + pkg.SortRelationships(relationships) + return pkgs, relationships, nil } diff --git a/syft/pkg/cataloger/dotnet/parse_dotnet_deps_test.go b/syft/pkg/cataloger/dotnet/parse_dotnet_deps_test.go index b8535374472..1815a4439f5 100644 --- a/syft/pkg/cataloger/dotnet/parse_dotnet_deps_test.go +++ b/syft/pkg/cataloger/dotnet/parse_dotnet_deps_test.go @@ -12,202 +12,339 @@ import ( func TestParseDotnetDeps(t *testing.T) { fixture := "test-fixtures/TestLibrary.deps.json" fixtureLocationSet := file.NewLocationSet(file.NewLocation(fixture)) - expected := []pkg.Package{ - { - Name: "AWSSDK.Core", - Version: "3.7.10.6", - PURL: "pkg:nuget/AWSSDK.Core@3.7.10.6", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "AWSSDK.Core", - Version: "3.7.10.6", - Sha512: "sha512-kHBB+QmosVaG6DpngXQ8OlLVVNMzltNITfsRr68Z90qO7dSqJ2EHNd8dtBU1u3AQQLqqFHOY0lfmbpexeH6Pew==", - Path: "awssdk.core/3.7.10.6", - HashPath: "awssdk.core.3.7.10.6.nupkg.sha512", - }, - }, - { - Name: "Microsoft.Extensions.DependencyInjection.Abstractions", - Version: "6.0.0", - PURL: "pkg:nuget/Microsoft.Extensions.DependencyInjection.Abstractions@6.0.0", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "Microsoft.Extensions.DependencyInjection.Abstractions", - Version: "6.0.0", - Sha512: "sha512-xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==", - Path: "microsoft.extensions.dependencyinjection.abstractions/6.0.0", - HashPath: "microsoft.extensions.dependencyinjection.abstractions.6.0.0.nupkg.sha512", - }, - }, - { - Name: "Microsoft.Extensions.DependencyInjection", - Version: "6.0.0", - PURL: "pkg:nuget/Microsoft.Extensions.DependencyInjection@6.0.0", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "Microsoft.Extensions.DependencyInjection", - Version: "6.0.0", - Sha512: "sha512-k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", - Path: "microsoft.extensions.dependencyinjection/6.0.0", - HashPath: "microsoft.extensions.dependencyinjection.6.0.0.nupkg.sha512", - }, - }, - { - Name: "Microsoft.Extensions.Logging.Abstractions", - Version: "6.0.0", - PURL: "pkg:nuget/Microsoft.Extensions.Logging.Abstractions@6.0.0", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "Microsoft.Extensions.Logging.Abstractions", - Version: "6.0.0", - Sha512: "sha512-/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==", - Path: "microsoft.extensions.logging.abstractions/6.0.0", - HashPath: "microsoft.extensions.logging.abstractions.6.0.0.nupkg.sha512", - }, - }, - { - Name: "Microsoft.Extensions.Logging", - Version: "6.0.0", - PURL: "pkg:nuget/Microsoft.Extensions.Logging@6.0.0", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "Microsoft.Extensions.Logging", - Version: "6.0.0", - Sha512: "sha512-eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", - Path: "microsoft.extensions.logging/6.0.0", - HashPath: "microsoft.extensions.logging.6.0.0.nupkg.sha512", - }, + rootPkg := pkg.Package{ + Name: "TestLibrary", + Version: "1.0.0", + PURL: "pkg:nuget/TestLibrary@1.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "TestLibrary", + Version: "1.0.0", }, + } + testCommon := pkg.Package{ + Name: "TestCommon", + Version: "1.0.0", + PURL: "pkg:nuget/TestCommon@1.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "TestCommon", + Version: "1.0.0", + }, + } + awssdkcore := pkg.Package{ + Name: "AWSSDK.Core", + Version: "3.7.10.6", + PURL: "pkg:nuget/AWSSDK.Core@3.7.10.6", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "AWSSDK.Core", + Version: "3.7.10.6", + Sha512: "sha512-kHBB+QmosVaG6DpngXQ8OlLVVNMzltNITfsRr68Z90qO7dSqJ2EHNd8dtBU1u3AQQLqqFHOY0lfmbpexeH6Pew==", + Path: "awssdk.core/3.7.10.6", + HashPath: "awssdk.core.3.7.10.6.nupkg.sha512", + }, + } + msftDependencyInjectionAbstractions := pkg.Package{ + Name: "Microsoft.Extensions.DependencyInjection.Abstractions", + Version: "6.0.0", + PURL: "pkg:nuget/Microsoft.Extensions.DependencyInjection.Abstractions@6.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Microsoft.Extensions.DependencyInjection.Abstractions", + Version: "6.0.0", + Sha512: "sha512-xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==", + Path: "microsoft.extensions.dependencyinjection.abstractions/6.0.0", + HashPath: "microsoft.extensions.dependencyinjection.abstractions.6.0.0.nupkg.sha512", + }, + } + msftDependencyInjection := pkg.Package{ + Name: "Microsoft.Extensions.DependencyInjection", + Version: "6.0.0", + PURL: "pkg:nuget/Microsoft.Extensions.DependencyInjection@6.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Microsoft.Extensions.DependencyInjection", + Version: "6.0.0", + Sha512: "sha512-k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + Path: "microsoft.extensions.dependencyinjection/6.0.0", + HashPath: "microsoft.extensions.dependencyinjection.6.0.0.nupkg.sha512", + }, + } + msftLoggingAbstractions := pkg.Package{ + Name: "Microsoft.Extensions.Logging.Abstractions", + Version: "6.0.0", + PURL: "pkg:nuget/Microsoft.Extensions.Logging.Abstractions@6.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Microsoft.Extensions.Logging.Abstractions", + Version: "6.0.0", + Sha512: "sha512-/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==", + Path: "microsoft.extensions.logging.abstractions/6.0.0", + HashPath: "microsoft.extensions.logging.abstractions.6.0.0.nupkg.sha512", + }, + } + msftExtensionsLogging := pkg.Package{ + Name: "Microsoft.Extensions.Logging", + Version: "6.0.0", + PURL: "pkg:nuget/Microsoft.Extensions.Logging@6.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Microsoft.Extensions.Logging", + Version: "6.0.0", + Sha512: "sha512-eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + Path: "microsoft.extensions.logging/6.0.0", + HashPath: "microsoft.extensions.logging.6.0.0.nupkg.sha512", + }, + } + msftExtensionsOptions := pkg.Package{ + Name: "Microsoft.Extensions.Options", + Version: "6.0.0", + PURL: "pkg:nuget/Microsoft.Extensions.Options@6.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Microsoft.Extensions.Options", + Version: "6.0.0", + Sha512: "sha512-dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + Path: "microsoft.extensions.options/6.0.0", + HashPath: "microsoft.extensions.options.6.0.0.nupkg.sha512", + }, + } + msftExtensionsPrimitives := pkg.Package{ + Name: "Microsoft.Extensions.Primitives", + Version: "6.0.0", + PURL: "pkg:nuget/Microsoft.Extensions.Primitives@6.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Microsoft.Extensions.Primitives", + Version: "6.0.0", + Sha512: "sha512-9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + Path: "microsoft.extensions.primitives/6.0.0", + HashPath: "microsoft.extensions.primitives.6.0.0.nupkg.sha512", + }, + } + newtonsoftJson := pkg.Package{ + Name: "Newtonsoft.Json", + Version: "13.0.1", + PURL: "pkg:nuget/Newtonsoft.Json@13.0.1", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Newtonsoft.Json", + Version: "13.0.1", + Sha512: "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==", + Path: "newtonsoft.json/13.0.1", + HashPath: "newtonsoft.json.13.0.1.nupkg.sha512", + }, + } + serilogSinksConsole := pkg.Package{ + Name: "Serilog.Sinks.Console", + Version: "4.0.1", + PURL: "pkg:nuget/Serilog.Sinks.Console@4.0.1", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Serilog.Sinks.Console", + Version: "4.0.1", + Sha512: "sha512-apLOvSJQLlIbKlbx+Y2UDHSP05kJsV7mou+fvJoRGs/iR+jC22r8cuFVMjjfVxz/AD4B2UCltFhE1naRLXwKNw==", + Path: "serilog.sinks.console/4.0.1", + HashPath: "serilog.sinks.console.4.0.1.nupkg.sha512", + }, + } + serilog := pkg.Package{ + Name: "Serilog", + Version: "2.10.0", + PURL: "pkg:nuget/Serilog@2.10.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Serilog", + Version: "2.10.0", + Sha512: "sha512-+QX0hmf37a0/OZLxM3wL7V6/ADvC1XihXN4Kq/p6d8lCPfgkRdiuhbWlMaFjR9Av0dy5F0+MBeDmDdRZN/YwQA==", + Path: "serilog/2.10.0", + HashPath: "serilog.2.10.0.nupkg.sha512", + }, + } + systemDiagnosticsDiagnosticsource := pkg.Package{ + Name: "System.Diagnostics.DiagnosticSource", + Version: "6.0.0", + PURL: "pkg:nuget/System.Diagnostics.DiagnosticSource@6.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "System.Diagnostics.DiagnosticSource", + Version: "6.0.0", + Sha512: "sha512-frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + Path: "system.diagnostics.diagnosticsource/6.0.0", + HashPath: "system.diagnostics.diagnosticsource.6.0.0.nupkg.sha512", + }, + } + systemRuntimeCompilerServicesUnsafe := pkg.Package{ + Name: "System.Runtime.CompilerServices.Unsafe", + Version: "6.0.0", + PURL: "pkg:nuget/System.Runtime.CompilerServices.Unsafe@6.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "System.Runtime.CompilerServices.Unsafe", + Version: "6.0.0", + Sha512: "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==", + Path: "system.runtime.compilerservices.unsafe/6.0.0", + HashPath: "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512", + }, + } + + expectedPkgs := []pkg.Package{ + awssdkcore, + msftDependencyInjection, + msftDependencyInjectionAbstractions, + msftExtensionsLogging, + msftLoggingAbstractions, + msftExtensionsOptions, + msftExtensionsPrimitives, + newtonsoftJson, + serilog, + serilogSinksConsole, + systemDiagnosticsDiagnosticsource, + systemRuntimeCompilerServicesUnsafe, + testCommon, + rootPkg, + } + expectedRelationships := []artifact.Relationship{ + { + From: awssdkcore, + To: testCommon, + Type: artifact.DependencyOfRelationship, + }, + { + From: msftDependencyInjection, + To: msftExtensionsLogging, + Type: artifact.DependencyOfRelationship, + }, + { + From: msftDependencyInjection, + To: rootPkg, + Type: artifact.DependencyOfRelationship, + }, + { + From: msftDependencyInjectionAbstractions, + To: msftDependencyInjection, + Type: artifact.DependencyOfRelationship, + }, + { + From: msftDependencyInjectionAbstractions, + To: msftExtensionsLogging, + Type: artifact.DependencyOfRelationship, + }, + { + From: msftDependencyInjectionAbstractions, + To: msftExtensionsOptions, + Type: artifact.DependencyOfRelationship, + }, + { + From: msftExtensionsLogging, + To: rootPkg, + Type: artifact.DependencyOfRelationship, + }, + { + From: msftLoggingAbstractions, + To: msftExtensionsLogging, + Type: artifact.DependencyOfRelationship, + }, + { + From: msftExtensionsOptions, + To: msftExtensionsLogging, + Type: artifact.DependencyOfRelationship, + }, + { + From: msftExtensionsPrimitives, + To: msftExtensionsOptions, + Type: artifact.DependencyOfRelationship, + }, + { + From: newtonsoftJson, + To: rootPkg, + Type: artifact.DependencyOfRelationship, + }, + { + From: serilog, + To: serilogSinksConsole, + Type: artifact.DependencyOfRelationship, + }, + { + From: serilog, + To: rootPkg, + Type: artifact.DependencyOfRelationship, + }, + { + From: serilogSinksConsole, + To: rootPkg, + Type: artifact.DependencyOfRelationship, + }, + { + From: systemDiagnosticsDiagnosticsource, + To: msftExtensionsLogging, + Type: artifact.DependencyOfRelationship, + }, + { + From: systemRuntimeCompilerServicesUnsafe, + To: msftDependencyInjection, + Type: artifact.DependencyOfRelationship, + }, + { + From: systemRuntimeCompilerServicesUnsafe, + To: msftExtensionsPrimitives, + Type: artifact.DependencyOfRelationship, + }, + { + From: systemRuntimeCompilerServicesUnsafe, + To: systemDiagnosticsDiagnosticsource, + Type: artifact.DependencyOfRelationship, + }, { - Name: "Microsoft.Extensions.Options", - Version: "6.0.0", - PURL: "pkg:nuget/Microsoft.Extensions.Options@6.0.0", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "Microsoft.Extensions.Options", - Version: "6.0.0", - Sha512: "sha512-dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", - Path: "microsoft.extensions.options/6.0.0", - HashPath: "microsoft.extensions.options.6.0.0.nupkg.sha512", - }, - }, - { - Name: "Microsoft.Extensions.Primitives", - Version: "6.0.0", - PURL: "pkg:nuget/Microsoft.Extensions.Primitives@6.0.0", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "Microsoft.Extensions.Primitives", - Version: "6.0.0", - Sha512: "sha512-9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", - Path: "microsoft.extensions.primitives/6.0.0", - HashPath: "microsoft.extensions.primitives.6.0.0.nupkg.sha512", - }, - }, - { - Name: "Newtonsoft.Json", - Version: "13.0.1", - PURL: "pkg:nuget/Newtonsoft.Json@13.0.1", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "Newtonsoft.Json", - Version: "13.0.1", - Sha512: "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==", - Path: "newtonsoft.json/13.0.1", - HashPath: "newtonsoft.json.13.0.1.nupkg.sha512", - }, - }, - { - Name: "Serilog.Sinks.Console", - Version: "4.0.1", - PURL: "pkg:nuget/Serilog.Sinks.Console@4.0.1", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "Serilog.Sinks.Console", - Version: "4.0.1", - Sha512: "sha512-apLOvSJQLlIbKlbx+Y2UDHSP05kJsV7mou+fvJoRGs/iR+jC22r8cuFVMjjfVxz/AD4B2UCltFhE1naRLXwKNw==", - Path: "serilog.sinks.console/4.0.1", - HashPath: "serilog.sinks.console.4.0.1.nupkg.sha512", - }, - }, - { - Name: "Serilog", - Version: "2.10.0", - PURL: "pkg:nuget/Serilog@2.10.0", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "Serilog", - Version: "2.10.0", - Sha512: "sha512-+QX0hmf37a0/OZLxM3wL7V6/ADvC1XihXN4Kq/p6d8lCPfgkRdiuhbWlMaFjR9Av0dy5F0+MBeDmDdRZN/YwQA==", - Path: "serilog/2.10.0", - HashPath: "serilog.2.10.0.nupkg.sha512", - }, - }, - { - Name: "System.Diagnostics.DiagnosticSource", - Version: "6.0.0", - PURL: "pkg:nuget/System.Diagnostics.DiagnosticSource@6.0.0", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "System.Diagnostics.DiagnosticSource", - Version: "6.0.0", - Sha512: "sha512-frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", - Path: "system.diagnostics.diagnosticsource/6.0.0", - HashPath: "system.diagnostics.diagnosticsource.6.0.0.nupkg.sha512", - }, - }, - { - Name: "System.Runtime.CompilerServices.Unsafe", - Version: "6.0.0", - PURL: "pkg:nuget/System.Runtime.CompilerServices.Unsafe@6.0.0", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "System.Runtime.CompilerServices.Unsafe", - Version: "6.0.0", - Sha512: "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==", - Path: "system.runtime.compilerservices.unsafe/6.0.0", - HashPath: "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512", - }, + From: testCommon, + To: rootPkg, + Type: artifact.DependencyOfRelationship, }, } - var expectedRelationships []artifact.Relationship - pkgtest.TestFileParser(t, fixture, parseDotnetDeps, expected, expectedRelationships) + pkgtest.TestFileParser(t, fixture, parseDotnetDeps, expectedPkgs, expectedRelationships) } diff --git a/syft/pkg/cataloger/dotnet/test-fixtures/TestLibrary.deps.json b/syft/pkg/cataloger/dotnet/test-fixtures/TestLibrary.deps.json index a429ec4094d..788eb909ec6 100644 --- a/syft/pkg/cataloger/dotnet/test-fixtures/TestLibrary.deps.json +++ b/syft/pkg/cataloger/dotnet/test-fixtures/TestLibrary.deps.json @@ -232,4 +232,4 @@ "sha512": "" } } -} \ No newline at end of file +} diff --git a/syft/pkg/relationships.go b/syft/pkg/relationships.go index 8e3628cabe1..b18e09df20a 100644 --- a/syft/pkg/relationships.go +++ b/syft/pkg/relationships.go @@ -1,9 +1,44 @@ package pkg -import "github.com/anchore/syft/syft/artifact" +import ( + "sort" + + "github.com/anchore/syft/syft/artifact" +) func NewRelationships(catalog *Collection) []artifact.Relationship { rels := RelationshipsByFileOwnership(catalog) rels = append(rels, RelationshipsEvidentBy(catalog)...) return rels } + +func RelationshipLess(i, j artifact.Relationship) bool { + iFrom, ok1 := i.From.(Package) + iTo, ok2 := i.To.(Package) + jFrom, ok3 := j.From.(Package) + jTo, ok4 := j.To.(Package) + + if !(ok1 && ok2 && ok3 && ok4) { + return false + } + + if iFrom.Name != jFrom.Name { + return iFrom.Name < jFrom.Name + } + if iFrom.Version != jFrom.Version { + return iFrom.Version < jFrom.Version + } + if iTo.Name != jTo.Name { + return iTo.Name < jTo.Name + } + if iTo.Version != jTo.Version { + return iTo.Version < jTo.Version + } + return i.Type < j.Type +} + +func SortRelationships(rels []artifact.Relationship) { + sort.SliceStable(rels, func(i, j int) bool { + return RelationshipLess(rels[i], rels[j]) + }) +} diff --git a/test/integration/catalog_packages_cases_test.go b/test/integration/catalog_packages_cases_test.go index c88b2d559b2..52bd6f2874b 100644 --- a/test/integration/catalog_packages_cases_test.go +++ b/test/integration/catalog_packages_cases_test.go @@ -247,6 +247,8 @@ var dirOnlyTestCases = []testCase{ "Serilog.Sinks.Console": "4.0.1", "System.Diagnostics.DiagnosticSource": "6.0.0", "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "TestCommon": "1.0.0", + "TestLibrary": "1.0.0", }, }, {