github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/syft/pkg/cataloger/cpp/parse_conanlock.go (about)

     1  package cpp
     2  
     3  import (
     4  	"encoding/json"
     5  	"strings"
     6  
     7  	"github.com/anchore/syft/syft/artifact"
     8  	"github.com/anchore/syft/syft/file"
     9  	"github.com/anchore/syft/syft/pkg"
    10  	"github.com/anchore/syft/syft/pkg/cataloger/generic"
    11  )
    12  
    13  var _ generic.Parser = parseConanlock
    14  
    15  type conanLock struct {
    16  	GraphLock struct {
    17  		Nodes map[string]struct {
    18  			Ref            string   `json:"ref"`
    19  			PackageID      string   `json:"package_id"`
    20  			Context        string   `json:"context"`
    21  			Prev           string   `json:"prev"`
    22  			Requires       []string `json:"requires"`
    23  			PythonRequires string   `json:"py_requires"`
    24  			Options        string   `json:"options"`
    25  			Path           string   `json:"path"`
    26  		} `json:"nodes"`
    27  	} `json:"graph_lock"`
    28  	Version     string `json:"version"`
    29  	ProfileHost string `json:"profile_host"`
    30  }
    31  
    32  // parseConanlock is a parser function for conan.lock contents, returning all packages discovered.
    33  func parseConanlock(_ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
    34  	var pkgs []pkg.Package
    35  	var cl conanLock
    36  	if err := json.NewDecoder(reader).Decode(&cl); err != nil {
    37  		return nil, nil, err
    38  	}
    39  
    40  	// requires is a list of package indices. We first need to fill it, and then we can resolve the package
    41  	// in a second iteration
    42  	var indexToPkgMap = map[string]pkg.Package{}
    43  
    44  	// we do not want to store the index list requires in the conan metadata, because it is not useful to have it in
    45  	// the SBOM. Instead, we will store it in a map and then use it to build the relationships
    46  	// maps pkg.ID to a list of indices
    47  	var parsedPkgRequires = map[artifact.ID][]string{}
    48  
    49  	for idx, node := range cl.GraphLock.Nodes {
    50  		metadata := pkg.ConanLockEntry{
    51  			Ref:       node.Ref,
    52  			Options:   parseOptions(node.Options),
    53  			Path:      node.Path,
    54  			Context:   node.Context,
    55  			PackageID: node.PackageID,
    56  			Prev:      node.Prev,
    57  		}
    58  
    59  		p := newConanlockPackage(
    60  			metadata,
    61  			reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
    62  		)
    63  
    64  		if p != nil {
    65  			pk := *p
    66  			pkgs = append(pkgs, pk)
    67  			parsedPkgRequires[pk.ID()] = node.Requires
    68  			indexToPkgMap[idx] = pk
    69  		}
    70  	}
    71  
    72  	var relationships []artifact.Relationship
    73  
    74  	for _, p := range pkgs {
    75  		requires := parsedPkgRequires[p.ID()]
    76  		for _, r := range requires {
    77  			// this is a pkg that package "p" depends on... make a relationship
    78  			relationships = append(relationships, artifact.Relationship{
    79  				From: indexToPkgMap[r],
    80  				To:   p,
    81  				Type: artifact.DependencyOfRelationship,
    82  			})
    83  		}
    84  	}
    85  
    86  	return pkgs, relationships, nil
    87  }
    88  
    89  func parseOptions(options string) map[string]string {
    90  	o := make(map[string]string)
    91  	if len(options) == 0 {
    92  		return nil
    93  	}
    94  
    95  	kvps := strings.Split(options, "\n")
    96  	for _, kvp := range kvps {
    97  		kv := strings.Split(kvp, "=")
    98  		if len(kv) == 2 {
    99  			o[kv[0]] = kv[1]
   100  		}
   101  	}
   102  
   103  	return o
   104  }