mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-05 20:40:16 -08:00
feat(go): construct dependencies in the parser (#7973)
Signed-off-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
@@ -9,10 +9,12 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"github.com/spf13/pflag"
|
||||
"golang.org/x/mod/semver"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dependency"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
||||
@@ -64,27 +66,12 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc
|
||||
pkgs := make(ftypes.Packages, 0, len(info.Deps)+2)
|
||||
pkgs = append(pkgs, ftypes.Package{
|
||||
// Add the Go version used to build this binary.
|
||||
ID: dependency.ID(ftypes.GoBinary, "stdlib", stdlibVersion),
|
||||
Name: "stdlib",
|
||||
Version: stdlibVersion,
|
||||
Relationship: ftypes.RelationshipDirect, // Considered a direct dependency as the main module depends on the standard packages.
|
||||
})
|
||||
|
||||
// There are times when gobinaries don't contain Main information.
|
||||
// e.g. `Go` binaries (e.g. `go`, `gofmt`, etc.)
|
||||
if info.Main.Path != "" {
|
||||
pkgs = append(pkgs, ftypes.Package{
|
||||
// Add main module
|
||||
Name: info.Main.Path,
|
||||
// Only binaries installed with `go install` contain semver version of the main module.
|
||||
// Other binaries use the `(devel)` version, but still may contain a stamped version
|
||||
// set via `go build -ldflags='-X main.version=<semver>'`, so we fallback to this as.
|
||||
// as a secondary source.
|
||||
// See https://github.com/aquasecurity/trivy/issues/1837#issuecomment-1832523477.
|
||||
Version: cmp.Or(p.checkVersion(info.Main.Path, info.Main.Version), p.ParseLDFlags(info.Main.Path, ldflags)),
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
})
|
||||
}
|
||||
|
||||
for _, dep := range info.Deps {
|
||||
// binaries with old go version may incorrectly add module in Deps
|
||||
// In this case Path == "", Version == "Devel"
|
||||
@@ -98,14 +85,49 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc
|
||||
mod = dep.Replace
|
||||
}
|
||||
|
||||
version := p.checkVersion(mod.Path, mod.Version)
|
||||
pkgs = append(pkgs, ftypes.Package{
|
||||
Name: mod.Path,
|
||||
Version: p.checkVersion(mod.Path, mod.Version),
|
||||
ID: dependency.ID(ftypes.GoBinary, mod.Path, version),
|
||||
Name: mod.Path,
|
||||
Version: version,
|
||||
Relationship: ftypes.RelationshipUnknown,
|
||||
})
|
||||
}
|
||||
|
||||
// There are times when gobinaries don't contain Main information.
|
||||
// e.g. `Go` binaries (e.g. `go`, `gofmt`, etc.)
|
||||
var deps []ftypes.Dependency
|
||||
if info.Main.Path != "" {
|
||||
// Only binaries installed with `go install` contain semver version of the main module.
|
||||
// Other binaries use the `(devel)` version, but still may contain a stamped version
|
||||
// set via `go build -ldflags='-X main.version=<semver>'`, so we fallback to this as.
|
||||
// as a secondary source.
|
||||
// See https://github.com/aquasecurity/trivy/issues/1837#issuecomment-1832523477.
|
||||
version := cmp.Or(p.checkVersion(info.Main.Path, info.Main.Version), p.ParseLDFlags(info.Main.Path, ldflags))
|
||||
root := ftypes.Package{
|
||||
ID: dependency.ID(ftypes.GoBinary, info.Main.Path, version),
|
||||
Name: info.Main.Path,
|
||||
Version: version,
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
}
|
||||
|
||||
depIDs := lo.Map(pkgs, func(pkg ftypes.Package, _ int) string {
|
||||
return pkg.ID
|
||||
})
|
||||
sort.Strings(depIDs)
|
||||
|
||||
deps = []ftypes.Dependency{
|
||||
{
|
||||
ID: root.ID,
|
||||
DependsOn: depIDs, // Consider all packages as dependencies of the main module.
|
||||
},
|
||||
}
|
||||
// Add main module
|
||||
pkgs = append(pkgs, root)
|
||||
}
|
||||
|
||||
sort.Sort(pkgs)
|
||||
return pkgs, nil, nil
|
||||
return pkgs, deps, nil
|
||||
}
|
||||
|
||||
// checkVersion detects `(devel)` versions, removes them and adds a debug message about it.
|
||||
@@ -153,7 +175,12 @@ func (p *Parser) ParseLDFlags(name string, flags []string) string {
|
||||
// [1]: Versions that use prefixes from `defaultPrefixes`
|
||||
// [2]: Other versions
|
||||
var foundVersions = make([][]string, 3)
|
||||
defaultPrefixes := []string{"main", "common", "version", "cmd"}
|
||||
defaultPrefixes := []string{
|
||||
"main",
|
||||
"common",
|
||||
"version",
|
||||
"cmd",
|
||||
}
|
||||
for key, val := range x {
|
||||
// It's valid to set the -X flags with quotes so we trim any that might
|
||||
// have been provided: Ex:
|
||||
|
||||
@@ -14,111 +14,166 @@ import (
|
||||
func TestParse(t *testing.T) {
|
||||
wantPkgs := []ftypes.Package{
|
||||
{
|
||||
ID: "github.com/aquasecurity/test",
|
||||
Name: "github.com/aquasecurity/test",
|
||||
Version: "",
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
},
|
||||
{
|
||||
ID: "stdlib@v1.15.2",
|
||||
Name: "stdlib",
|
||||
Version: "v1.15.2",
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "github.com/aquasecurity/go-pep440-version@v0.0.0-20210121094942-22b2f8951d46",
|
||||
Name: "github.com/aquasecurity/go-pep440-version",
|
||||
Version: "v0.0.0-20210121094942-22b2f8951d46",
|
||||
},
|
||||
{
|
||||
ID: "github.com/aquasecurity/go-version@v0.0.0-20210121072130-637058cfe492",
|
||||
Name: "github.com/aquasecurity/go-version",
|
||||
Version: "v0.0.0-20210121072130-637058cfe492",
|
||||
},
|
||||
{
|
||||
ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
|
||||
Name: "golang.org/x/xerrors",
|
||||
Version: "v0.0.0-20200804184101-5ec99f83aff1",
|
||||
},
|
||||
}
|
||||
wantDeps := []ftypes.Dependency{
|
||||
{
|
||||
ID: "github.com/aquasecurity/test",
|
||||
DependsOn: []string{
|
||||
"github.com/aquasecurity/go-pep440-version@v0.0.0-20210121094942-22b2f8951d46",
|
||||
"github.com/aquasecurity/go-version@v0.0.0-20210121072130-637058cfe492",
|
||||
"golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
|
||||
"stdlib@v1.15.2",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
inputFile string
|
||||
want []ftypes.Package
|
||||
wantPkgs []ftypes.Package
|
||||
wantDeps []ftypes.Dependency
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "ELF",
|
||||
inputFile: "testdata/test.elf",
|
||||
want: wantPkgs,
|
||||
wantPkgs: wantPkgs,
|
||||
wantDeps: wantDeps,
|
||||
},
|
||||
{
|
||||
name: "PE",
|
||||
inputFile: "testdata/test.exe",
|
||||
want: wantPkgs,
|
||||
wantPkgs: wantPkgs,
|
||||
wantDeps: wantDeps,
|
||||
},
|
||||
{
|
||||
name: "Mach-O",
|
||||
inputFile: "testdata/test.macho",
|
||||
want: wantPkgs,
|
||||
wantPkgs: wantPkgs,
|
||||
wantDeps: wantDeps,
|
||||
},
|
||||
{
|
||||
name: "with replace directive",
|
||||
inputFile: "testdata/replace.elf",
|
||||
want: []ftypes.Package{
|
||||
wantPkgs: []ftypes.Package{
|
||||
{
|
||||
ID: "github.com/ebati/trivy-mod-parse",
|
||||
Name: "github.com/ebati/trivy-mod-parse",
|
||||
Version: "",
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
},
|
||||
{
|
||||
ID: "stdlib@v1.16.4",
|
||||
Name: "stdlib",
|
||||
Version: "v1.16.4",
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "github.com/davecgh/go-spew@v1.1.1",
|
||||
Name: "github.com/davecgh/go-spew",
|
||||
Version: "v1.1.1",
|
||||
},
|
||||
{
|
||||
ID: "github.com/go-sql-driver/mysql@v1.5.0",
|
||||
Name: "github.com/go-sql-driver/mysql",
|
||||
Version: "v1.5.0",
|
||||
},
|
||||
},
|
||||
wantDeps: []ftypes.Dependency{
|
||||
{
|
||||
ID: "github.com/ebati/trivy-mod-parse",
|
||||
DependsOn: []string{
|
||||
"github.com/davecgh/go-spew@v1.1.1",
|
||||
"github.com/go-sql-driver/mysql@v1.5.0",
|
||||
"stdlib@v1.16.4",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with semver main module version",
|
||||
inputFile: "testdata/semver-main-module-version.macho",
|
||||
want: []ftypes.Package{
|
||||
wantPkgs: []ftypes.Package{
|
||||
{
|
||||
ID: "go.etcd.io/bbolt@v1.3.5",
|
||||
Name: "go.etcd.io/bbolt",
|
||||
Version: "v1.3.5",
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
},
|
||||
{
|
||||
ID: "stdlib@v1.20.6",
|
||||
Name: "stdlib",
|
||||
Version: "v1.20.6",
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
},
|
||||
wantDeps: []ftypes.Dependency{
|
||||
{
|
||||
ID: "go.etcd.io/bbolt@v1.3.5",
|
||||
DependsOn: []string{
|
||||
"stdlib@v1.20.6",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with -ldflags=\"-X main.version=v1.0.0\"",
|
||||
inputFile: "testdata/main-version-via-ldflags.elf",
|
||||
want: []ftypes.Package{
|
||||
wantPkgs: []ftypes.Package{
|
||||
{
|
||||
ID: "github.com/aquasecurity/test@v1.0.0",
|
||||
Name: "github.com/aquasecurity/test",
|
||||
Version: "v1.0.0",
|
||||
Relationship: ftypes.RelationshipRoot,
|
||||
},
|
||||
{
|
||||
ID: "stdlib@v1.22.1",
|
||||
Name: "stdlib",
|
||||
Version: "v1.22.1",
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
},
|
||||
wantDeps: []ftypes.Dependency{
|
||||
{
|
||||
ID: "github.com/aquasecurity/test@v1.0.0",
|
||||
DependsOn: []string{
|
||||
"stdlib@v1.22.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "goexperiment",
|
||||
inputFile: "testdata/goexperiment",
|
||||
want: []ftypes.Package{
|
||||
wantPkgs: []ftypes.Package{
|
||||
{
|
||||
ID: "stdlib@v1.22.1",
|
||||
Name: "stdlib",
|
||||
Version: "v1.22.1",
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
@@ -137,15 +192,15 @@ func TestParse(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
defer f.Close()
|
||||
|
||||
got, _, err := binary.NewParser().Parse(f)
|
||||
gotPkgs, gotDeps, err := binary.NewParser().Parse(f)
|
||||
if tt.wantErr != "" {
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tt.wantErr)
|
||||
assert.ErrorContains(t, err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, got)
|
||||
assert.Equal(t, tt.wantPkgs, gotPkgs)
|
||||
assert.Equal(t, tt.wantDeps, gotDeps)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,24 +30,35 @@ func Test_gobinaryLibraryAnalyzer_Analyze(t *testing.T) {
|
||||
FilePath: "testdata/executable_gobinary",
|
||||
Packages: types.Packages{
|
||||
{
|
||||
ID: "github.com/aquasecurity/test",
|
||||
Name: "github.com/aquasecurity/test",
|
||||
Version: "",
|
||||
Relationship: types.RelationshipRoot,
|
||||
DependsOn: []string{
|
||||
"github.com/aquasecurity/go-pep440-version@v0.0.0-20210121094942-22b2f8951d46",
|
||||
"github.com/aquasecurity/go-version@v0.0.0-20210121072130-637058cfe492",
|
||||
"golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
|
||||
"stdlib@v1.15.2",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "stdlib@v1.15.2",
|
||||
Name: "stdlib",
|
||||
Version: "v1.15.2",
|
||||
Relationship: types.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "github.com/aquasecurity/go-pep440-version@v0.0.0-20210121094942-22b2f8951d46",
|
||||
Name: "github.com/aquasecurity/go-pep440-version",
|
||||
Version: "v0.0.0-20210121094942-22b2f8951d46",
|
||||
},
|
||||
{
|
||||
ID: "github.com/aquasecurity/go-version@v0.0.0-20210121072130-637058cfe492",
|
||||
Name: "github.com/aquasecurity/go-version",
|
||||
Version: "v0.0.0-20210121072130-637058cfe492",
|
||||
},
|
||||
{
|
||||
ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
|
||||
Name: "golang.org/x/xerrors",
|
||||
Version: "v0.0.0-20200804184101-5ec99f83aff1",
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user