github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/pkg/cataloger/cpp/package.go (about)

     1  package cpp
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/anchore/packageurl-go"
     7  	"github.com/anchore/syft/syft/file"
     8  	"github.com/anchore/syft/syft/pkg"
     9  )
    10  
    11  type conanRef struct {
    12  	Name      string
    13  	Version   string
    14  	User      string
    15  	Channel   string
    16  	Revision  string
    17  	PackageID string
    18  	Timestamp string
    19  }
    20  
    21  func splitConanRef(ref string) *conanRef {
    22  	// ConanfileEntry ref format is:
    23  	// pkg/0.1@user/channel#rrev%timestamp
    24  	// This method is based on conan's ref.loads method:
    25  	// https://github.com/conan-io/conan/blob/release/2.0/conans/model/recipe_ref.py#L93C21-L93C21
    26  
    27  	var cref conanRef
    28  
    29  	// timestamp
    30  	tokens := strings.Split(ref, "%")
    31  	text := tokens[0]
    32  	if len(tokens) == 2 {
    33  		cref.Timestamp = tokens[1]
    34  	}
    35  
    36  	// package_id
    37  	tokens = strings.Split(text, ":")
    38  	text = tokens[0]
    39  	if len(tokens) == 2 {
    40  		cref.PackageID = tokens[1]
    41  	}
    42  
    43  	// revision
    44  	tokens = strings.Split(text, "#")
    45  	ref = tokens[0]
    46  	if len(tokens) == 2 {
    47  		cref.Revision = tokens[1]
    48  	}
    49  
    50  	// name and version are always given
    51  	tokens = strings.Split(ref, "@")
    52  	nameAndVersion := strings.Split(tokens[0], "/")
    53  	if len(nameAndVersion) < 2 || nameAndVersion[0] == "" || nameAndVersion[1] == "" {
    54  		return nil
    55  	}
    56  	cref.Name = nameAndVersion[0]
    57  	cref.Version = nameAndVersion[1]
    58  	// user and channel
    59  	if len(tokens) == 2 && tokens[1] != "" {
    60  		tokens = strings.Split(tokens[1], "/")
    61  		if len(tokens) == 2 {
    62  			cref.User = tokens[0]
    63  			cref.Channel = tokens[1]
    64  		}
    65  	}
    66  	return &cref
    67  }
    68  
    69  func newConanfilePackage(m pkg.ConanfileEntry, locations ...file.Location) *pkg.Package {
    70  	return newConanPackage(m.Ref, m, locations...)
    71  }
    72  
    73  func newConanlockPackage(m pkg.ConanV1LockEntry, locations ...file.Location) *pkg.Package {
    74  	return newConanPackage(m.Ref, m, locations...)
    75  }
    76  
    77  func newConanReferencePackage(m pkg.ConanV2LockEntry, locations ...file.Location) *pkg.Package {
    78  	return newConanPackage(m.Ref, m, locations...)
    79  }
    80  
    81  func newConaninfoPackage(m pkg.ConaninfoEntry, locations ...file.Location) *pkg.Package {
    82  	return newConanPackage(m.Ref, m, locations...)
    83  }
    84  
    85  func newConanPackage(refStr string, metadata any, locations ...file.Location) *pkg.Package {
    86  	ref := splitConanRef(refStr)
    87  	if ref == nil {
    88  		return nil
    89  	}
    90  
    91  	p := pkg.Package{
    92  		Name:      ref.Name,
    93  		Version:   ref.Version,
    94  		Locations: file.NewLocationSet(locations...),
    95  		PURL:      packageURL(ref),
    96  		Language:  pkg.CPP,
    97  		Type:      pkg.ConanPkg,
    98  		Metadata:  metadata,
    99  	}
   100  
   101  	p.SetID()
   102  
   103  	return &p
   104  }
   105  
   106  func packageURL(ref *conanRef) string {
   107  	qualifiers := packageurl.Qualifiers{}
   108  	if ref.Channel != "" {
   109  		qualifiers = append(qualifiers, packageurl.Qualifier{
   110  			Key:   "channel",
   111  			Value: ref.Channel,
   112  		})
   113  	}
   114  	return packageurl.NewPackageURL(
   115  		packageurl.TypeConan,
   116  		ref.User,
   117  		ref.Name,
   118  		ref.Version,
   119  		qualifiers,
   120  		"",
   121  	).ToString()
   122  }