github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/syft/pkg/cataloger/cpp/parse_conaninfo.go (about) 1 package cpp 2 3 import ( 4 "bufio" 5 "errors" 6 "fmt" 7 "io" 8 "regexp" 9 "strings" 10 11 "github.com/anchore/syft/syft/artifact" 12 "github.com/anchore/syft/syft/file" 13 "github.com/anchore/syft/syft/pkg" 14 "github.com/anchore/syft/syft/pkg/cataloger/generic" 15 ) 16 17 var _ generic.Parser = parseConaninfo 18 19 func parseConanMetadataFromFilePath(path string) (pkg.ConaninfoEntry, error) { 20 // fullFilePath = str(reader.Location.AccessPath) 21 // Split the full patch into the folders we expect. I.e.: 22 // $HOME/.conan/data/<pkg-name>/<pkg-version>/<user>/<channel>/package/<package_id>/conaninfo.txt 23 re := regexp.MustCompile(`.*[/\\](?P<name>[^/\\]+)[/\\](?P<version>[^/\\]+)[/\\](?P<user>[^/\\]+)[/\\](?P<channel>[^/\\]+)[/\\]package[/\\](?P<id>[^/\\]+)[/\\]conaninfo\.txt`) 24 matches := re.FindStringSubmatch(path) 25 if len(matches) != 6 { 26 return pkg.ConaninfoEntry{}, fmt.Errorf("failed to get parent package info from conaninfo file path") 27 } 28 mainPackageRef := fmt.Sprintf("%s/%s@%s/%s", matches[1], matches[2], matches[3], matches[4]) 29 return pkg.ConaninfoEntry{ 30 Ref: mainPackageRef, 31 PackageID: matches[5], 32 }, nil 33 } 34 35 func getRelationships(pkgs []pkg.Package, mainPackageRef pkg.Package) []artifact.Relationship { 36 var relationships []artifact.Relationship 37 for _, p := range pkgs { 38 // this is a pkg that package "main_package" depends on... make a relationship 39 relationships = append(relationships, artifact.Relationship{ 40 From: p, 41 To: mainPackageRef, 42 Type: artifact.DependencyOfRelationship, 43 }) 44 } 45 return relationships 46 } 47 48 func parseFullRequiresLine(line string, reader file.LocationReadCloser, pkgs *[]pkg.Package) { 49 if len(line) == 0 { 50 return 51 } 52 53 cref := splitConanRef(line) 54 55 meta := pkg.ConaninfoEntry{ 56 Ref: line, 57 PackageID: cref.PackageID, 58 } 59 60 p := newConaninfoPackage( 61 meta, 62 reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 63 ) 64 if p != nil { 65 *pkgs = append(*pkgs, *p) 66 } 67 } 68 69 // parseConaninfo is a parser function for conaninfo.txt contents, returning all packages discovered. 70 // The conaninfo.txt file is typically present for an installed conan package under: 71 // $HOME/.conan/data/<pkg-name>/<pkg-version>/<user>/<channel>/package/<package_id>/conaninfo.txt 72 // Based on the relative path we can get: 73 // - package name 74 // - package version 75 // - package id 76 // - user 77 // - channel 78 // The conaninfo.txt gives: 79 // - package requires (full_requires) 80 // - recipe revision (recipe_hash) 81 func parseConaninfo(_ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { 82 // First set the base package info by checking the relative path 83 fullFilePath := string(reader.Location.LocationData.Reference().RealPath) 84 if len(fullFilePath) == 0 { 85 fullFilePath = reader.Location.LocationData.RealPath 86 } 87 88 mainMetadata, err := parseConanMetadataFromFilePath(fullFilePath) 89 if err != nil { 90 return nil, nil, err 91 } 92 93 r := bufio.NewReader(reader) 94 inRequirements := false 95 inRecipeHash := false 96 var pkgs []pkg.Package 97 98 for { 99 line, err := r.ReadString('\n') 100 switch { 101 case errors.Is(io.EOF, err): 102 mainPackage := newConaninfoPackage( 103 mainMetadata, 104 reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 105 ) 106 107 mainPackageRef := *mainPackage 108 relationships := getRelationships(pkgs, mainPackageRef) 109 110 pkgs = append(pkgs, mainPackageRef) 111 112 return pkgs, relationships, nil 113 case err != nil: 114 return nil, nil, fmt.Errorf("failed to parse conaninfo.txt file: %w", err) 115 } 116 117 switch { 118 case strings.Contains(line, "[full_requires]"): 119 inRequirements = true 120 inRecipeHash = false 121 continue 122 case strings.Contains(line, "[recipe_hash]"): 123 inRequirements = false 124 inRecipeHash = true 125 continue 126 case strings.ContainsAny(line, "[]") || strings.HasPrefix(strings.TrimSpace(line), "#"): 127 inRequirements = false 128 inRecipeHash = false 129 continue 130 } 131 132 if inRequirements { 133 parseFullRequiresLine(strings.Trim(line, "\n "), reader, &pkgs) 134 } 135 if inRecipeHash { 136 // add recipe hash to the metadata ref 137 mainMetadata.Ref = mainMetadata.Ref + "#" + strings.Trim(line, "\n ") 138 inRecipeHash = false 139 } 140 } 141 }