github.com/nextlinux/gosbom@v0.81.1-0.20230627115839-1ff50c281391/gosbom/pkg/cataloger/nix/cataloger.go (about) 1 package nix 2 3 import ( 4 "fmt" 5 6 "github.com/bmatcuk/doublestar/v4" 7 "github.com/nextlinux/gosbom/gosbom/artifact" 8 "github.com/nextlinux/gosbom/gosbom/file" 9 "github.com/nextlinux/gosbom/gosbom/pkg" 10 "github.com/nextlinux/gosbom/internal/log" 11 ) 12 13 const ( 14 catalogerName = "nix-store-cataloger" 15 nixStoreGlob = "**/nix/store/*" 16 ) 17 18 // StoreCataloger finds package outputs installed in the Nix store location (/nix/store/*). 19 type StoreCataloger struct{} 20 21 func NewStoreCataloger() *StoreCataloger { 22 return &StoreCataloger{} 23 } 24 25 func (c *StoreCataloger) Name() string { 26 return catalogerName 27 } 28 29 func (c *StoreCataloger) Catalog(resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) { 30 // 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 31 var pkgs []pkg.Package 32 var filesByPath = make(map[string]*file.LocationSet) 33 for location := range resolver.AllLocations() { 34 matchesStorePath, err := doublestar.Match(nixStoreGlob, location.RealPath) 35 if err != nil { 36 return nil, nil, fmt.Errorf("failed to match nix store path: %w", err) 37 } 38 39 parentStorePath := findParentNixStorePath(location.RealPath) 40 if parentStorePath != "" { 41 if _, ok := filesByPath[parentStorePath]; !ok { 42 s := file.NewLocationSet() 43 filesByPath[parentStorePath] = &s 44 } 45 filesByPath[parentStorePath].Add(location) 46 } 47 48 if !matchesStorePath { 49 continue 50 } 51 52 storePath := parseNixStorePath(location.RealPath) 53 54 if storePath == nil || !storePath.isValidPackage() { 55 continue 56 } 57 58 p := newNixStorePackage(*storePath, location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)) 59 pkgs = append(pkgs, p) 60 } 61 62 // add file sets to packages 63 for i := range pkgs { 64 p := &pkgs[i] 65 locations := p.Locations.ToSlice() 66 if len(locations) == 0 { 67 log.WithFields("package", p.Name).Warn("nix package has no evidence locations associated") 68 continue 69 } 70 parentStorePath := locations[0].RealPath 71 files, ok := filesByPath[parentStorePath] 72 if !ok { 73 log.WithFields("path", parentStorePath, "nix-store-path", parentStorePath).Warn("found a nix store file for a non-existent package") 74 continue 75 } 76 appendFiles(p, files.ToSlice()...) 77 } 78 79 return pkgs, nil, nil 80 } 81 82 func appendFiles(p *pkg.Package, location ...file.Location) { 83 metadata, ok := p.Metadata.(pkg.NixStoreMetadata) 84 if !ok { 85 log.WithFields("package", p.Name).Warn("nix package metadata missing") 86 return 87 } 88 89 for _, l := range location { 90 metadata.Files = append(metadata.Files, l.RealPath) 91 } 92 93 if metadata.Files == nil { 94 // note: we always have an allocated collection for output 95 metadata.Files = []string{} 96 } 97 98 p.Metadata = metadata 99 p.SetID() 100 }