mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +01:00
port golang cataloger to new generic cataloger pattern (#1289)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com> Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
parent
52cb7269bf
commit
6826d7603b
@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
Package golang provides a concrete Cataloger implementation for go.mod files.
|
|
||||||
*/
|
|
||||||
package golang
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/internal"
|
|
||||||
"github.com/anchore/syft/internal/log"
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/unionreader"
|
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
)
|
|
||||||
|
|
||||||
const catalogerName = "go-module-binary-cataloger"
|
|
||||||
|
|
||||||
type Cataloger struct{}
|
|
||||||
|
|
||||||
// NewGoModuleBinaryCataloger returns a new Golang cataloger object.
|
|
||||||
func NewGoModuleBinaryCataloger() *Cataloger {
|
|
||||||
return &Cataloger{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns a string that uniquely describes a cataloger
|
|
||||||
func (c *Cataloger) Name() string {
|
|
||||||
return catalogerName
|
|
||||||
}
|
|
||||||
|
|
||||||
// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing rpm db installation.
|
|
||||||
func (c *Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) {
|
|
||||||
var pkgs []pkg.Package
|
|
||||||
|
|
||||||
fileMatches, err := resolver.FilesByMIMEType(internal.ExecutableMIMETypeSet.List()...)
|
|
||||||
if err != nil {
|
|
||||||
return pkgs, nil, fmt.Errorf("failed to find bin by mime types: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, location := range fileMatches {
|
|
||||||
readerCloser, err := resolver.FileContentsByLocation(location)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("golang cataloger: opening file: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
reader, err := unionreader.GetUnionReader(readerCloser)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
mods, archs := scanFile(reader, location.RealPath)
|
|
||||||
internal.CloseAndLogError(readerCloser, location.RealPath)
|
|
||||||
|
|
||||||
for i, mod := range mods {
|
|
||||||
pkgs = append(pkgs, buildGoPkgInfo(location, mod, archs[i])...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pkgs, nil, nil
|
|
||||||
}
|
|
||||||
21
syft/pkg/cataloger/golang/cataloger.go
Normal file
21
syft/pkg/cataloger/golang/cataloger.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
Package golang provides a concrete Cataloger implementation for go.mod files.
|
||||||
|
*/
|
||||||
|
package golang
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/anchore/syft/internal"
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewGoModFileCataloger returns a new Go module cataloger object.
|
||||||
|
func NewGoModFileCataloger() *generic.Cataloger {
|
||||||
|
return generic.NewCataloger("go-mod-file-cataloger").
|
||||||
|
WithParserByGlobs(parseGoModFile, "**/go.mod")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGoModuleBinaryCataloger returns a new Golang cataloger object.
|
||||||
|
func NewGoModuleBinaryCataloger() *generic.Cataloger {
|
||||||
|
return generic.NewCataloger("go-module-binary-cataloger").
|
||||||
|
WithParserByMimeTypes(parseGoBinary, internal.ExecutableMIMETypeSet.List()...)
|
||||||
|
}
|
||||||
@ -1,17 +0,0 @@
|
|||||||
/*
|
|
||||||
Package golang provides a concrete Cataloger implementation for go.mod files.
|
|
||||||
*/
|
|
||||||
package golang
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewGoModFileCataloger returns a new Go module cataloger object.
|
|
||||||
func NewGoModFileCataloger() *common.GenericCataloger {
|
|
||||||
globParsers := map[string]common.ParserFn{
|
|
||||||
"**/go.mod": parseGoMod,
|
|
||||||
}
|
|
||||||
|
|
||||||
return common.NewGenericCataloger(nil, globParsers, "go-mod-file-cataloger")
|
|
||||||
}
|
|
||||||
69
syft/pkg/cataloger/golang/package.go
Normal file
69
syft/pkg/cataloger/golang/package.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package golang
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"runtime/debug"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/anchore/packageurl-go"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newGoBinaryPackage(dep *debug.Module, mainModule, goVersion, architecture string, buildSettings map[string]string, locations ...source.Location) pkg.Package {
|
||||||
|
if dep.Replace != nil {
|
||||||
|
dep = dep.Replace
|
||||||
|
}
|
||||||
|
|
||||||
|
p := pkg.Package{
|
||||||
|
Name: dep.Path,
|
||||||
|
Version: dep.Version,
|
||||||
|
PURL: packageURL(dep.Path, dep.Version),
|
||||||
|
Language: pkg.Go,
|
||||||
|
Type: pkg.GoModulePkg,
|
||||||
|
Locations: source.NewLocationSet(locations...),
|
||||||
|
MetadataType: pkg.GolangBinMetadataType,
|
||||||
|
Metadata: pkg.GolangBinMetadata{
|
||||||
|
GoCompiledVersion: goVersion,
|
||||||
|
H1Digest: dep.Sum,
|
||||||
|
Architecture: architecture,
|
||||||
|
BuildSettings: buildSettings,
|
||||||
|
MainModule: mainModule,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
p.SetID()
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func packageURL(moduleName, moduleVersion string) string {
|
||||||
|
// source: https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst#golang
|
||||||
|
// note: "The version is often empty when a commit is not specified and should be the commit in most cases when available."
|
||||||
|
|
||||||
|
re := regexp.MustCompile(`(/)[^/]*$`)
|
||||||
|
fields := re.Split(moduleName, -1)
|
||||||
|
if len(fields) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
namespace := fields[0]
|
||||||
|
name := strings.TrimPrefix(strings.TrimPrefix(moduleName, namespace), "/")
|
||||||
|
|
||||||
|
if name == "" {
|
||||||
|
// this is a "short" url (with no namespace)
|
||||||
|
name = namespace
|
||||||
|
namespace = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// The subpath is used to point to a subpath inside a package (e.g. pkg:golang/google.golang.org/genproto#googleapis/api/annotations)
|
||||||
|
subpath := "" // TODO: not implemented
|
||||||
|
|
||||||
|
return packageurl.NewPackageURL(
|
||||||
|
packageurl.TypeGolang,
|
||||||
|
namespace,
|
||||||
|
name,
|
||||||
|
moduleVersion,
|
||||||
|
nil,
|
||||||
|
subpath,
|
||||||
|
).ToString()
|
||||||
|
}
|
||||||
41
syft/pkg/cataloger/golang/package_test.go
Normal file
41
syft/pkg/cataloger/golang/package_test.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package golang
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_packageURL(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
pkg pkg.Package
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "gocase",
|
||||||
|
pkg: pkg.Package{
|
||||||
|
Name: "github.com/anchore/syft",
|
||||||
|
Version: "v0.1.0",
|
||||||
|
},
|
||||||
|
expected: "pkg:golang/github.com/anchore/syft@v0.1.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "golang short name",
|
||||||
|
pkg: pkg.Package{
|
||||||
|
Name: "go.opencensus.io",
|
||||||
|
Version: "v0.23.0",
|
||||||
|
},
|
||||||
|
expected: "pkg:golang/go.opencensus.io@v0.23.0",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, test.expected, packageURL(test.pkg.Name, test.pkg.Version))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -14,9 +14,13 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/mod/module"
|
"golang.org/x/mod/module"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal"
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/golang/internal/xcoff"
|
"github.com/anchore/syft/syft/pkg/cataloger/golang/internal/xcoff"
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/internal/unionreader"
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -34,9 +38,27 @@ var (
|
|||||||
|
|
||||||
const devel = "(devel)"
|
const devel = "(devel)"
|
||||||
|
|
||||||
|
// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing rpm db installation.
|
||||||
|
func parseGoBinary(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
|
var pkgs []pkg.Package
|
||||||
|
|
||||||
|
unionReader, err := unionreader.GetUnionReader(reader.ReadCloser)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mods, archs := scanFile(unionReader, reader.RealPath)
|
||||||
|
internal.CloseAndLogError(reader.ReadCloser, reader.RealPath)
|
||||||
|
|
||||||
|
for i, mod := range mods {
|
||||||
|
pkgs = append(pkgs, buildGoPkgInfo(reader.Location, mod, archs[i])...)
|
||||||
|
}
|
||||||
|
return pkgs, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func makeGoMainPackage(mod *debug.BuildInfo, arch string, location source.Location) pkg.Package {
|
func makeGoMainPackage(mod *debug.BuildInfo, arch string, location source.Location) pkg.Package {
|
||||||
gbs := getBuildSettings(mod.Settings)
|
gbs := getBuildSettings(mod.Settings)
|
||||||
main := newGoBinaryPackage(&mod.Main, mod.Main.Path, mod.GoVersion, arch, location, gbs)
|
main := newGoBinaryPackage(&mod.Main, mod.Main.Path, mod.GoVersion, arch, gbs, location)
|
||||||
if main.Version == devel {
|
if main.Version == devel {
|
||||||
if version, ok := gbs["vcs.revision"]; ok {
|
if version, ok := gbs["vcs.revision"]; ok {
|
||||||
if timestamp, ok := gbs["vcs.time"]; ok {
|
if timestamp, ok := gbs["vcs.time"]; ok {
|
||||||
@ -50,39 +72,14 @@ func makeGoMainPackage(mod *debug.BuildInfo, arch string, location source.Locati
|
|||||||
version = module.PseudoVersion("", "", ts, version)
|
version = module.PseudoVersion("", "", ts, version)
|
||||||
}
|
}
|
||||||
main.Version = version
|
main.Version = version
|
||||||
|
main.PURL = packageURL(main.Name, main.Version)
|
||||||
|
main.SetID()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return main
|
return main
|
||||||
}
|
}
|
||||||
|
|
||||||
func newGoBinaryPackage(dep *debug.Module, mainModule, goVersion, architecture string, location source.Location, buildSettings map[string]string) pkg.Package {
|
|
||||||
if dep.Replace != nil {
|
|
||||||
dep = dep.Replace
|
|
||||||
}
|
|
||||||
|
|
||||||
p := pkg.Package{
|
|
||||||
FoundBy: catalogerName,
|
|
||||||
Name: dep.Path,
|
|
||||||
Version: dep.Version,
|
|
||||||
Language: pkg.Go,
|
|
||||||
Type: pkg.GoModulePkg,
|
|
||||||
Locations: source.NewLocationSet(location),
|
|
||||||
MetadataType: pkg.GolangBinMetadataType,
|
|
||||||
Metadata: pkg.GolangBinMetadata{
|
|
||||||
GoCompiledVersion: goVersion,
|
|
||||||
H1Digest: dep.Sum,
|
|
||||||
Architecture: architecture,
|
|
||||||
BuildSettings: buildSettings,
|
|
||||||
MainModule: mainModule,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
p.SetID()
|
|
||||||
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// getArchs finds a binary architecture by two ways:
|
// getArchs finds a binary architecture by two ways:
|
||||||
// 1) reading build info from binaries compiled by go1.18+
|
// 1) reading build info from binaries compiled by go1.18+
|
||||||
// 2) reading file headers from binaries compiled by < go1.18
|
// 2) reading file headers from binaries compiled by < go1.18
|
||||||
@ -192,7 +189,7 @@ func buildGoPkgInfo(location source.Location, mod *debug.BuildInfo, arch string)
|
|||||||
if dep == nil {
|
if dep == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
p := newGoBinaryPackage(dep, mod.Main.Path, mod.GoVersion, arch, location, nil)
|
p := newGoBinaryPackage(dep, mod.Main.Path, mod.GoVersion, arch, nil, location)
|
||||||
if pkg.IsValid(&p) {
|
if pkg.IsValid(&p) {
|
||||||
pkgs = append(pkgs, p)
|
pkgs = append(pkgs, p)
|
||||||
}
|
}
|
||||||
@ -123,18 +123,18 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
goCompiledVersion = "1.18"
|
goCompiledVersion = "1.18"
|
||||||
archDetails = "amd64"
|
archDetails = "amd64"
|
||||||
)
|
)
|
||||||
buildSettings := map[string]string{
|
defaultBuildSettings := map[string]string{
|
||||||
"GOARCH": "amd64",
|
"GOARCH": "amd64",
|
||||||
"GOOS": "darwin",
|
"GOOS": "darwin",
|
||||||
"GOAMD64": "v1",
|
"GOAMD64": "v1",
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedMain := pkg.Package{
|
unmodifiedMain := pkg.Package{
|
||||||
Name: "github.com/anchore/syft",
|
Name: "github.com/anchore/syft",
|
||||||
FoundBy: catalogerName,
|
|
||||||
Language: pkg.Go,
|
Language: pkg.Go,
|
||||||
Type: pkg.GoModulePkg,
|
Type: pkg.GoModulePkg,
|
||||||
Version: "(devel)",
|
Version: "(devel)",
|
||||||
|
PURL: "pkg:golang/github.com/anchore/syft@(devel)",
|
||||||
Locations: source.NewLocationSet(
|
Locations: source.NewLocationSet(
|
||||||
source.Location{
|
source.Location{
|
||||||
Coordinates: source.Coordinates{
|
Coordinates: source.Coordinates{
|
||||||
@ -147,7 +147,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
Metadata: pkg.GolangBinMetadata{
|
Metadata: pkg.GolangBinMetadata{
|
||||||
GoCompiledVersion: goCompiledVersion,
|
GoCompiledVersion: goCompiledVersion,
|
||||||
Architecture: archDetails,
|
Architecture: archDetails,
|
||||||
BuildSettings: buildSettings,
|
BuildSettings: defaultBuildSettings,
|
||||||
MainModule: "github.com/anchore/syft",
|
MainModule: "github.com/anchore/syft",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -159,7 +159,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
expected []pkg.Package
|
expected []pkg.Package
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "buildGoPkgInfo parses a nil mod",
|
name: "parse an empty mod",
|
||||||
mod: nil,
|
mod: nil,
|
||||||
expected: []pkg.Package(nil),
|
expected: []pkg.Package(nil),
|
||||||
},
|
},
|
||||||
@ -179,7 +179,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
expected: []pkg.Package{
|
expected: []pkg.Package{
|
||||||
{
|
{
|
||||||
Name: "github.com/adrg/xdg",
|
Name: "github.com/adrg/xdg",
|
||||||
FoundBy: catalogerName,
|
PURL: "pkg:golang/github.com/adrg/xdg",
|
||||||
Language: pkg.Go,
|
Language: pkg.Go,
|
||||||
Type: pkg.GoModulePkg,
|
Type: pkg.GoModulePkg,
|
||||||
Locations: source.NewLocationSet(
|
Locations: source.NewLocationSet(
|
||||||
@ -201,7 +201,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
expected: []pkg.Package(nil),
|
expected: []pkg.Package(nil),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "buildGoPkgInfo parses a mod without main module",
|
name: "parse a mod without main module",
|
||||||
arch: archDetails,
|
arch: archDetails,
|
||||||
mod: &debug.BuildInfo{
|
mod: &debug.BuildInfo{
|
||||||
GoVersion: goCompiledVersion,
|
GoVersion: goCompiledVersion,
|
||||||
@ -221,8 +221,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
expected: []pkg.Package{
|
expected: []pkg.Package{
|
||||||
{
|
{
|
||||||
Name: "github.com/adrg/xdg",
|
Name: "github.com/adrg/xdg",
|
||||||
FoundBy: catalogerName,
|
|
||||||
Version: "v0.2.1",
|
Version: "v0.2.1",
|
||||||
|
PURL: "pkg:golang/github.com/adrg/xdg@v0.2.1",
|
||||||
Language: pkg.Go,
|
Language: pkg.Go,
|
||||||
Type: pkg.GoModulePkg,
|
Type: pkg.GoModulePkg,
|
||||||
Locations: source.NewLocationSet(
|
Locations: source.NewLocationSet(
|
||||||
@ -243,7 +243,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "buildGoPkgInfo parses a mod without packages",
|
name: "parse a mod without packages",
|
||||||
arch: archDetails,
|
arch: archDetails,
|
||||||
mod: &debug.BuildInfo{
|
mod: &debug.BuildInfo{
|
||||||
GoVersion: goCompiledVersion,
|
GoVersion: goCompiledVersion,
|
||||||
@ -254,10 +254,55 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
{Key: "GOAMD64", Value: "v1"},
|
{Key: "GOAMD64", Value: "v1"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: []pkg.Package{expectedMain},
|
expected: []pkg.Package{unmodifiedMain},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "buildGoPkgInfo parses a populated mod string and returns packages but no source info",
|
name: "parse main mod and replace devel version",
|
||||||
|
arch: archDetails,
|
||||||
|
mod: &debug.BuildInfo{
|
||||||
|
GoVersion: goCompiledVersion,
|
||||||
|
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
|
||||||
|
Settings: []debug.BuildSetting{
|
||||||
|
{Key: "GOARCH", Value: archDetails},
|
||||||
|
{Key: "GOOS", Value: "darwin"},
|
||||||
|
{Key: "GOAMD64", Value: "v1"},
|
||||||
|
{Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"},
|
||||||
|
{Key: "vcs.time", Value: "2022-10-14T19:54:57Z"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []pkg.Package{
|
||||||
|
{
|
||||||
|
Name: "github.com/anchore/syft",
|
||||||
|
Language: pkg.Go,
|
||||||
|
Type: pkg.GoModulePkg,
|
||||||
|
Version: "v0.0.0-20221014195457-41bc6bb41035",
|
||||||
|
PURL: "pkg:golang/github.com/anchore/syft@v0.0.0-20221014195457-41bc6bb41035",
|
||||||
|
Locations: source.NewLocationSet(
|
||||||
|
source.Location{
|
||||||
|
Coordinates: source.Coordinates{
|
||||||
|
RealPath: "/a-path",
|
||||||
|
FileSystemID: "layer-id",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
MetadataType: pkg.GolangBinMetadataType,
|
||||||
|
Metadata: pkg.GolangBinMetadata{
|
||||||
|
GoCompiledVersion: goCompiledVersion,
|
||||||
|
Architecture: archDetails,
|
||||||
|
BuildSettings: map[string]string{
|
||||||
|
"GOARCH": archDetails,
|
||||||
|
"GOOS": "darwin",
|
||||||
|
"GOAMD64": "v1",
|
||||||
|
"vcs.revision": "41bc6bb410352845f22766e27dd48ba93aa825a4",
|
||||||
|
"vcs.time": "2022-10-14T19:54:57Z",
|
||||||
|
},
|
||||||
|
MainModule: "github.com/anchore/syft",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "parse a populated mod string and returns packages but no source info",
|
||||||
arch: archDetails,
|
arch: archDetails,
|
||||||
mod: &debug.BuildInfo{
|
mod: &debug.BuildInfo{
|
||||||
GoVersion: goCompiledVersion,
|
GoVersion: goCompiledVersion,
|
||||||
@ -283,8 +328,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
expected: []pkg.Package{
|
expected: []pkg.Package{
|
||||||
{
|
{
|
||||||
Name: "github.com/adrg/xdg",
|
Name: "github.com/adrg/xdg",
|
||||||
FoundBy: catalogerName,
|
|
||||||
Version: "v0.2.1",
|
Version: "v0.2.1",
|
||||||
|
PURL: "pkg:golang/github.com/adrg/xdg@v0.2.1",
|
||||||
Language: pkg.Go,
|
Language: pkg.Go,
|
||||||
Type: pkg.GoModulePkg,
|
Type: pkg.GoModulePkg,
|
||||||
Locations: source.NewLocationSet(
|
Locations: source.NewLocationSet(
|
||||||
@ -305,8 +350,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "github.com/anchore/client-go",
|
Name: "github.com/anchore/client-go",
|
||||||
FoundBy: catalogerName,
|
|
||||||
Version: "v0.0.0-20210222170800-9c70f9b80bcf",
|
Version: "v0.0.0-20210222170800-9c70f9b80bcf",
|
||||||
|
PURL: "pkg:golang/github.com/anchore/client-go@v0.0.0-20210222170800-9c70f9b80bcf",
|
||||||
Language: pkg.Go,
|
Language: pkg.Go,
|
||||||
Type: pkg.GoModulePkg,
|
Type: pkg.GoModulePkg,
|
||||||
Locations: source.NewLocationSet(
|
Locations: source.NewLocationSet(
|
||||||
@ -325,11 +370,11 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
MainModule: "github.com/anchore/syft",
|
MainModule: "github.com/anchore/syft",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedMain,
|
unmodifiedMain,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "buildGoPkgInfo parses a populated mod string and returns packages when a replace directive exists",
|
name: "parse a populated mod string and returns packages when a replace directive exists",
|
||||||
arch: archDetails,
|
arch: archDetails,
|
||||||
mod: &debug.BuildInfo{
|
mod: &debug.BuildInfo{
|
||||||
GoVersion: goCompiledVersion,
|
GoVersion: goCompiledVersion,
|
||||||
@ -360,8 +405,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
expected: []pkg.Package{
|
expected: []pkg.Package{
|
||||||
{
|
{
|
||||||
Name: "golang.org/x/sys",
|
Name: "golang.org/x/sys",
|
||||||
FoundBy: catalogerName,
|
|
||||||
Version: "v0.0.0-20211006194710-c8a6f5223071",
|
Version: "v0.0.0-20211006194710-c8a6f5223071",
|
||||||
|
PURL: "pkg:golang/golang.org/x/sys@v0.0.0-20211006194710-c8a6f5223071",
|
||||||
Language: pkg.Go,
|
Language: pkg.Go,
|
||||||
Type: pkg.GoModulePkg,
|
Type: pkg.GoModulePkg,
|
||||||
Locations: source.NewLocationSet(
|
Locations: source.NewLocationSet(
|
||||||
@ -381,8 +426,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
}},
|
}},
|
||||||
{
|
{
|
||||||
Name: "golang.org/x/term",
|
Name: "golang.org/x/term",
|
||||||
FoundBy: catalogerName,
|
|
||||||
Version: "v0.0.0-20210916214954-140adaaadfaf",
|
Version: "v0.0.0-20210916214954-140adaaadfaf",
|
||||||
|
PURL: "pkg:golang/golang.org/x/term@v0.0.0-20210916214954-140adaaadfaf",
|
||||||
Language: pkg.Go,
|
Language: pkg.Go,
|
||||||
Type: pkg.GoModulePkg,
|
Type: pkg.GoModulePkg,
|
||||||
Locations: source.NewLocationSet(
|
Locations: source.NewLocationSet(
|
||||||
@ -401,7 +446,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||||||
MainModule: "github.com/anchore/syft",
|
MainModule: "github.com/anchore/syft",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedMain,
|
unmodifiedMain,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -9,38 +9,44 @@ import (
|
|||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
// parseGoMod takes a go.mod and lists all packages discovered.
|
// parseGoModFile takes a go.mod and lists all packages discovered.
|
||||||
func parseGoMod(path string, reader io.Reader) ([]*pkg.Package, []artifact.Relationship, error) {
|
func parseGoModFile(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
packages := make(map[string]*pkg.Package)
|
packages := make(map[string]pkg.Package)
|
||||||
|
|
||||||
contents, err := io.ReadAll(reader)
|
contents, err := io.ReadAll(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to read go module: %w", err)
|
return nil, nil, fmt.Errorf("failed to read go module: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := modfile.Parse(path, contents, nil)
|
file, err := modfile.Parse(reader.RealPath, contents, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to parse go module: %w", err)
|
return nil, nil, fmt.Errorf("failed to parse go module: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, m := range file.Require {
|
for _, m := range file.Require {
|
||||||
packages[m.Mod.Path] = &pkg.Package{
|
packages[m.Mod.Path] = pkg.Package{
|
||||||
Name: m.Mod.Path,
|
Name: m.Mod.Path,
|
||||||
Version: m.Mod.Version,
|
Version: m.Mod.Version,
|
||||||
Language: pkg.Go,
|
Locations: source.NewLocationSet(reader.Location),
|
||||||
Type: pkg.GoModulePkg,
|
PURL: packageURL(m.Mod.Path, m.Mod.Version),
|
||||||
|
Language: pkg.Go,
|
||||||
|
Type: pkg.GoModulePkg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove any old packages and replace with new ones...
|
// remove any old packages and replace with new ones...
|
||||||
for _, m := range file.Replace {
|
for _, m := range file.Replace {
|
||||||
packages[m.New.Path] = &pkg.Package{
|
packages[m.New.Path] = pkg.Package{
|
||||||
Name: m.New.Path,
|
Name: m.New.Path,
|
||||||
Version: m.New.Version,
|
Version: m.New.Version,
|
||||||
Language: pkg.Go,
|
Locations: source.NewLocationSet(reader.Location),
|
||||||
Type: pkg.GoModulePkg,
|
PURL: packageURL(m.New.Path, m.New.Version),
|
||||||
|
Language: pkg.Go,
|
||||||
|
Type: pkg.GoModulePkg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,9 +55,10 @@ func parseGoMod(path string, reader io.Reader) ([]*pkg.Package, []artifact.Relat
|
|||||||
delete(packages, m.Mod.Path)
|
delete(packages, m.Mod.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pkgsSlice := make([]*pkg.Package, len(packages))
|
pkgsSlice := make([]pkg.Package, len(packages))
|
||||||
idx := 0
|
idx := 0
|
||||||
for _, p := range packages {
|
for _, p := range packages {
|
||||||
|
p.SetID()
|
||||||
pkgsSlice[idx] = p
|
pkgsSlice[idx] = p
|
||||||
idx++
|
idx++
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,63 +1,74 @@
|
|||||||
package golang
|
package golang
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/go-test/deep"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParseGoMod(t *testing.T) {
|
func TestParseGoMod(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
fixture string
|
fixture string
|
||||||
expected map[string]pkg.Package
|
expected []pkg.Package
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
fixture: "test-fixtures/one-package",
|
fixture: "test-fixtures/one-package",
|
||||||
expected: map[string]pkg.Package{
|
expected: []pkg.Package{
|
||||||
"github.com/bmatcuk/doublestar": {
|
{
|
||||||
Name: "github.com/bmatcuk/doublestar",
|
Name: "github.com/bmatcuk/doublestar",
|
||||||
Version: "v1.3.1",
|
Version: "v1.3.1",
|
||||||
Language: pkg.Go,
|
PURL: "pkg:golang/github.com/bmatcuk/doublestar@v1.3.1",
|
||||||
Type: pkg.GoModulePkg,
|
Locations: source.NewLocationSet(source.NewLocation("test-fixtures/one-package")),
|
||||||
|
Language: pkg.Go,
|
||||||
|
Type: pkg.GoModulePkg,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
||||||
fixture: "test-fixtures/many-packages",
|
fixture: "test-fixtures/many-packages",
|
||||||
expected: map[string]pkg.Package{
|
expected: []pkg.Package{
|
||||||
"github.com/anchore/go-testutils": {
|
{
|
||||||
Name: "github.com/anchore/go-testutils",
|
Name: "github.com/anchore/go-testutils",
|
||||||
Version: "v0.0.0-20200624184116-66aa578126db",
|
Version: "v0.0.0-20200624184116-66aa578126db",
|
||||||
Language: pkg.Go,
|
PURL: "pkg:golang/github.com/anchore/go-testutils@v0.0.0-20200624184116-66aa578126db",
|
||||||
Type: pkg.GoModulePkg,
|
Locations: source.NewLocationSet(source.NewLocation("test-fixtures/many-packages")),
|
||||||
|
Language: pkg.Go,
|
||||||
|
Type: pkg.GoModulePkg,
|
||||||
},
|
},
|
||||||
"github.com/anchore/go-version": {
|
{
|
||||||
Name: "github.com/anchore/go-version",
|
Name: "github.com/anchore/go-version",
|
||||||
Version: "v1.2.2-0.20200701162849-18adb9c92b9b",
|
Version: "v1.2.2-0.20200701162849-18adb9c92b9b",
|
||||||
Language: pkg.Go,
|
PURL: "pkg:golang/github.com/anchore/go-version@v1.2.2-0.20200701162849-18adb9c92b9b",
|
||||||
Type: pkg.GoModulePkg,
|
Locations: source.NewLocationSet(source.NewLocation("test-fixtures/many-packages")),
|
||||||
|
Language: pkg.Go,
|
||||||
|
Type: pkg.GoModulePkg,
|
||||||
},
|
},
|
||||||
"github.com/anchore/stereoscope": {
|
{
|
||||||
Name: "github.com/anchore/stereoscope",
|
Name: "github.com/anchore/stereoscope",
|
||||||
Version: "v0.0.0-20200706164556-7cf39d7f4639",
|
Version: "v0.0.0-20200706164556-7cf39d7f4639",
|
||||||
Language: pkg.Go,
|
PURL: "pkg:golang/github.com/anchore/stereoscope@v0.0.0-20200706164556-7cf39d7f4639",
|
||||||
Type: pkg.GoModulePkg,
|
Locations: source.NewLocationSet(source.NewLocation("test-fixtures/many-packages")),
|
||||||
|
Language: pkg.Go,
|
||||||
|
Type: pkg.GoModulePkg,
|
||||||
},
|
},
|
||||||
"github.com/bmatcuk/doublestar": {
|
{
|
||||||
Name: "github.com/bmatcuk/doublestar",
|
Name: "github.com/bmatcuk/doublestar",
|
||||||
Version: "v8.8.8",
|
Version: "v8.8.8",
|
||||||
Language: pkg.Go,
|
PURL: "pkg:golang/github.com/bmatcuk/doublestar@v8.8.8",
|
||||||
Type: pkg.GoModulePkg,
|
Locations: source.NewLocationSet(source.NewLocation("test-fixtures/many-packages")),
|
||||||
|
Language: pkg.Go,
|
||||||
|
Type: pkg.GoModulePkg,
|
||||||
},
|
},
|
||||||
"github.com/go-test/deep": {
|
{
|
||||||
Name: "github.com/go-test/deep",
|
Name: "github.com/go-test/deep",
|
||||||
Version: "v1.0.6",
|
Version: "v1.0.6",
|
||||||
Language: pkg.Go,
|
PURL: "pkg:golang/github.com/go-test/deep@v1.0.6",
|
||||||
Type: pkg.GoModulePkg,
|
Locations: source.NewLocationSet(source.NewLocation("test-fixtures/many-packages")),
|
||||||
|
Language: pkg.Go,
|
||||||
|
Type: pkg.GoModulePkg,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -65,43 +76,10 @@ func TestParseGoMod(t *testing.T) {
|
|||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.fixture, func(t *testing.T) {
|
t.Run(test.fixture, func(t *testing.T) {
|
||||||
f, err := os.Open(test.fixture)
|
pkgtest.NewCatalogTester().
|
||||||
if err != nil {
|
FromFile(t, test.fixture).
|
||||||
t.Fatalf(err.Error())
|
Expects(test.expected, nil).
|
||||||
}
|
TestParser(t, parseGoModFile)
|
||||||
|
|
||||||
// TODO: no relationships are under test yet
|
|
||||||
actual, _, err := parseGoMod(test.fixture, f)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(actual) != len(test.expected) {
|
|
||||||
t.Fatalf("unexpected length: %d", len(actual))
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, a := range actual {
|
|
||||||
e, ok := test.expected[a.Name]
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("extra package: %s", a.Name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
diffs := deep.Equal(a, &e)
|
|
||||||
if len(diffs) > 0 {
|
|
||||||
t.Errorf("diffs found for %q", a.Name)
|
|
||||||
for _, d := range diffs {
|
|
||||||
t.Errorf("diff: %+v", d)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.Failed() {
|
|
||||||
for _, a := range actual {
|
|
||||||
t.Logf("Found: %+v", a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
package pkg
|
package pkg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -42,13 +41,6 @@ func URL(p Package, release *linux.Release) string {
|
|||||||
switch {
|
switch {
|
||||||
case purlType == "":
|
case purlType == "":
|
||||||
purlType = packageurl.TypeGeneric
|
purlType = packageurl.TypeGeneric
|
||||||
case p.Type == GoModulePkg:
|
|
||||||
re := regexp.MustCompile(`(/)[^/]*$`)
|
|
||||||
fields := re.Split(p.Name, -1)
|
|
||||||
if len(fields) > 1 {
|
|
||||||
namespace = fields[0]
|
|
||||||
name = strings.TrimPrefix(p.Name, namespace+"/")
|
|
||||||
}
|
|
||||||
case p.Type == NpmPkg:
|
case p.Type == NpmPkg:
|
||||||
fields := strings.SplitN(p.Name, "/", 2)
|
fields := strings.SplitN(p.Name, "/", 2)
|
||||||
if len(fields) > 1 {
|
if len(fields) > 1 {
|
||||||
|
|||||||
@ -17,24 +17,6 @@ func TestPackageURL(t *testing.T) {
|
|||||||
distro *linux.Release
|
distro *linux.Release
|
||||||
expected string
|
expected string
|
||||||
}{
|
}{
|
||||||
{
|
|
||||||
name: "golang",
|
|
||||||
pkg: Package{
|
|
||||||
Name: "github.com/anchore/syft",
|
|
||||||
Version: "v0.1.0",
|
|
||||||
Type: GoModulePkg,
|
|
||||||
},
|
|
||||||
expected: "pkg:golang/github.com/anchore/syft@v0.1.0",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "golang short name",
|
|
||||||
pkg: Package{
|
|
||||||
Name: "go.opencensus.io",
|
|
||||||
Version: "v0.23.0",
|
|
||||||
Type: GoModulePkg,
|
|
||||||
},
|
|
||||||
expected: "pkg:golang/go.opencensus.io@v0.23.0",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "python",
|
name: "python",
|
||||||
pkg: Package{
|
pkg: Package{
|
||||||
@ -182,6 +164,7 @@ func TestPackageURL(t *testing.T) {
|
|||||||
expectedTypes.Remove(string(DartPubPkg))
|
expectedTypes.Remove(string(DartPubPkg))
|
||||||
expectedTypes.Remove(string(DotnetPkg))
|
expectedTypes.Remove(string(DotnetPkg))
|
||||||
expectedTypes.Remove(string(DebPkg))
|
expectedTypes.Remove(string(DebPkg))
|
||||||
|
expectedTypes.Remove(string(GoModulePkg))
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user