github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/pkg/cataloger/binary/cataloger.go (about) 1 package binary 2 3 import ( 4 "github.com/anchore/syft/internal/log" 5 "github.com/anchore/syft/syft/artifact" 6 "github.com/anchore/syft/syft/file" 7 "github.com/anchore/syft/syft/pkg" 8 ) 9 10 const catalogerName = "binary-cataloger" 11 12 func NewCataloger() *Cataloger { 13 return &Cataloger{} 14 } 15 16 // Cataloger is the cataloger responsible for surfacing evidence of a very limited set of binary files, 17 // which have been identified by the classifiers. The Cataloger is _NOT_ a place to catalog any and every 18 // binary, but rather the specific set that has been curated to be important, predominantly related to toolchain- 19 // related runtimes like Python, Go, Java, or Node. Some exceptions can be made for widely-used binaries such 20 // as busybox. 21 type Cataloger struct{} 22 23 // Name returns a string that uniquely describes the Cataloger 24 func (c Cataloger) Name() string { 25 return catalogerName 26 } 27 28 // Catalog is given an object to resolve file references and content, this function returns any discovered Packages 29 // after analyzing the catalog source. 30 func (c Cataloger) Catalog(resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) { 31 var packages []pkg.Package 32 var relationships []artifact.Relationship 33 34 for _, cls := range defaultClassifiers { 35 log.WithFields("classifier", cls.Class).Trace("cataloging binaries") 36 newPkgs, err := catalog(resolver, cls) 37 if err != nil { 38 log.WithFields("error", err, "classifier", cls.Class).Warn("unable to catalog binary package: %w", err) 39 continue 40 } 41 newPackages: 42 for i := range newPkgs { 43 newPkg := &newPkgs[i] 44 for j := range packages { 45 p := &packages[j] 46 // consolidate identical packages found in different locations or by different classifiers 47 if packagesMatch(p, newPkg) { 48 mergePackages(p, newPkg) 49 continue newPackages 50 } 51 } 52 packages = append(packages, *newPkg) 53 } 54 } 55 56 return packages, relationships, nil 57 } 58 59 // mergePackages merges information from the extra package into the target package 60 func mergePackages(target *pkg.Package, extra *pkg.Package) { 61 // add the locations 62 target.Locations.Add(extra.Locations.ToSlice()...) 63 // update the metadata to indicate which classifiers were used 64 meta, _ := target.Metadata.(pkg.BinaryMetadata) 65 if m, ok := extra.Metadata.(pkg.BinaryMetadata); ok { 66 meta.Matches = append(meta.Matches, m.Matches...) 67 } 68 target.Metadata = meta 69 } 70 71 func catalog(resolver file.Resolver, cls classifier) (packages []pkg.Package, err error) { 72 locations, err := resolver.FilesByGlob(cls.FileGlob) 73 if err != nil { 74 return nil, err 75 } 76 for _, location := range locations { 77 pkgs, err := cls.EvidenceMatcher(resolver, cls, location) 78 if err != nil { 79 return nil, err 80 } 81 packages = append(packages, pkgs...) 82 } 83 return packages, nil 84 } 85 86 // packagesMatch returns true if the binary packages "match" based on basic criteria 87 func packagesMatch(p1 *pkg.Package, p2 *pkg.Package) bool { 88 if p1.Name != p2.Name || 89 p1.Version != p2.Version || 90 p1.Language != p2.Language || 91 p1.Type != p2.Type { 92 return false 93 } 94 95 return true 96 }