syft/syft/pkg/cataloger/golang/stdlib_package.go
Christopher Angelo Phillips f77d503892
detect license ID from full text when incidentally provided as a value (#3876)
---------
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-05-13 16:37:18 -04:00

103 lines
3.2 KiB
Go

package golang
import (
"context"
"fmt"
"strings"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
)
func stdlibProcessor(ctx context.Context, _ file.Resolver, pkgs []pkg.Package, relationships []artifact.Relationship, err error) ([]pkg.Package, []artifact.Relationship, error) {
compilerPkgs, newRelationships := stdlibPackageAndRelationships(ctx, pkgs)
return append(pkgs, compilerPkgs...), append(relationships, newRelationships...), err
}
func stdlibPackageAndRelationships(ctx context.Context, pkgs []pkg.Package) ([]pkg.Package, []artifact.Relationship) {
var goCompilerPkgs []pkg.Package
var relationships []artifact.Relationship
totalLocations := file.NewLocationSet()
for _, goPkg := range pkgs {
mValue, ok := goPkg.Metadata.(pkg.GolangBinaryBuildinfoEntry)
if !ok {
continue
}
// go binary packages should only contain a single location
for _, location := range goPkg.Locations.ToSlice() {
if totalLocations.Contains(location) {
continue
}
stdLibPkg := newGoStdLib(ctx, mValue.GoCompiledVersion, goPkg.Locations)
if stdLibPkg == nil {
continue
}
goCompilerPkgs = append(goCompilerPkgs, *stdLibPkg)
totalLocations.Add(location)
relationships = append(relationships, artifact.Relationship{
From: *stdLibPkg,
To: goPkg,
Type: artifact.DependencyOfRelationship,
})
}
}
return goCompilerPkgs, relationships
}
func newGoStdLib(ctx context.Context, version string, location file.LocationSet) *pkg.Package {
stdlibCpe, err := generateStdlibCpe(version)
if err != nil {
return nil
}
goCompilerPkg := &pkg.Package{
Name: "stdlib",
Version: version,
PURL: packageURL("stdlib", strings.TrimPrefix(version, "go")),
CPEs: []cpe.CPE{stdlibCpe},
Locations: location,
Licenses: pkg.NewLicenseSet(pkg.NewLicenseWithContext(ctx, "BSD-3-Clause")),
Language: pkg.Go,
Type: pkg.GoModulePkg,
Metadata: pkg.GolangBinaryBuildinfoEntry{
GoCompiledVersion: version,
},
}
goCompilerPkg.SetID()
return goCompilerPkg
}
func generateStdlibCpe(version string) (stdlibCpe cpe.CPE, err error) {
// GoCompiledVersion when pulled from a binary is prefixed by go
version = strings.TrimPrefix(version, "go")
// we also need to trim starting from the first +<metadata> to
// correctly extract potential rc candidate information for cpe generation
// ex: 2.0.0-rc.1+build.123 -> 2.0.0-rc.1; if no + is found then + is returned
after, _, found := strings.Cut("+", version)
if found {
version = after
}
// extracting <version> and <candidate>
// https://regex101.com/r/985GsI/1
captureGroups := internal.MatchNamedCaptureGroups(versionCandidateGroups, version)
vr, ok := captureGroups["version"]
if !ok || vr == "" {
return stdlibCpe, fmt.Errorf("could not match candidate version for: %s", version)
}
cpeString := fmt.Sprintf("cpe:2.3:a:golang:go:%s:-:*:*:*:*:*:*", captureGroups["version"])
if candidate, ok := captureGroups["candidate"]; ok && candidate != "" {
cpeString = fmt.Sprintf("cpe:2.3:a:golang:go:%s:%s:*:*:*:*:*:*", vr, candidate)
}
return cpe.New(cpeString, cpe.GeneratedSource)
}