github.com/anchore/syft@v1.38.2/syft/pkg/cataloger/nix/derivation.go (about) 1 package nix 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/nix-community/go-nix/pkg/derivation" 8 9 "github.com/anchore/syft/internal" 10 "github.com/anchore/syft/syft/file" 11 ) 12 13 type derivationFile struct { 14 Location file.Location 15 derivation.Derivation 16 } 17 18 func newDerivationFromPath(p string, resolver file.Resolver) (*derivationFile, error) { 19 locs, err := resolver.FilesByPath(p) 20 if err != nil { 21 return nil, fmt.Errorf("failed to find derivation: %w", err) 22 } 23 24 if len(locs) == 0 { 25 return nil, nil 26 } 27 28 // only use one reference 29 return newDerivationFromLocation(locs[0], resolver) 30 } 31 32 func newDerivationFromLocation(loc file.Location, resolver file.Resolver) (*derivationFile, error) { 33 reader, err := resolver.FileContentsByLocation(loc) 34 if err != nil { 35 return nil, fmt.Errorf("failed to read derivation: %w", err) 36 } 37 defer internal.CloseAndLogError(reader, loc.RealPath) 38 39 d, err := derivation.ReadDerivation(reader) 40 if err != nil { 41 return nil, fmt.Errorf("failed to parse derivation: %w", err) 42 } 43 44 return &derivationFile{ 45 Location: loc, 46 Derivation: *d, 47 }, nil 48 } 49 50 type derivations struct { 51 derivationsByDrvPath map[string]derivationFile 52 drvPathByOutputPath map[string]string 53 } 54 55 func newDerivations() *derivations { 56 return &derivations{ 57 derivationsByDrvPath: make(map[string]derivationFile), 58 drvPathByOutputPath: make(map[string]string), 59 } 60 } 61 62 func (c *derivations) add(df derivationFile) { 63 c.derivationsByDrvPath[df.Location.RealPath] = df 64 for _, output := range df.Outputs { 65 if output == nil || output.Path == "" { 66 continue 67 } 68 c.drvPathByOutputPath[output.Path] = df.Location.RealPath 69 } 70 } 71 72 func (c *derivations) findDerivationForOutputPath(outputPath string) *derivationFile { 73 if !strings.HasPrefix(outputPath, "/") { 74 outputPath = "/" + outputPath 75 } 76 if drvPath, ok := c.drvPathByOutputPath[outputPath]; ok { 77 d, ok := c.derivationsByDrvPath[drvPath] 78 if ok { 79 return &d 80 } 81 } 82 return nil 83 } 84 85 // given a path as input, assuming it's an output path for a derivation, find all input store paths needed for this particular output path. 86 func (c *derivations) findDependencies(p string) []string { 87 if d, ok := c.derivationsByDrvPath[p]; ok { 88 var deps []string 89 for drvPath, names := range d.InputDerivations { 90 if len(names) == 0 { 91 continue 92 } 93 for _, n := range names { 94 outputPath := c.namedOutputStorePath(drvPath, n) 95 if outputPath == "" { 96 continue 97 } 98 deps = append(deps, outputPath) 99 } 100 } 101 for _, inputSrc := range d.InputSources { 102 if inputSrc == "" { 103 continue 104 } 105 deps = append(deps, inputSrc) 106 } 107 return deps 108 } 109 if drvPath, ok := c.drvPathByOutputPath[p]; ok { 110 return c.findDependencies(drvPath) 111 } 112 return nil 113 } 114 115 func (c *derivations) namedOutputStorePath(drvPath, name string) string { 116 if d, ok := c.derivationsByDrvPath[drvPath]; ok { 117 if output, ok := d.Outputs[name]; ok { 118 return output.Path 119 } 120 } 121 return "" 122 }