github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/syft/pkg/cataloger/golang/cataloger.go (about) 1 /* 2 Package golang provides a concrete Cataloger implementation relating to packages within the Go language ecosystem. 3 */ 4 package golang 5 6 import ( 7 "fmt" 8 "regexp" 9 "strings" 10 11 "github.com/anchore/syft/syft/artifact" 12 "github.com/anchore/syft/syft/cpe" 13 "github.com/anchore/syft/syft/file" 14 "github.com/anchore/syft/syft/pkg" 15 "github.com/anchore/syft/syft/pkg/cataloger/generic" 16 "github.com/lineaje-labs/syft/internal" 17 ) 18 19 var versionCandidateGroups = regexp.MustCompile(`(?P<version>\d+(\.\d+)?(\.\d+)?)(?P<candidate>\w*)`) 20 21 // NewGoModuleFileCataloger returns a new cataloger object that searches within go.mod files. 22 func NewGoModuleFileCataloger(opts CatalogerConfig) pkg.Cataloger { 23 c := goModCataloger{ 24 licenses: newGoLicenses(opts), 25 } 26 return &progressingCataloger{ 27 cataloger: generic.NewCataloger("go-module-file-cataloger"). 28 WithParserByGlobs(c.parseGoModFile, "**/go.mod"), 29 } 30 } 31 32 // NewGoModuleBinaryCataloger returns a new cataloger object that searches within binaries built by the go compiler. 33 func NewGoModuleBinaryCataloger(opts CatalogerConfig) pkg.Cataloger { 34 c := goBinaryCataloger{ 35 licenses: newGoLicenses(opts), 36 } 37 return &progressingCataloger{ 38 cataloger: generic.NewCataloger("go-module-binary-cataloger"). 39 WithParserByMimeTypes(c.parseGoBinary, internal.ExecutableMIMETypeSet.List()...), 40 } 41 } 42 43 type progressingCataloger struct { 44 cataloger *generic.Cataloger 45 } 46 47 func (p *progressingCataloger) Name() string { 48 return p.cataloger.Name() 49 } 50 51 func (p *progressingCataloger) Catalog(resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) { 52 pkgs, relationships, err := p.cataloger.Catalog(resolver) 53 goCompilerPkgs := []pkg.Package{} 54 totalLocations := file.NewLocationSet() 55 for _, goPkg := range pkgs { 56 mValue, ok := goPkg.Metadata.(pkg.GolangBinaryBuildinfoEntry) 57 if !ok { 58 continue 59 } 60 // go binary packages should only contain a single location 61 for _, location := range goPkg.Locations.ToSlice() { 62 if !totalLocations.Contains(location) { 63 stdLibPkg := newGoStdLib(mValue.GoCompiledVersion, goPkg.Locations) 64 if stdLibPkg != nil { 65 goCompilerPkgs = append(goCompilerPkgs, *stdLibPkg) 66 totalLocations.Add(location) 67 } 68 } 69 } 70 } 71 pkgs = append(pkgs, goCompilerPkgs...) 72 return pkgs, relationships, err 73 } 74 75 func newGoStdLib(version string, location file.LocationSet) *pkg.Package { 76 stdlibCpe, err := generateStdlibCpe(version) 77 if err != nil { 78 return nil 79 } 80 goCompilerPkg := &pkg.Package{ 81 Name: "stdlib", 82 Version: version, 83 PURL: packageURL("stdlib", strings.TrimPrefix(version, "go")), 84 CPEs: []cpe.CPE{stdlibCpe}, 85 Locations: location, 86 Licenses: pkg.NewLicenseSet(pkg.NewLicense("BSD-3-Clause")), 87 Language: pkg.Go, 88 Type: pkg.GoModulePkg, 89 Metadata: pkg.GolangBinaryBuildinfoEntry{ 90 GoCompiledVersion: version, 91 }, 92 } 93 goCompilerPkg.SetID() 94 95 return goCompilerPkg 96 } 97 98 func generateStdlibCpe(version string) (stdlibCpe cpe.CPE, err error) { 99 // GoCompiledVersion when pulled from a binary is prefixed by go 100 version = strings.TrimPrefix(version, "go") 101 102 // we also need to trim starting from the first +<metadata> to 103 // correctly extract potential rc candidate information for cpe generation 104 // ex: 2.0.0-rc.1+build.123 -> 2.0.0-rc.1; if no + is found then + is returned 105 after, _, found := strings.Cut("+", version) 106 if found { 107 version = after 108 } 109 110 // extracting <version> and <candidate> 111 // https://regex101.com/r/985GsI/1 112 captureGroups := internal.MatchNamedCaptureGroups(versionCandidateGroups, version) 113 vr, ok := captureGroups["version"] 114 if !ok || vr == "" { 115 return stdlibCpe, fmt.Errorf("could not match candidate version for: %s", version) 116 } 117 118 cpeString := fmt.Sprintf("cpe:2.3:a:golang:go:%s:-:*:*:*:*:*:*", captureGroups["version"]) 119 if candidate, ok := captureGroups["candidate"]; ok && candidate != "" { 120 cpeString = fmt.Sprintf("cpe:2.3:a:golang:go:%s:%s:*:*:*:*:*:*", vr, candidate) 121 } 122 123 return cpe.New(cpeString) 124 }