github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/pkg/relationships_by_file_ownership.go (about) 1 package pkg 2 3 import ( 4 "sort" 5 6 "github.com/bmatcuk/doublestar/v4" 7 "github.com/scylladb/go-set/strset" 8 9 "github.com/anchore/syft/internal/log" 10 "github.com/anchore/syft/syft/artifact" 11 ) 12 13 // AltRpmDBGlob allows db matches against new locations introduced in fedora:{36,37} 14 // See https://github.com/anchore/syft/issues/1077 for larger context 15 const AltRpmDBGlob = "**/rpm/{Packages,Packages.db,rpmdb.sqlite}" 16 17 var globsForbiddenFromBeingOwned = []string{ 18 // any OS DBs should automatically be ignored to prevent cyclic issues (e.g. the "rpm" RPM owns the path to the 19 // RPM DB, so if not ignored that package would own all other packages on the system). 20 ApkDBGlob, 21 DpkgDBGlob, 22 RpmDBGlob, 23 AltRpmDBGlob, 24 // DEB packages share common copyright info between, this does not mean that sharing these paths implies ownership. 25 "/usr/share/doc/**/copyright", 26 } 27 28 type ownershipByFilesMetadata struct { 29 Files []string `json:"files"` 30 } 31 32 // RelationshipsByFileOwnership creates a package-to-package relationship based on discovering which packages have 33 // evidence locations that overlap with ownership claim from another package's package manager metadata. 34 func RelationshipsByFileOwnership(catalog *Collection) []artifact.Relationship { 35 var relationships = findOwnershipByFilesRelationships(catalog) 36 37 var edges []artifact.Relationship 38 for parentID, children := range relationships { 39 for childID, files := range children { 40 fs := files.List() 41 sort.Strings(fs) 42 edges = append(edges, artifact.Relationship{ 43 From: catalog.byID[parentID], 44 To: catalog.byID[childID], 45 Type: artifact.OwnershipByFileOverlapRelationship, 46 Data: ownershipByFilesMetadata{ 47 Files: fs, 48 }, 49 }) 50 } 51 } 52 53 return edges 54 } 55 56 // findOwnershipByFilesRelationships find overlaps in file ownership with a file that defines another package. Specifically, a .Location.Path of 57 // a package is found to be owned by another (from the owner's .Metadata.Files[]). 58 func findOwnershipByFilesRelationships(catalog *Collection) map[artifact.ID]map[artifact.ID]*strset.Set { 59 var relationships = make(map[artifact.ID]map[artifact.ID]*strset.Set) 60 61 if catalog == nil { 62 return relationships 63 } 64 65 for _, candidateOwnerPkg := range catalog.Sorted() { 66 id := candidateOwnerPkg.ID() 67 if candidateOwnerPkg.Metadata == nil { 68 continue 69 } 70 71 // check to see if this is a file owner 72 pkgFileOwner, ok := candidateOwnerPkg.Metadata.(FileOwner) 73 if !ok { 74 continue 75 } 76 for _, ownedFilePath := range pkgFileOwner.OwnedFiles() { 77 if matchesAny(ownedFilePath, globsForbiddenFromBeingOwned) { 78 // we skip over known exceptions to file ownership, such as the RPM package owning 79 // the RPM DB path, otherwise the RPM package would "own" all RPMs, which is not intended 80 continue 81 } 82 83 // look for package(s) in the catalog that may be owned by this package and mark the relationship 84 for _, subPackage := range catalog.PackagesByPath(ownedFilePath) { 85 subID := subPackage.ID() 86 if subID == id { 87 continue 88 } 89 if _, exists := relationships[id]; !exists { 90 relationships[id] = make(map[artifact.ID]*strset.Set) 91 } 92 93 if _, exists := relationships[id][subID]; !exists { 94 relationships[id][subID] = strset.New() 95 } 96 relationships[id][subID].Add(ownedFilePath) 97 } 98 } 99 } 100 101 return relationships 102 } 103 104 func matchesAny(s string, globs []string) bool { 105 for _, g := range globs { 106 matches, err := doublestar.Match(g, s) 107 if err != nil { 108 log.Errorf("failed to match glob=%q : %+v", g, err) 109 } 110 if matches { 111 return true 112 } 113 } 114 return false 115 }