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