mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 08:23:15 +01:00
fix: don't panic on universal go binaries (#2078)
If crypto settings or arch cannot be determined, still attempt to catalog packages from the build info, rather than panicking. Signed-off-by: Will Murphy <will.murphy@anchore.com>
This commit is contained in:
parent
2b7a9d0be3
commit
cfebae27f5
@ -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 {
|
||||
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user