diff --git a/internal/formats/syftjson/to_format_model.go b/internal/formats/syftjson/to_format_model.go index adff02119..b97e22725 100644 --- a/internal/formats/syftjson/to_format_model.go +++ b/internal/formats/syftjson/to_format_model.go @@ -215,6 +215,15 @@ func toRelationshipModel(relationships []artifact.Relationship) []model.Relation Metadata: r.Data, } } + sort.Slice(result, func(i, j int) bool { + if iParent, jParent := result[i].Parent, result[j].Parent; iParent != jParent { + return iParent < jParent + } + if iChild, jChild := result[i].Child, result[j].Child; iChild != jChild { + return iChild < jChild + } + return result[i].Type < result[j].Type + }) return result } diff --git a/syft/pkg/catalog.go b/syft/pkg/catalog.go index 0e4d1098e..7dfff5126 100644 --- a/syft/pkg/catalog.go +++ b/syft/pkg/catalog.go @@ -203,6 +203,10 @@ func (c *Catalog) Sorted(types ...Type) (pkgs []Package) { iLocations := pkgs[i].Locations.ToSlice() jLocations := pkgs[j].Locations.ToSlice() if pkgs[i].Type == pkgs[j].Type && len(iLocations) > 0 && len(jLocations) > 0 { + if iLocations[0].String() == jLocations[0].String() { + // compare IDs as a final fallback + return pkgs[i].ID() < pkgs[j].ID() + } return iLocations[0].String() < jLocations[0].String() } return pkgs[i].Type < pkgs[j].Type diff --git a/syft/pkg/relationships_by_file_ownership.go b/syft/pkg/relationships_by_file_ownership.go index 157c6bf6e..5738b85c1 100644 --- a/syft/pkg/relationships_by_file_ownership.go +++ b/syft/pkg/relationships_by_file_ownership.go @@ -1,6 +1,8 @@ package pkg import ( + "sort" + "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" "github.com/bmatcuk/doublestar/v4" @@ -34,12 +36,14 @@ func RelationshipsByFileOwnership(catalog *Catalog) []artifact.Relationship { var edges []artifact.Relationship for parentID, children := range relationships { for childID, files := range children { + fs := files.List() + sort.Strings(fs) edges = append(edges, artifact.Relationship{ From: catalog.byID[parentID], To: catalog.byID[childID], Type: artifact.OwnershipByFileOverlapRelationship, Data: ownershipByFilesMetadata{ - Files: files.List(), + Files: fs, }, }) }