github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/syft/pkg/cataloger/generic/cataloger.go (about) 1 package generic 2 3 import ( 4 "github.com/anchore/syft/syft/artifact" 5 "github.com/anchore/syft/syft/file" 6 "github.com/anchore/syft/syft/linux" 7 "github.com/anchore/syft/syft/pkg" 8 "github.com/lineaje-labs/syft/internal" 9 "github.com/lineaje-labs/syft/internal/log" 10 ) 11 12 type processor func(resolver file.Resolver, env Environment) []request 13 14 type request struct { 15 file.Location 16 Parser 17 } 18 19 // Cataloger implements the Catalog interface and is responsible for dispatching the proper parser function for 20 // a given path or glob pattern. This is intended to be reusable across many package cataloger types. 21 type Cataloger struct { 22 processor []processor 23 upstreamCataloger string 24 } 25 26 func (c *Cataloger) WithParserByGlobs(parser Parser, globs ...string) *Cataloger { 27 c.processor = append(c.processor, 28 func(resolver file.Resolver, env Environment) []request { 29 var requests []request 30 for _, g := range globs { 31 log.WithFields("glob", g).Trace("searching for paths matching glob") 32 33 matches, err := resolver.FilesByGlob(g) 34 if err != nil { 35 log.Warnf("unable to process glob=%q: %+v", g, err) 36 continue 37 } 38 requests = append(requests, makeRequests(parser, matches)...) 39 } 40 return requests 41 }, 42 ) 43 return c 44 } 45 46 func (c *Cataloger) WithParserByMimeTypes(parser Parser, types ...string) *Cataloger { 47 c.processor = append(c.processor, 48 func(resolver file.Resolver, env Environment) []request { 49 var requests []request 50 log.WithFields("mimetypes", types).Trace("searching for paths matching mimetype") 51 matches, err := resolver.FilesByMIMEType(types...) 52 if err != nil { 53 log.Warnf("unable to process mimetypes=%+v: %+v", types, err) 54 return nil 55 } 56 requests = append(requests, makeRequests(parser, matches)...) 57 return requests 58 }, 59 ) 60 return c 61 } 62 63 func (c *Cataloger) WithParserByPath(parser Parser, paths ...string) *Cataloger { 64 c.processor = append(c.processor, 65 func(resolver file.Resolver, env Environment) []request { 66 var requests []request 67 for _, p := range paths { 68 log.WithFields("path", p).Trace("searching for path") 69 70 matches, err := resolver.FilesByPath(p) 71 if err != nil { 72 log.Warnf("unable to process path=%q: %+v", p, err) 73 continue 74 } 75 requests = append(requests, makeRequests(parser, matches)...) 76 } 77 return requests 78 }, 79 ) 80 return c 81 } 82 83 func makeRequests(parser Parser, locations []file.Location) []request { 84 var requests []request 85 for _, l := range locations { 86 requests = append(requests, request{ 87 Location: l, 88 Parser: parser, 89 }) 90 } 91 return requests 92 } 93 94 // NewCataloger if provided path-to-parser-function and glob-to-parser-function lookups creates a Cataloger 95 func NewCataloger(upstreamCataloger string) *Cataloger { 96 return &Cataloger{ 97 upstreamCataloger: upstreamCataloger, 98 } 99 } 100 101 // Name returns a string that uniquely describes the upstream cataloger that this Generic Cataloger represents. 102 func (c *Cataloger) Name() string { 103 return c.upstreamCataloger 104 } 105 106 // Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing the catalog source. 107 func (c *Cataloger) Catalog(resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) { 108 var packages []pkg.Package 109 var relationships []artifact.Relationship 110 111 logger := log.Nested("cataloger", c.upstreamCataloger) 112 113 env := Environment{ 114 // TODO: consider passing into the cataloger, this would affect the cataloger interface (and all implementations). This can be deferred until later. 115 LinuxRelease: linux.IdentifyRelease(resolver), 116 } 117 118 for _, req := range c.selectFiles(resolver) { 119 location, parser := req.Location, req.Parser 120 121 log.WithFields("path", location.RealPath).Trace("parsing file contents") 122 123 contentReader, err := resolver.FileContentsByLocation(location) 124 if err != nil { 125 logger.WithFields("location", location.RealPath, "error", err).Warn("unable to fetch contents") 126 continue 127 } 128 129 discoveredPackages, discoveredRelationships, err := parser(resolver, &env, file.NewLocationReadCloser(location, contentReader)) 130 internal.CloseAndLogError(contentReader, location.AccessPath) 131 if err != nil { 132 logger.WithFields("location", location.RealPath, "error", err).Warnf("cataloger failed") 133 continue 134 } 135 136 for _, p := range discoveredPackages { 137 p.FoundBy = c.upstreamCataloger 138 packages = append(packages, p) 139 } 140 141 relationships = append(relationships, discoveredRelationships...) 142 } 143 return packages, relationships, nil 144 } 145 146 // selectFiles takes a set of file trees and resolves and file references of interest for future cataloging 147 func (c *Cataloger) selectFiles(resolver file.Resolver) []request { 148 var requests []request 149 for _, proc := range c.processor { 150 requests = append(requests, proc(resolver, Environment{})...) 151 } 152 return requests 153 }