Skip to content

Commit

Permalink
feat(go): parse main module of go binary files (#6530)
Browse files Browse the repository at this point in the history
Co-authored-by: Teppei Fukuda <[email protected]>
  • Loading branch information
DmitriyLewen and knqyf263 committed Apr 22, 2024
1 parent d4da83c commit e32215c
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 35 deletions.
10 changes: 10 additions & 0 deletions docs/docs/coverage/language/golang.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,17 @@ $ trivy rootfs ./your_binary
!!! note
It doesn't work with UPX-compressed binaries.

#### Empty versions
There are times when Go uses the `(devel)` version for modules/dependencies and Trivy can't resolve them:

- Only Go binaries installed using the `go install` command contain correct (semver) version for the main module.
In other cases, Go uses the `(devel)` version[^3].
- Dependencies replaced with local ones use the `(devel)` versions.

In these cases, the version of such packages is empty.

[^1]: It doesn't require the Internet access.
[^2]: Need to download modules to local cache beforehand
[^3]: See https://github.com/aquasecurity/trivy/issues/1837#issuecomment-1832523477

[dependency-graph]: ../../configuration/reporting.md#show-origins-of-vulnerable-dependencies
43 changes: 35 additions & 8 deletions pkg/dependency/parser/golang/binary/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package binary

import (
"debug/buildinfo"
"sort"
"strings"

"golang.org/x/xerrors"

"github.com/aquasecurity/trivy/pkg/dependency/types"
"github.com/aquasecurity/trivy/pkg/log"
xio "github.com/aquasecurity/trivy/pkg/x/io"
)

Expand All @@ -29,10 +31,14 @@ func convertError(err error) error {
return err
}

type Parser struct{}
type Parser struct {
logger *log.Logger
}

func NewParser() types.Parser {
return &Parser{}
return &Parser{
logger: log.WithPrefix("gobinary"),
}
}

// Parse scans file to try to report the Go and module versions.
Expand All @@ -42,11 +48,22 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
return nil, nil, convertError(err)
}

libs := make([]types.Library, 0, len(info.Deps)+1)
libs = append(libs, types.Library{
Name: "stdlib",
Version: strings.TrimPrefix(info.GoVersion, "go"),
})
libs := make([]types.Library, 0, len(info.Deps)+2)
libs = append(libs, []types.Library{
{
// Add the Go version used to build this binary.
Name: "stdlib",
Version: strings.TrimPrefix(info.GoVersion, "go"),
},
{
// 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.
// See https://github.com/aquasecurity/trivy/issues/1837#issuecomment-1832523477.
Version: p.checkVersion(info.Main.Path, info.Main.Version),
},
}...)

for _, dep := range info.Deps {
// binaries with old go version may incorrectly add module in Deps
Expand All @@ -63,9 +80,19 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,

libs = append(libs, types.Library{
Name: mod.Path,
Version: mod.Version,
Version: p.checkVersion(mod.Path, mod.Version),
})
}

sort.Sort(types.Libraries(libs))
return libs, nil, nil
}

// checkVersion detects `(devel)` versions, removes them and adds a debug message about it.
func (p *Parser) checkVersion(name, version string) string {
if version == "(devel)" {
p.logger.Debug("Unable to detect dependency version (`(devel)` is used). Version will be empty.", log.String("dependency", name))
return ""
}
return version
}
62 changes: 46 additions & 16 deletions pkg/dependency/parser/golang/binary/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ func TestParse(t *testing.T) {
name: "ELF",
inputFile: "testdata/test.elf",
want: []types.Library{
{
Name: "stdlib",
Version: "1.15.2",
},
{
Name: "github.com/aquasecurity/go-pep440-version",
Version: "v0.0.0-20210121094942-22b2f8951d46",
Expand All @@ -34,20 +30,24 @@ func TestParse(t *testing.T) {
Name: "github.com/aquasecurity/go-version",
Version: "v0.0.0-20210121072130-637058cfe492",
},
{
Name: "github.com/aquasecurity/test",
Version: "",
},
{
Name: "golang.org/x/xerrors",
Version: "v0.0.0-20200804184101-5ec99f83aff1",
},
{
Name: "stdlib",
Version: "1.15.2",
},
},
},
{
name: "PE",
inputFile: "testdata/test.exe",
want: []types.Library{
{
Name: "stdlib",
Version: "1.15.2",
},
{
Name: "github.com/aquasecurity/go-pep440-version",
Version: "v0.0.0-20210121094942-22b2f8951d46",
Expand All @@ -56,20 +56,24 @@ func TestParse(t *testing.T) {
Name: "github.com/aquasecurity/go-version",
Version: "v0.0.0-20210121072130-637058cfe492",
},
{
Name: "github.com/aquasecurity/test",
Version: "",
},
{
Name: "golang.org/x/xerrors",
Version: "v0.0.0-20200804184101-5ec99f83aff1",
},
{
Name: "stdlib",
Version: "1.15.2",
},
},
},
{
name: "Mach-O",
inputFile: "testdata/test.macho",
want: []types.Library{
{
Name: "stdlib",
Version: "1.15.2",
},
{
Name: "github.com/aquasecurity/go-pep440-version",
Version: "v0.0.0-20210121094942-22b2f8951d46",
Expand All @@ -78,28 +82,54 @@ func TestParse(t *testing.T) {
Name: "github.com/aquasecurity/go-version",
Version: "v0.0.0-20210121072130-637058cfe492",
},
{
Name: "github.com/aquasecurity/test",
Version: "",
},
{
Name: "golang.org/x/xerrors",
Version: "v0.0.0-20200804184101-5ec99f83aff1",
},
{
Name: "stdlib",
Version: "1.15.2",
},
},
},
{
name: "with replace directive",
inputFile: "testdata/replace.elf",
want: []types.Library{
{
Name: "stdlib",
Version: "1.16.4",
},
{
Name: "github.com/davecgh/go-spew",
Version: "v1.1.1",
},
{
Name: "github.com/ebati/trivy-mod-parse",
Version: "",
},
{
Name: "github.com/go-sql-driver/mysql",
Version: "v1.5.0",
},
{
Name: "stdlib",
Version: "1.16.4",
},
},
},
{
name: "with semver main module version",
inputFile: "testdata/semver-main-module-version.macho",
want: []types.Library{
{
Name: "go.etcd.io/bbolt",
Version: "v1.3.5",
},
{
Name: "stdlib",
Version: "1.20.6",
},
},
},
{
Expand Down
Binary file not shown.
12 changes: 8 additions & 4 deletions pkg/fanal/analyzer/language/golang/binary/binary_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@ func Test_gobinaryLibraryAnalyzer_Analyze(t *testing.T) {
Type: types.GoBinary,
FilePath: "testdata/executable_gobinary",
Libraries: types.Packages{
{
Name: "stdlib",
Version: "1.15.2",
},
{
Name: "github.com/aquasecurity/go-pep440-version",
Version: "v0.0.0-20210121094942-22b2f8951d46",
Expand All @@ -41,10 +37,18 @@ func Test_gobinaryLibraryAnalyzer_Analyze(t *testing.T) {
Name: "github.com/aquasecurity/go-version",
Version: "v0.0.0-20210121072130-637058cfe492",
},
{
Name: "github.com/aquasecurity/test",
Version: "",
},
{
Name: "golang.org/x/xerrors",
Version: "v0.0.0-20200804184101-5ec99f83aff1",
},
{
Name: "stdlib",
Version: "1.15.2",
},
},
},
},
Expand Down
2 changes: 1 addition & 1 deletion pkg/purl/purl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ func TestNewPackageURL(t *testing.T) {
typ: ftypes.GoModule,
pkg: ftypes.Package{
Name: "./private_repos/cnrm.googlesource.com/cnrm/",
Version: "(devel)",
Version: "",
},
want: nil,
},
Expand Down
4 changes: 2 additions & 2 deletions pkg/sbom/cyclonedx/marshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ func TestMarshaler_MarshalReport(t *testing.T) {
// dependency has been replaced with local directory
{
Name: "./api",
Version: "(devel)",
Version: "",
},
},
},
Expand Down Expand Up @@ -423,7 +423,7 @@ func TestMarshaler_MarshalReport(t *testing.T) {
BOMRef: "3ff14136-e09f-4df9-80ea-000000000013",
Type: cdx.ComponentTypeLibrary,
Name: "./api",
Version: "(devel)",
Version: "",
Properties: &[]cdx.Property{
{
Name: "aquasecurity:trivy:PkgType",
Expand Down
7 changes: 3 additions & 4 deletions pkg/sbom/spdx/marshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1056,7 +1056,7 @@ func TestMarshaler_Marshal(t *testing.T) {
Packages: []ftypes.Package{
{
Name: "./private_repos/cnrm.googlesource.com/cnrm/",
Version: "(devel)",
Version: "",
},
{
Name: "golang.org/x/crypto",
Expand Down Expand Up @@ -1105,10 +1105,9 @@ func TestMarshaler_Marshal(t *testing.T) {
},
},
{
PackageSPDXIdentifier: spdx.ElementID("Package-9a16e221e11f8a90"),
PackageSPDXIdentifier: spdx.ElementID("Package-b1c3b9e2363f5ff7"),
PackageDownloadLocation: "NONE",
PackageName: "./private_repos/cnrm.googlesource.com/cnrm/",
PackageVersion: "(devel)",
PackageLicenseConcluded: "NONE",
PackageLicenseDeclared: "NONE",
PrimaryPackagePurpose: tspdx.PackagePurposeLibrary,
Expand Down Expand Up @@ -1152,7 +1151,7 @@ func TestMarshaler_Marshal(t *testing.T) {
Relationships: []*spdx.Relationship{
{
RefA: spdx.DocElementID{ElementRefID: "Application-aab0f4e8cf174c67"},
RefB: spdx.DocElementID{ElementRefID: "Package-9a16e221e11f8a90"},
RefB: spdx.DocElementID{ElementRefID: "Package-b1c3b9e2363f5ff7"},
Relationship: "CONTAINS",
},
{
Expand Down

0 comments on commit e32215c

Please sign in to comment.