github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/pkg/cataloger/nix/cataloger.go (about) 1 /* 2 Package nix provides a concrete Cataloger implementation for packages within the Nix packaging ecosystem. 3 */ 4 package nix 5 6 import ( 7 "context" 8 "fmt" 9 10 "github.com/bmatcuk/doublestar/v4" 11 12 "github.com/anchore/syft/internal/log" 13 "github.com/anchore/syft/syft/artifact" 14 "github.com/anchore/syft/syft/file" 15 "github.com/anchore/syft/syft/pkg" 16 ) 17 18 const catalogerName = "nix-store-cataloger" 19 20 // storeCataloger finds package outputs installed in the Nix store location (/nix/store/*). 21 type storeCataloger struct{} 22 23 func NewStoreCataloger() pkg.Cataloger { 24 return &storeCataloger{} 25 } 26 27 func (c *storeCataloger) Name() string { 28 return catalogerName 29 } 30 31 func (c *storeCataloger) Catalog(ctx context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) { 32 // we want to search for only directories, which isn't possible via the stereoscope API, so we need to apply the glob manually on all returned paths 33 var pkgs []pkg.Package 34 var filesByPath = make(map[string]*file.LocationSet) 35 ctx, cancel := context.WithCancel(ctx) 36 defer cancel() 37 for location := range resolver.AllLocations(ctx) { 38 matchesStorePath, err := doublestar.Match("**/nix/store/*", location.RealPath) 39 if err != nil { 40 return nil, nil, fmt.Errorf("failed to match nix store path: %w", err) 41 } 42 43 parentStorePath := findParentNixStorePath(location.RealPath) 44 if parentStorePath != "" { 45 if _, ok := filesByPath[parentStorePath]; !ok { 46 s := file.NewLocationSet() 47 filesByPath[parentStorePath] = &s 48 } 49 filesByPath[parentStorePath].Add(location) 50 } 51 52 if !matchesStorePath { 53 continue 54 } 55 56 storePath := parseNixStorePath(location.RealPath) 57 58 if storePath == nil || !storePath.isValidPackage() { 59 continue 60 } 61 62 p := newNixStorePackage(*storePath, location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)) 63 pkgs = append(pkgs, p) 64 } 65 66 // add file sets to packages 67 for i := range pkgs { 68 p := &pkgs[i] 69 locations := p.Locations.ToSlice() 70 if len(locations) == 0 { 71 log.WithFields("package", p.Name).Warn("nix package has no evidence locations associated") 72 continue 73 } 74 parentStorePath := locations[0].RealPath 75 files, ok := filesByPath[parentStorePath] 76 if !ok { 77 log.WithFields("path", parentStorePath, "nix-store-path", parentStorePath).Warn("found a nix store file for a non-existent package") 78 continue 79 } 80 appendFiles(p, files.ToSlice()...) 81 } 82 83 return pkgs, nil, nil 84 } 85 86 func appendFiles(p *pkg.Package, location ...file.Location) { 87 metadata, ok := p.Metadata.(pkg.NixStoreEntry) 88 if !ok { 89 log.WithFields("package", p.Name).Warn("nix package metadata missing") 90 return 91 } 92 93 for _, l := range location { 94 metadata.Files = append(metadata.Files, l.RealPath) 95 } 96 97 if metadata.Files == nil { 98 // note: we always have an allocated collection for output 99 metadata.Files = []string{} 100 } 101 102 p.Metadata = metadata 103 p.SetID() 104 }