github.com/anchore/syft@v1.38.2/syft/pkg/cataloger/golang/stdlib_package.go (about)

     1  package golang
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/anchore/syft/internal"
     9  	"github.com/anchore/syft/syft/artifact"
    10  	"github.com/anchore/syft/syft/cpe"
    11  	"github.com/anchore/syft/syft/file"
    12  	"github.com/anchore/syft/syft/pkg"
    13  )
    14  
    15  func stdlibProcessor(ctx context.Context, _ file.Resolver, pkgs []pkg.Package, relationships []artifact.Relationship, err error) ([]pkg.Package, []artifact.Relationship, error) {
    16  	compilerPkgs, newRelationships := stdlibPackageAndRelationships(ctx, pkgs)
    17  	return append(pkgs, compilerPkgs...), append(relationships, newRelationships...), err
    18  }
    19  
    20  func stdlibPackageAndRelationships(ctx context.Context, pkgs []pkg.Package) ([]pkg.Package, []artifact.Relationship) {
    21  	var goCompilerPkgs []pkg.Package
    22  	var relationships []artifact.Relationship
    23  	totalLocations := file.NewLocationSet()
    24  	for _, goPkg := range pkgs {
    25  		mValue, ok := goPkg.Metadata.(pkg.GolangBinaryBuildinfoEntry)
    26  		if !ok {
    27  			continue
    28  		}
    29  
    30  		// go binary packages should only contain a single location
    31  		for _, location := range goPkg.Locations.ToSlice() {
    32  			if totalLocations.Contains(location) {
    33  				continue
    34  			}
    35  
    36  			stdLibPkg := newGoStdLib(ctx, mValue.GoCompiledVersion, goPkg.Locations)
    37  			if stdLibPkg == nil {
    38  				continue
    39  			}
    40  			goCompilerPkgs = append(goCompilerPkgs, *stdLibPkg)
    41  			totalLocations.Add(location)
    42  
    43  			relationships = append(relationships, artifact.Relationship{
    44  				From: *stdLibPkg,
    45  				To:   goPkg,
    46  				Type: artifact.DependencyOfRelationship,
    47  			})
    48  		}
    49  	}
    50  	return goCompilerPkgs, relationships
    51  }
    52  
    53  func newGoStdLib(ctx context.Context, version string, location file.LocationSet) *pkg.Package {
    54  	stdlibCpe, err := generateStdlibCpe(version)
    55  	if err != nil {
    56  		return nil
    57  	}
    58  	goCompilerPkg := &pkg.Package{
    59  		Name:      "stdlib",
    60  		Version:   version,
    61  		PURL:      packageURL("stdlib", strings.TrimPrefix(version, "go")),
    62  		CPEs:      []cpe.CPE{stdlibCpe},
    63  		Locations: location,
    64  		Licenses:  pkg.NewLicenseSet(pkg.NewLicenseWithContext(ctx, "BSD-3-Clause")),
    65  		Language:  pkg.Go,
    66  		Type:      pkg.GoModulePkg,
    67  		Metadata: pkg.GolangBinaryBuildinfoEntry{
    68  			GoCompiledVersion: version,
    69  		},
    70  	}
    71  	goCompilerPkg.SetID()
    72  
    73  	return goCompilerPkg
    74  }
    75  
    76  func generateStdlibCpe(version string) (stdlibCpe cpe.CPE, err error) {
    77  	// GoCompiledVersion when pulled from a binary is prefixed by go
    78  	version = strings.TrimPrefix(version, "go")
    79  
    80  	// we also need to trim starting from the first +<metadata>  to
    81  	// correctly extract potential rc candidate information for cpe generation
    82  	// ex: 2.0.0-rc.1+build.123 -> 2.0.0-rc.1; if no + is found then + is returned
    83  	after, _, found := strings.Cut("+", version)
    84  	if found {
    85  		version = after
    86  	}
    87  
    88  	// extracting <version> and <candidate>
    89  	// https://regex101.com/r/985GsI/1
    90  	captureGroups := internal.MatchNamedCaptureGroups(versionCandidateGroups, version)
    91  	vr, ok := captureGroups["version"]
    92  	if !ok || vr == "" {
    93  		return stdlibCpe, fmt.Errorf("could not match candidate version for: %s", version)
    94  	}
    95  
    96  	cpeString := fmt.Sprintf("cpe:2.3:a:golang:go:%s:-:*:*:*:*:*:*", captureGroups["version"])
    97  	if candidate, ok := captureGroups["candidate"]; ok && candidate != "" {
    98  		cpeString = fmt.Sprintf("cpe:2.3:a:golang:go:%s:%s:*:*:*:*:*:*", vr, candidate)
    99  	}
   100  
   101  	return cpe.New(cpeString, cpe.GeneratedSource)
   102  }