From 92e33fe124ab1b66edc22a67db16bac442fb1b58 Mon Sep 17 00:00:00 2001 From: DmitriyLewen Date: Wed, 22 May 2024 13:42:54 +0600 Subject: [PATCH] refactor(go): use priority for version from ldflags --- pkg/dependency/parser/golang/binary/parse.go | 62 ++++++++++++- .../parser/golang/binary/parse_test.go | 87 ++++++++++++++++++- 2 files changed, 144 insertions(+), 5 deletions(-) diff --git a/pkg/dependency/parser/golang/binary/parse.go b/pkg/dependency/parser/golang/binary/parse.go index 0354668c33cc..2ef2b4cf1c47 100644 --- a/pkg/dependency/parser/golang/binary/parse.go +++ b/pkg/dependency/parser/golang/binary/parse.go @@ -4,6 +4,7 @@ import ( "cmp" "debug/buildinfo" "runtime/debug" + "slices" "sort" "strings" @@ -142,6 +143,14 @@ func (p *Parser) ParseLDFlags(name string, flags []string) string { return "" } + // foundVersions contains discovered versions by type. + // foundVersions doesn't contain duplicates. Versions are filled into first corresponding category. + // Possible elements(categories): + // [0]: Versions using format `github.com///cmd/**/*.=x.x.x` + // [1]: Versions that use prefixes from `defaultPrefixes` + // [2]: Other versions + var foundVersions = make([][]string, 3) + 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: @@ -154,16 +163,46 @@ func (p *Parser) ParseLDFlags(name string, flags []string) string { // -X "main.version=1.0.0" key = strings.TrimLeft(key, `'`) val = strings.TrimRight(val, `'`) - if isValidXKey(key) && isValidSemVer(val) { - return val + if isVersionXKey(key) && isValidSemVer(val) { + switch { + case strings.HasPrefix(key, name+"/cmd/"): + foundVersions[0] = append(foundVersions[0], val) + case slices.Contains(defaultPrefixes, strings.ToLower(versionPrefix(key))): + foundVersions[1] = append(foundVersions[1], val) + default: + foundVersions[2] = append(foundVersions[2], val) + } } } - p.logger.Debug("Unable to detect dependency version used in `-ldflags` build info settings. Empty version used.", log.String("dependency", name)) + return p.chooseVersion(name, foundVersions) +} + +// chooseVersion chooses version from found versions +// Categories order: +// module name with `cmd` => versions with default prefixes => other versions +// See more in https://github.com/aquasecurity/trivy/issues/6702#issuecomment-2122271427 +func (p *Parser) chooseVersion(moduleName string, vers [][]string) string { + for _, versions := range vers { + // Versions for this category was not found + if len(versions) == 0 { + continue + } + + // More than 1 version for one category. + // Use empty version. + if len(versions) > 1 { + p.logger.Debug("Unable to detect dependency version. `-ldflags` build info settings contain more than one version. Empty version used.", log.String("dependency", moduleName)) + return "" + } + return versions[0] + } + + p.logger.Debug("Unable to detect dependency version. `-ldflags` build info settings don't contain version flag. Empty version used.", log.String("dependency", moduleName)) return "" } -func isValidXKey(key string) bool { +func isVersionXKey(key string) bool { key = strings.ToLower(key) // The check for a 'ver' prefix enables the parser to pick up Trivy's own version value that's set. return strings.HasSuffix(key, ".version") || strings.HasSuffix(key, ".ver") @@ -175,3 +214,18 @@ func isValidSemVer(ver string) bool { // parse a valid semver version. return semver.IsValid(ver) || semver.IsValid("v"+ver) } + +// versionPrefix returns version prefix from `-ldflags` flag key +// e.g. +// - `github.com/aquasecurity/trivy/pkg/version.version` => `version` +// - `github.com/google/go-containerregistry/cmd/crane/common.ver` => `common` +func versionPrefix(s string) string { + // Trim module part. + // e.g. `github.com/aquasecurity/trivy/pkg/Version.version` => `Version.version` + if lastIndex := strings.LastIndex(s, "/"); lastIndex > 0 { + s = s[lastIndex+1:] + } + + s, _, _ = strings.Cut(s, ".") + return strings.ToLower(s) +} diff --git a/pkg/dependency/parser/golang/binary/parse_test.go b/pkg/dependency/parser/golang/binary/parse_test.go index 47e5ec0db7c6..fd500df4aaf5 100644 --- a/pkg/dependency/parser/golang/binary/parse_test.go +++ b/pkg/dependency/parser/golang/binary/parse_test.go @@ -238,6 +238,91 @@ func TestParser_ParseLDFlags(t *testing.T) { }, want: "0.50.1", }, + { + name: "with `cmd` + `default prefix` flags", + args: args{ + name: "github.com/aquasecurity/trivy", + flags: []string{ + "-X='github.com/aquasecurity/trivy/cmd/Any.Ver=0.50.0'", + "-X='github.com/aquasecurity/trivy/pkg/version.Ver=0.50.1'", + }, + }, + want: "0.50.0", + }, + { + name: "with `cmd` flag", + args: args{ + name: "github.com/aquasecurity/trivy", + flags: []string{ + "-X='github.com/aquasecurity/trivy/cmd/Any.Ver=0.50.0'", + }, + }, + want: "0.50.0", + }, + { + name: "with `cmd` + `other` flags", + args: args{ + name: "github.com/aquasecurity/trivy", + flags: []string{ + "-X='github.com/aquasecurity/trivy/cmd/Any.Ver=0.50.0'", + "-X='github.com/aquasecurity/trivy/pkg/Any.Ver=0.50.1'", + }, + }, + want: "0.50.0", + }, + { + name: "with `default prefix` flag", + args: args{ + name: "github.com/aquasecurity/trivy", + flags: []string{ + "-X='github.com/aquasecurity/trivy/pkg/Common.Ver=0.50.1'", + }, + }, + want: "0.50.1", + }, + { + name: "with `default prefix` + `other` flags", + args: args{ + name: "github.com/aquasecurity/trivy", + flags: []string{ + "-X='github.com/aquasecurity/trivy/pkg/Any.Ver=0.50.0'", + "-X='github.com/aquasecurity/trivy/pkg/Common.Ver=0.50.1'", + }, + }, + want: "0.50.1", + }, + { + name: "with `other` flag", + args: args{ + name: "github.com/aquasecurity/trivy", + flags: []string{ + "-X='github.com/aquasecurity/trivy/pkg/Any.Ver=0.50.1'", + }, + }, + want: "0.50.1", + }, + { + name: "with 2 flags using default prefixes", + args: args{ + name: "github.com/aquasecurity/trivy", + flags: []string{ + "-X='github.com/aquasecurity/trivy/pkg/Common.Ver=0.50.0'", + "-X='github.com/aquasecurity/trivy/pkg/Main.Ver=0.50.1'", + }, + }, + want: "", + }, + { + name: "with two `other` flags", + args: args{ + name: "github.com/aquasecurity/trivy", + flags: []string{ + "-X='github.com/aquasecurity/trivy/pkg/Any.Ver=0.50.1'", + "-X='github.com/aquasecurity/trivy/pkg/Any-pref.Ver=0.50.0'", + }, + }, + want: "", + }, { name: "with version with extra prefix", args: args{ @@ -262,7 +347,7 @@ func TestParser_ParseLDFlags(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { p := binary.NewParser() - assert.Equal(t, tt.want, p.ParseLDFlags(tt.args.name, tt.args.flags)) + require.Equal(t, tt.want, p.ParseLDFlags(tt.args.name, tt.args.flags)) }) } }