diff --git a/syft/pkg/cataloger/golang/parse_go_binary.go b/syft/pkg/cataloger/golang/parse_go_binary.go index 6da185839e3..5286270e1ce 100644 --- a/syft/pkg/cataloger/golang/parse_go_binary.go +++ b/syft/pkg/cataloger/golang/parse_go_binary.go @@ -16,7 +16,6 @@ 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/file" "github.com/anchore/syft/syft/pkg" @@ -57,11 +56,11 @@ func (c *goBinaryCataloger) parseGoBinary(resolver file.Resolver, _ *generic.Env return nil, nil, err } - mods, archs := scanFile(unionReader, reader.RealPath) + mods := scanFile(unionReader, reader.RealPath) internal.CloseAndLogError(reader.ReadCloser, reader.RealPath) - for i, mod := range mods { - pkgs = append(pkgs, c.buildGoPkgInfo(resolver, reader.Location, mod, archs[i])...) + for _, mod := range mods { + pkgs = append(pkgs, c.buildGoPkgInfo(resolver, reader.Location, mod, mod.arch)...) } return pkgs, nil, nil } @@ -151,42 +150,6 @@ func extractVersionFromLDFlags(ldflags string) (majorVersion string, fullVersion return "", "" } -// 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 -func getArchs(readers []io.ReaderAt, builds []*extendedBuildInfo) []string { - if len(readers) != len(builds) { - log.Trace("golang cataloger: bin parsing: number of builds and readers doesn't match") - return nil - } - - if len(readers) == 0 || len(builds) == 0 { - log.Tracef("golang cataloger: bin parsing: %d readers and %d build info items", len(readers), len(builds)) - return nil - } - - archs := make([]string, len(builds)) - for i, build := range builds { - archs[i] = getGOARCH(build.Settings) - } - - // if architecture was found via build settings return - if archs[0] != "" { - return archs - } - - for i, r := range readers { - a, err := getGOARCHFromBin(r) - if err != nil { - log.Tracef("golang cataloger: bin parsing: getting arch from binary: %v", err) - continue - } - - archs[i] = a - } - return archs -} - func getGOARCH(settings []debug.BuildSetting) string { for _, s := range settings { if s.Key == GOARCH { diff --git a/syft/pkg/cataloger/golang/parse_go_binary_test.go b/syft/pkg/cataloger/golang/parse_go_binary_test.go index 9014445649c..6b19a5465b7 100644 --- a/syft/pkg/cataloger/golang/parse_go_binary_test.go +++ b/syft/pkg/cataloger/golang/parse_go_binary_test.go @@ -156,18 +156,12 @@ func TestBuildGoPkgInfo(t *testing.T) { tests := []struct { name string mod *extendedBuildInfo - arch string expected []pkg.Package }{ - { - name: "parse an empty mod", - mod: nil, - expected: []pkg.Package(nil), - }, { name: "package without name", mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ Deps: []*debug.Module{ { Path: "github.com/adrg/xdg", @@ -177,7 +171,9 @@ func TestBuildGoPkgInfo(t *testing.T) { Version: "v0.2.1", }, }, - }, nil, + }, + cryptoSettings: nil, + arch: "", }, expected: []pkg.Package{ { @@ -200,14 +196,13 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "buildGoPkgInfo parses a blank mod and returns no packages", - mod: &extendedBuildInfo{&debug.BuildInfo{}, nil}, + mod: &extendedBuildInfo{&debug.BuildInfo{}, nil, ""}, expected: []pkg.Package(nil), }, { name: "parse a mod without main module", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Settings: []debug.BuildSetting{ {Key: "GOARCH", Value: archDetails}, @@ -221,7 +216,9 @@ func TestBuildGoPkgInfo(t *testing.T) { Sum: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=", }, }, - }, nil, + }, + cryptoSettings: nil, + arch: archDetails, }, expected: []pkg.Package{ { @@ -249,9 +246,8 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "parse a mod with path but no main module", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Settings: []debug.BuildSetting{ {Key: "GOARCH", Value: archDetails}, @@ -259,7 +255,9 @@ func TestBuildGoPkgInfo(t *testing.T) { {Key: "GOAMD64", Value: "v1"}, }, Path: "github.com/a/b/c", - }, []string{"boringcrypto + fips"}, + }, + cryptoSettings: []string{"boringcrypto + fips"}, + arch: archDetails, }, expected: []pkg.Package{ { @@ -294,9 +292,8 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "parse a mod without packages", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ @@ -304,15 +301,16 @@ func TestBuildGoPkgInfo(t *testing.T) { {Key: "GOOS", Value: "darwin"}, {Key: "GOAMD64", Value: "v1"}, }, - }, nil, + }, + cryptoSettings: nil, + arch: archDetails, }, expected: []pkg.Package{unmodifiedMain}, }, { name: "parse main mod and replace devel pseudo version and ldflags exists (but contains no version)", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ @@ -323,7 +321,9 @@ func TestBuildGoPkgInfo(t *testing.T) { {Key: "vcs.time", Value: "2022-10-14T19:54:57Z"}, {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X blah=foobar`}, }, - }, nil, + }, + cryptoSettings: nil, + arch: archDetails, }, expected: []pkg.Package{ { @@ -359,9 +359,8 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "parse main mod and replace devel version with one from ldflags with vcs. build settings", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ @@ -372,7 +371,9 @@ func TestBuildGoPkgInfo(t *testing.T) { {Key: "vcs.time", Value: "2022-10-14T19:54:57Z"}, {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`}, }, - }, nil, + }, + cryptoSettings: nil, + arch: archDetails, }, expected: []pkg.Package{ { @@ -408,9 +409,8 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "parse main mod and replace devel version with one from ldflags without any vcs. build settings", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ @@ -419,7 +419,9 @@ func TestBuildGoPkgInfo(t *testing.T) { {Key: "GOAMD64", Value: "v1"}, {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`}, }, - }, nil, + }, + cryptoSettings: nil, + arch: archDetails, }, expected: []pkg.Package{ { @@ -453,9 +455,8 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "parse main mod and replace devel version with one from ldflags main.version without any vcs. build settings", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ @@ -464,7 +465,9 @@ func TestBuildGoPkgInfo(t *testing.T) { {Key: "GOAMD64", Value: "v1"}, {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X main.version=0.79.0`}, }, - }, nil, + }, + cryptoSettings: nil, + arch: archDetails, }, expected: []pkg.Package{ { @@ -498,9 +501,8 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "parse main mod and replace devel version with one from ldflags main.Version without any vcs. build settings", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ @@ -509,7 +511,9 @@ func TestBuildGoPkgInfo(t *testing.T) { {Key: "GOAMD64", Value: "v1"}, {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X main.Version=0.79.0`}, }, - }, nil, + }, + cryptoSettings: nil, + arch: archDetails, }, expected: []pkg.Package{ { @@ -543,9 +547,8 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "parse main mod and replace devel version with a pseudo version", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ @@ -555,7 +558,9 @@ func TestBuildGoPkgInfo(t *testing.T) { {Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"}, {Key: "vcs.time", Value: "2022-10-14T19:54:57Z"}, }, - }, nil, + }, + cryptoSettings: nil, + arch: archDetails, }, expected: []pkg.Package{ { @@ -590,9 +595,8 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "parse a populated mod string and returns packages but no source info", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ @@ -612,7 +616,9 @@ func TestBuildGoPkgInfo(t *testing.T) { Sum: "h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=", }, }, - }, nil, + }, + cryptoSettings: nil, + arch: archDetails, }, expected: []pkg.Package{ { @@ -664,9 +670,8 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "parse a populated mod string and returns packages when a replace directive exists", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ @@ -691,7 +696,9 @@ func TestBuildGoPkgInfo(t *testing.T) { }, }, }, - }, nil, + }, + cryptoSettings: nil, + arch: archDetails, }, expected: []pkg.Package{ { @@ -756,7 +763,7 @@ func TestBuildGoPkgInfo(t *testing.T) { ) c := goBinaryCataloger{} - pkgs := c.buildGoPkgInfo(fileresolver.Empty{}, location, test.mod, test.arch) + pkgs := c.buildGoPkgInfo(fileresolver.Empty{}, location, test.mod, test.mod.arch) assert.Equal(t, test.expected, pkgs) }) } diff --git a/syft/pkg/cataloger/golang/scan_binary.go b/syft/pkg/cataloger/golang/scan_binary.go index b19e242740f..8927299298f 100644 --- a/syft/pkg/cataloger/golang/scan_binary.go +++ b/syft/pkg/cataloger/golang/scan_binary.go @@ -15,16 +15,17 @@ import ( type extendedBuildInfo struct { *debug.BuildInfo cryptoSettings []string + arch string } // scanFile scans file to try to report the Go and module versions. -func scanFile(reader unionreader.UnionReader, filename string) ([]*extendedBuildInfo, []string) { +func scanFile(reader unionreader.UnionReader, filename string) []*extendedBuildInfo { // NOTE: multiple readers are returned to cover universal binaries, which are files // with more than one binary readers, err := unionreader.GetReaders(reader) if err != nil { log.WithFields("error", err).Warnf("failed to open a golang binary") - return nil, nil + return nil } var builds []*extendedBuildInfo @@ -34,6 +35,7 @@ func scanFile(reader unionreader.UnionReader, filename string) ([]*extendedBuild log.WithFields("file", filename, "error", err).Trace("unable to read golang buildinfo") continue } + // it's possible the reader just isn't a go binary, in which case just skip it if bi == nil { continue } @@ -41,15 +43,22 @@ func scanFile(reader unionreader.UnionReader, filename string) ([]*extendedBuild v, err := getCryptoInformation(r) if err != nil { log.WithFields("file", filename, "error", err).Trace("unable to read golang version info") - continue + // don't skip this build info. + // we can still catalog packages, even if we can't get the crypto information + } + arch := getGOARCH(bi.Settings) + if arch == "" { + arch, err = getGOARCHFromBin(r) + if err != nil { + log.WithFields("file", filename, "error", err).Trace("unable to read golang arch info") + // don't skip this build info. + // we can still catalog packages, even if we can't get the arch information + } } - builds = append(builds, &extendedBuildInfo{bi, v}) + builds = append(builds, &extendedBuildInfo{bi, v, arch}) } - - archs := getArchs(readers, builds) - - return builds, archs + return builds } func getCryptoInformation(reader io.ReaderAt) ([]string, error) {