-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(go): parse main mod version from build info settings #6564
feat(go): parse main mod version from build info settings #6564
Conversation
I used a test project to test the new parsing ability with test data. I wasn't sure how to generate the test data otherwise. Please let me know if there's something that I've missed about this. |
9bb94c4
to
812f898
Compare
* Parse the build info for -ldflags. If included, the flags will be further parsed, and searched for -X sub-flags. These sub-flags are commonly used to set the binary version during build time. A common pattern is to run `go build -ldflags='-X main.version=<semver>'` so that the main package's version variable is replaced with the latest tag. It's not guaranteed that this flag will propogate into the binary. See golang/go#63432 for more info. * Flag parsing reuses the pflags library that's used by the main Trivy binary. This keeps the implementation concise, and a bit more robust than creating a custom flag parser for -X flag commonly passed to -ldflags. * Add simple binary to test for validity of ldflags parsing. The flag * The documentation has been updated to reflect the improved accuracy of the Go binary parser.
812f898
to
98370b3
Compare
Fixed the unit test that changed after the merge conflict. 😢 I forgot to run |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your great contribution! Looks almost good. I left some comments.
We'd love to include it in v0.51.0, which will be released this week. Please let me know if you don't have time. We'll add small tweaks and wrap it up.
|
||
key = strings.ToLower(key) | ||
// The check for a 'ver' prefix enables the parser to pick up Trivy's own version value that's set. | ||
if strings.HasSuffix(key, "version") || strings.HasSuffix(key, "ver") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if validating semver?
if strings.HasSuffix(key, "version") || strings.HasSuffix(key, "ver") { | |
if (strings.HasSuffix(key, "version") || strings.HasSuffix(key, "ver")) && semver.IsValid(value) { |
// The flag can also be set multiple times, so a string slice is needed | ||
// to handle that edge case. | ||
var x []string | ||
fset.StringSliceVarP(&x, "", "X", nil, `Set the value of the string variable in importpath named name to value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need the original usage here.
var x []string | ||
fset.StringSliceVarP(&x, "", "X", nil, `Set the value of the string variable in importpath named name to value. | ||
This is only effective if the variable is declared in the source code either uninitialized | ||
or initialized to a constant string expression. -X will not work if the initializer makes | ||
a function call or refers to other variables. | ||
Note that before Go 1.5 this option took two separate arguments.`) | ||
if err := fset.Parse(flags); err != nil { | ||
p.logger.Error("Could not parse -ldflags found in build info", "err", err) | ||
return "" | ||
} | ||
|
||
for _, xx := range x { | ||
// It's valid to set the -X flags with quotes so we trim any that might | ||
// have been provided: Ex: | ||
// | ||
// -X main.version=1.0.0 | ||
// -X=main.version=1.0.0 | ||
// -X 'main.version=1.0.0' | ||
// -X='main.version=1.0.0' | ||
// -X="main.version=1.0.0" | ||
// -X "main.version=1.0.0" | ||
xx = strings.Trim(xx, `'"`) | ||
key, val, found := strings.Cut(xx, "=") | ||
if !found { | ||
p.logger.Debug("Unable to parse an -ldflags -X flag value", "got", xx) | ||
continue | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
StringToStringVarP
possibly simplifies parsing.
var x []string | |
fset.StringSliceVarP(&x, "", "X", nil, `Set the value of the string variable in importpath named name to value. | |
This is only effective if the variable is declared in the source code either uninitialized | |
or initialized to a constant string expression. -X will not work if the initializer makes | |
a function call or refers to other variables. | |
Note that before Go 1.5 this option took two separate arguments.`) | |
if err := fset.Parse(flags); err != nil { | |
p.logger.Error("Could not parse -ldflags found in build info", "err", err) | |
return "" | |
} | |
for _, xx := range x { | |
// It's valid to set the -X flags with quotes so we trim any that might | |
// have been provided: Ex: | |
// | |
// -X main.version=1.0.0 | |
// -X=main.version=1.0.0 | |
// -X 'main.version=1.0.0' | |
// -X='main.version=1.0.0' | |
// -X="main.version=1.0.0" | |
// -X "main.version=1.0.0" | |
xx = strings.Trim(xx, `'"`) | |
key, val, found := strings.Cut(xx, "=") | |
if !found { | |
p.logger.Debug("Unable to parse an -ldflags -X flag value", "got", xx) | |
continue | |
} | |
var x map[string]string | |
fset.StringToStringVarP(&x, "", "X", nil, "") | |
if err := fset.Parse(flags); err != nil { | |
p.logger.Error("Could not parse -ldflags found in build info", "err", err) | |
return "" | |
} | |
for key, value := range x { | |
key = strings.ToLower(key) | |
// The check for a 'ver' prefix enables the parser to pick up Trivy's own version value that's set. | |
if (strings.HasSuffix(key, "version") || strings.HasSuffix(key, "ver")) && semver.IsValid(value) { | |
return value | |
} | |
}``` |
} | ||
|
||
// parseLDFlags attempts to parse the binary's version from any `-ldflags` passed to `go build` at build time. | ||
func (p *Parser) parseLDFlags(name string, flags []string) string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The input is so varied that it is worth exporting this method and writing tests for it.
func (p *Parser) parseLDFlags(name string, flags []string) string { | |
func (p *Parser) ParseLDFlags(name string, flags []string) string { |
func TestParser_ParseLDFlags(t *testing.T) {
type args struct {
name string
flags []string
}
tests := []struct {
name string
args args
want string
}{
{
name: "normal",
args: args{
name: "github.com/aquasecurity/trivy",
flags: []string{
"-s",
"-w",
"-X=foo=bar",
"-X='github.com/aquasecurity/trivy/pkg/version.ver=0.50.1'",
},
},
want: "0.50.1",
},
{
name: "empty",
args: args{
name: "github.com/aquasecurity/test",
flags: []string{},
},
want: "",
},
... (more tests) ...
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := binary.NewParser().(*binary.Parser)
assert.Equal(t, tt.want, p.ParseLDFlags(tt.args.name, tt.args.flags))
})
}
}
|
||
// parseLDFlags attempts to parse the binary's version from any `-ldflags` passed to `go build` at build time. | ||
func (p *Parser) parseLDFlags(name string, flags []string) string { | ||
log.Debug("Parsing dependency's build info settings", "dependency", name, "-ldflags", flags) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
log.Debug("Parsing dependency's build info settings", "dependency", name, "-ldflags", flags) | |
p.logger.Debug("Parsing dependency's build info settings", "dependency", name, "-ldflags", flags) |
Reuse the parsing done by StringToStringVarP to parse the -ldflags. Additionally, a check for valid semver strings is added, and the check for a valid sub-flag name is extracted to increase readability. Test cases are also added for the exported ParseLDFlags function. Co-authored-by: Teppei Fukuda <[email protected]>
@knqyf263 Thank you for the review. Having use the pflag library in the past, I had overlooked the |
Great! I'll take another look today. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! I left very small comments.
Co-authored-by: Teppei Fukuda <[email protected]>
Thank you for catching those errors 😄 I've applied them all. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your contribution!
…ty#6564) Co-authored-by: Teppei Fukuda <[email protected]>
…b 0.0.1 Trivy recently started inferring the version of the binary as of 0.51.0, see aquasecurity/trivy#6564 The version used generated by go releaser is 0.0.1-next, and trivy detects that as version 0.0.1 of SpiceDB, and flags that as having CVEs, even though it's not really version 0.0.1.
…b 0.0.1 Trivy recently started inferring the version of the binary as of 0.51.0, see aquasecurity/trivy#6564 The version used generated by go releaser is 0.0.1-next, and trivy detects that as version 0.0.1 of SpiceDB, and flags that as having CVEs, even though it's not really version 0.0.1.
…b 0.0.1 Trivy recently started inferring the version of the binary as of 0.51.0, see aquasecurity/trivy#6564 The version used generated by go releaser is 0.0.1-next, and trivy detects that as version 0.0.1 of SpiceDB, and flags that as having CVEs, even though it's not really version 0.0.1.
…b 0.0.1 Trivy recently started inferring the version of the binary as of 0.51.0, see aquasecurity/trivy#6564 The version used generated by go releaser is 0.0.1-next, and trivy detects that as version 0.0.1 of SpiceDB, and flags that as having CVEs, even though it's not really version 0.0.1.
this fixes an issue with trivy where it flags SpiceDB as vulnerable, possibly as of aquasecurity/trivy#6564 include in version 0.51.0. It's flagged because it parses it as version 0.0.1-next
this fixes an issue with trivy where it flags SpiceDB as vulnerable, possibly as of aquasecurity/trivy#6564 include in version 0.51.0. It's flagged because it parses it as version 0.0.1-next
this fixes an issue with trivy where it flags SpiceDB as vulnerable, possibly as of aquasecurity/trivy#6564 include in version 0.51.0. It's flagged because it parses it as version 0.0.1-next
this fixes an issue with trivy where it flags SpiceDB as vulnerable, possibly as of aquasecurity/trivy#6564 include in version 0.51.0. It's flagged because it parses it as version 0.0.1-next
this fixes an issue with trivy where it flags SpiceDB as vulnerable, possibly as of aquasecurity/trivy#6564 include in version 0.51.0. It's flagged because it parses it as version 0.0.1-next as generated by goreleaser, because it does not have the tags available
this fixes an issue with trivy where it flags SpiceDB as vulnerable, possibly as of aquasecurity/trivy#6564 include in version 0.51.0. It's flagged because it parses it as version 0.0.1-next as generated by goreleaser, because it does not have the tags available
…ty#6564) Co-authored-by: Teppei Fukuda <[email protected]>
Description
Parse the build info for -ldflags. If included, the flags will be further parsed, and searched for -X sub-flags. These sub-flags are commonly used to set the binary version during build time. A common pattern is to run
go build -ldflags='-X main.version=<semver>'
so that the main package's version variable is replaced with the latest tag. It's not guaranteed that this flag will propogate into the binary. See cmd/go: using--trimpath
ingo build
removes-ldflags
fromgo version -m
output golang/go#63432 for more info.Flag parsing reuses the pflags library that's used by the main Trivy binary. This keeps the implementation concise, and a bit more robust than creating a custom flag parser for -X flag commonly passed to -ldflags.
Add simple binary to test for validity of ldflags parsing.
The documentation has been updated to reflect the improved accuracy of the Go binary parser.
Related issues
Checklist
Output in CycloneDX