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  }