mirror of
https://github.com/anchore/syft
synced 2024-11-13 23:57:07 +00: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
12 changed files with 294 additions and 239 deletions
|
@ -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"
|
||||
|
||||
"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/generic"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
@ -34,9 +38,27 @@ var (
|
|||
|
||||
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 {
|
||||
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 version, ok := gbs["vcs.revision"]; 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)
|
||||
}
|
||||
main.Version = version
|
||||
main.PURL = packageURL(main.Name, main.Version)
|
||||
main.SetID()
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
// 1) reading build info 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 {
|
||||
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) {
|
||||
pkgs = append(pkgs, p)
|
||||
}
|
|
@ -123,18 +123,18 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||
goCompiledVersion = "1.18"
|
||||
archDetails = "amd64"
|
||||
)
|
||||
buildSettings := map[string]string{
|
||||
defaultBuildSettings := map[string]string{
|
||||
"GOARCH": "amd64",
|
||||
"GOOS": "darwin",
|
||||
"GOAMD64": "v1",
|
||||
}
|
||||
|
||||
expectedMain := pkg.Package{
|
||||
unmodifiedMain := pkg.Package{
|
||||
Name: "github.com/anchore/syft",
|
||||
FoundBy: catalogerName,
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Version: "(devel)",
|
||||
PURL: "pkg:golang/github.com/anchore/syft@(devel)",
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
|
@ -147,7 +147,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||
Metadata: pkg.GolangBinMetadata{
|
||||
GoCompiledVersion: goCompiledVersion,
|
||||
Architecture: archDetails,
|
||||
BuildSettings: buildSettings,
|
||||
BuildSettings: defaultBuildSettings,
|
||||
MainModule: "github.com/anchore/syft",
|
||||
},
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||
expected []pkg.Package
|
||||
}{
|
||||
{
|
||||
name: "buildGoPkgInfo parses a nil mod",
|
||||
name: "parse an empty mod",
|
||||
mod: nil,
|
||||
expected: []pkg.Package(nil),
|
||||
},
|
||||
|
@ -179,7 +179,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "github.com/adrg/xdg",
|
||||
FoundBy: catalogerName,
|
||||
PURL: "pkg:golang/github.com/adrg/xdg",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Locations: source.NewLocationSet(
|
||||
|
@ -201,7 +201,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||
expected: []pkg.Package(nil),
|
||||
},
|
||||
{
|
||||
name: "buildGoPkgInfo parses a mod without main module",
|
||||
name: "parse a mod without main module",
|
||||
arch: archDetails,
|
||||
mod: &debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
|
@ -221,8 +221,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "github.com/adrg/xdg",
|
||||
FoundBy: catalogerName,
|
||||
Version: "v0.2.1",
|
||||
PURL: "pkg:golang/github.com/adrg/xdg@v0.2.1",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
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,
|
||||
mod: &debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
|
@ -254,10 +254,55 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||
{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,
|
||||
mod: &debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
|
@ -283,8 +328,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "github.com/adrg/xdg",
|
||||
FoundBy: catalogerName,
|
||||
Version: "v0.2.1",
|
||||
PURL: "pkg:golang/github.com/adrg/xdg@v0.2.1",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Locations: source.NewLocationSet(
|
||||
|
@ -305,8 +350,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||
},
|
||||
{
|
||||
Name: "github.com/anchore/client-go",
|
||||
FoundBy: catalogerName,
|
||||
Version: "v0.0.0-20210222170800-9c70f9b80bcf",
|
||||
PURL: "pkg:golang/github.com/anchore/client-go@v0.0.0-20210222170800-9c70f9b80bcf",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Locations: source.NewLocationSet(
|
||||
|
@ -325,11 +370,11 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||
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,
|
||||
mod: &debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
|
@ -360,8 +405,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "golang.org/x/sys",
|
||||
FoundBy: catalogerName,
|
||||
Version: "v0.0.0-20211006194710-c8a6f5223071",
|
||||
PURL: "pkg:golang/golang.org/x/sys@v0.0.0-20211006194710-c8a6f5223071",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Locations: source.NewLocationSet(
|
||||
|
@ -381,8 +426,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||
}},
|
||||
{
|
||||
Name: "golang.org/x/term",
|
||||
FoundBy: catalogerName,
|
||||
Version: "v0.0.0-20210916214954-140adaaadfaf",
|
||||
PURL: "pkg:golang/golang.org/x/term@v0.0.0-20210916214954-140adaaadfaf",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Locations: source.NewLocationSet(
|
||||
|
@ -401,7 +446,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
|||
MainModule: "github.com/anchore/syft",
|
||||
},
|
||||
},
|
||||
expectedMain,
|
||||
unmodifiedMain,
|
||||
},
|
||||
},
|
||||
}
|
|
@ -9,38 +9,44 @@ import (
|
|||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"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.
|
||||
func parseGoMod(path string, reader io.Reader) ([]*pkg.Package, []artifact.Relationship, error) {
|
||||
packages := make(map[string]*pkg.Package)
|
||||
// parseGoModFile takes a go.mod and lists all packages discovered.
|
||||
func parseGoModFile(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
packages := make(map[string]pkg.Package)
|
||||
|
||||
contents, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
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 {
|
||||
return nil, nil, fmt.Errorf("failed to parse go module: %w", err)
|
||||
}
|
||||
|
||||
for _, m := range file.Require {
|
||||
packages[m.Mod.Path] = &pkg.Package{
|
||||
Name: m.Mod.Path,
|
||||
Version: m.Mod.Version,
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
packages[m.Mod.Path] = pkg.Package{
|
||||
Name: m.Mod.Path,
|
||||
Version: m.Mod.Version,
|
||||
Locations: source.NewLocationSet(reader.Location),
|
||||
PURL: packageURL(m.Mod.Path, m.Mod.Version),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
}
|
||||
}
|
||||
|
||||
// remove any old packages and replace with new ones...
|
||||
for _, m := range file.Replace {
|
||||
packages[m.New.Path] = &pkg.Package{
|
||||
Name: m.New.Path,
|
||||
Version: m.New.Version,
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
packages[m.New.Path] = pkg.Package{
|
||||
Name: m.New.Path,
|
||||
Version: m.New.Version,
|
||||
Locations: source.NewLocationSet(reader.Location),
|
||||
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)
|
||||
}
|
||||
|
||||
pkgsSlice := make([]*pkg.Package, len(packages))
|
||||
pkgsSlice := make([]pkg.Package, len(packages))
|
||||
idx := 0
|
||||
for _, p := range packages {
|
||||
p.SetID()
|
||||
pkgsSlice[idx] = p
|
||||
idx++
|
||||
}
|
||||
|
|
|
@ -1,63 +1,74 @@
|
|||
package golang
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/go-test/deep"
|
||||
|
||||
"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) {
|
||||
tests := []struct {
|
||||
fixture string
|
||||
expected map[string]pkg.Package
|
||||
expected []pkg.Package
|
||||
}{
|
||||
{
|
||||
fixture: "test-fixtures/one-package",
|
||||
expected: map[string]pkg.Package{
|
||||
"github.com/bmatcuk/doublestar": {
|
||||
Name: "github.com/bmatcuk/doublestar",
|
||||
Version: "v1.3.1",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "github.com/bmatcuk/doublestar",
|
||||
Version: "v1.3.1",
|
||||
PURL: "pkg:golang/github.com/bmatcuk/doublestar@v1.3.1",
|
||||
Locations: source.NewLocationSet(source.NewLocation("test-fixtures/one-package")),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
fixture: "test-fixtures/many-packages",
|
||||
expected: map[string]pkg.Package{
|
||||
"github.com/anchore/go-testutils": {
|
||||
Name: "github.com/anchore/go-testutils",
|
||||
Version: "v0.0.0-20200624184116-66aa578126db",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "github.com/anchore/go-testutils",
|
||||
Version: "v0.0.0-20200624184116-66aa578126db",
|
||||
PURL: "pkg:golang/github.com/anchore/go-testutils@v0.0.0-20200624184116-66aa578126db",
|
||||
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",
|
||||
Version: "v1.2.2-0.20200701162849-18adb9c92b9b",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
{
|
||||
Name: "github.com/anchore/go-version",
|
||||
Version: "v1.2.2-0.20200701162849-18adb9c92b9b",
|
||||
PURL: "pkg:golang/github.com/anchore/go-version@v1.2.2-0.20200701162849-18adb9c92b9b",
|
||||
Locations: source.NewLocationSet(source.NewLocation("test-fixtures/many-packages")),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
},
|
||||
"github.com/anchore/stereoscope": {
|
||||
Name: "github.com/anchore/stereoscope",
|
||||
Version: "v0.0.0-20200706164556-7cf39d7f4639",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
{
|
||||
Name: "github.com/anchore/stereoscope",
|
||||
Version: "v0.0.0-20200706164556-7cf39d7f4639",
|
||||
PURL: "pkg:golang/github.com/anchore/stereoscope@v0.0.0-20200706164556-7cf39d7f4639",
|
||||
Locations: source.NewLocationSet(source.NewLocation("test-fixtures/many-packages")),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
},
|
||||
"github.com/bmatcuk/doublestar": {
|
||||
Name: "github.com/bmatcuk/doublestar",
|
||||
Version: "v8.8.8",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
{
|
||||
Name: "github.com/bmatcuk/doublestar",
|
||||
Version: "v8.8.8",
|
||||
PURL: "pkg:golang/github.com/bmatcuk/doublestar@v8.8.8",
|
||||
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",
|
||||
Version: "v1.0.6",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
{
|
||||
Name: "github.com/go-test/deep",
|
||||
Version: "v1.0.6",
|
||||
PURL: "pkg:golang/github.com/go-test/deep@v1.0.6",
|
||||
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 {
|
||||
t.Run(test.fixture, func(t *testing.T) {
|
||||
f, err := os.Open(test.fixture)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
pkgtest.NewCatalogTester().
|
||||
FromFile(t, test.fixture).
|
||||
Expects(test.expected, nil).
|
||||
TestParser(t, parseGoModFile)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package pkg
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
|
@ -42,13 +41,6 @@ func URL(p Package, release *linux.Release) string {
|
|||
switch {
|
||||
case purlType == "":
|
||||
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:
|
||||
fields := strings.SplitN(p.Name, "/", 2)
|
||||
if len(fields) > 1 {
|
||||
|
|
|
@ -17,24 +17,6 @@ func TestPackageURL(t *testing.T) {
|
|||
distro *linux.Release
|
||||
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",
|
||||
pkg: Package{
|
||||
|
@ -182,6 +164,7 @@ func TestPackageURL(t *testing.T) {
|
|||
expectedTypes.Remove(string(DartPubPkg))
|
||||
expectedTypes.Remove(string(DotnetPkg))
|
||||
expectedTypes.Remove(string(DebPkg))
|
||||
expectedTypes.Remove(string(GoModulePkg))
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
|
|
Loading…
Reference in a new issue